0 votes

Hi All,

My second question. In my 3d rpg game, I am creating a prototype where you have two knights and a bunch of randomly placed hounds in a room.

I have created an Area node in the main scene that will be the area where the randomly created hounds will appear.

In a script attached to the main scene, loop 6 times and create hound instances off of my hound scene and add then as CHILDs to the Area node. I do this because it is, at least in my opinion, cleaner to generate random x and z positions for grounded enemies by simply using the range -1,1 for the x and z coordinates. These are taken as local coordinates within the area so works nicely. Each hounds spawn function is nicely contained within an enemy.gd script attached to each Hound scene.

Because the hounds are children of this area that I created and this is scalled, I reset all of the hounds scale by mutliplying it with 1/Area.scale.x, 1/Area.scale.y and 1/Area.scale.z (found this useful trick out in another Godot forum)

In order to detect whether a hound has landed on another hound while being randomly placed, I have each hound have an Area node of its own that is roughly the same size as its collision shape. In the physicsprocess(delta)() function of the hound, I have this check to see if it is spawned on another area:

extends KinematicBody
var spawned = false
signal spawn_collision

func _physics_process(delta):
var bodies = $Area.get_overlapping_areas()
if(bodies.size()):# and !spawned):
    if(!spawned):
        spawn()
    else:
        spawned = true

func spawn():
randomize()
var x_pos = rand_range(-1,1)
var z_pos = rand_range(-1,1)
set_translation(Vector3(x_pos,0,z_pos))

This means, if the hounds did land on each other, for a split second when the level loads up, I see a hound or two quickly change positions before settling down.

The main level scene script takes care of spawning all hounds and when all hounds have their spawned flag set to true, it will stop spawning and move on to other processing and not call the spawning process again.

It does appear to be quite a round about way of accomplishing something trivial. Is there a more effecient way of detecting collisions straight away of a spawned object? (without calculating the bounds of each spawned enemy already and comparing it with this list)

in Engine by (29 points)

1 Answer

0 votes
Best answer

After spending some time on this, I have a better way than what I explained in my question.

  1. I create a new scene called EnemySpawner.
  2. The Scene simply contains a MeshInstance Node as its root node.
  3. I rename this root node to EnemySpawner.
  4. I attach a script to this root node called "EnemySpawner.gd.
  5. I choose a BoxShape for the Meshinstance.
  6. I can change the size and position of this if I want - practically this will be done more when I place this in my main level scene.
  7. In the EnemySpawner.gd script ready function, I calculate the minimum and maximum x and z positions (dealing only with grounded units for the time being so y is 0).
    This is done using the code:

    var minx = 0
    var max
    x = 0
    var miny = 0
    var max
    y = 0
    var minz = 0
    var max
    z = 0

    func ready():
    # Called when the node is added to the scene for the first time.
    # Initialization here
    min
    x = translation.x - ((mesh.size.x * scale.x)/2)
    maxx = translation.x + ((mesh.size.x * scale.x)/2)
    min
    z = translation.z - ((mesh.size.z * scale.z)/2)
    max_z = translation.z + ((mesh.size.z * scale.z)/2)
    pass

  8. I have a function called spawn() in the EnemySpawner.gd script that takes the enemy object to spawn as an argument and sets its translation (currently I am not using random atm, but that can easily be added soon):

func spawn(enemy):
var enemyxoffset = enemy.getnode("EnemyCollider").shape.getextents().x/2
var enemyzoffset = enemy.getnode("EnemyCollider").shape.getextents().z/2

enemy.translation = Vector3(translation.x,0,max_z - enemy_z_offset)

The important thing to note is that I also have to subtract or add the enemy collider offset to the position I am inserting the object in otherwise, when placed on an edge, the enemy will be sticking out of the spawn area.

Finally, in my main level script, I can create random enemies using the spawn area. I do not add them as childs to the EnemySpawner but instead add them as child nodes to the main level to avoid child node transform headaches:

func ready():
var hound
scene = load("res://Hound.tscn")
var houndnode = houndscene.instance()
houndnode.setname("Hound1")
$EnemySpawner.spawn(houndnode)
add
child(hound_node)

Though I mentioned in my question I didnt want to calculate the points to prevent enemies spawning on each other - I have come to the conclusion (at least for now) that it is better to do so. This way it is easier as the logic will be encapsulated within the EnemySpawner scene. I can have an Array of bounds that have already been spawned so new enemies cannot be spawned within those bounds.

I also liked this approach because when I drag the EnemySpawner to my level, I can adjust its size and position to what I want it to be and the spawned enemies will continue to spawn correctly.

by (29 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.