0 votes

I add and spawn a enemy with this, 10 away from the player:

var Enemy = load("res://Enemy/Enemy.tscn").instance()
add_child(Enemy)
Enemy.global_transform.origin.x = Player.global_transform.origin.x + 10
Enemy.global_transform.origin.z = Player.global_transform.origin.z + 10

But I don't want to spawn the Enemy in another object (they all have CollisionShapes). Is there a way to check if there is a collision at a specific point in the "world"?
So what I would like to do:

var Enemy = load("res://Enemy/Enemy.tscn").instance()
x = Player.global_transform.origin.x
z = Player.global_transform.origin.z
while is_collision(x, 0, z):
    x += 1
    z += 1
add_child(Enemy)
Enemy.global_transform.origin.x = x
Enemy.global_transform.origin.z = z

My whole game world is flat, this is the reason why I don't have to care about the y-coordinate. Maybe I have to change the y-coordinate from 0 to 1 or 2, if 0 is in the ground.
[is_collision(x-coordinate, y-coordinate, z-coordinate) is just a fictional function that replaces the function I search.]

in Engine by (130 points)

4 Answers

0 votes
Best answer

Thanks for your help, I found a simple "workaround":

Enemy.move_and_slide(Vector3(0,0,0))
while Enemy.get_slide_count() > 0:
    Enemy.global_transform.origin.x += 1
    Enemy.global_transform.origin.z += 1
    Enemy.move_and_slide(Vector3(0,0,0))
by (130 points)
selected by
+1 vote

I found a quick solution, if it suits you.

You can create the enemy, give the player's position, test if it collides. If it collides you move him away a little until it dosn't collide.

There is a function in kinematicbody, test_move(), that can test a mouvement.
https://docs.godotengine.org/en/stable/classes/class_kinematicbody2d.html#methods

Here the code I used

var Enemy = load("res://Enemy/Enemy.tscn").instance()
# Add the Enemy before test anything
add_child(Enemy)
Enemy.translation = Players.translation
while Enemy.test_move(transform, Vector3(0,0,0) == true:
    Enemy.translation.x += 1
    Enemy.translation.z += 1
by (288 points)
edited by

Thanks, I tried this, but the while loop doesn't seem to work. (it never ends)

It's weird because our code do litteraly the same thing.
Anyway

–1 vote

Hi,
would be easier to keep track of your game objects.

Put your enemy in a group like "enemies"

For performance reasons i would use a simple radius for keeping the distance. (used a myRadius variable)

The when spawning test for other enemies:

func spawn():
   var Enemy = load("res://Enemy/Enemy.tscn").instance()
   var spawnPosition = Player.global_transform.origin
   while isTooNearToOtherEnemy( spawnPosition  ):
      spawnPosition.x += rand_range(-1,1)
      spawnPosition.z += rand_range(-1,1)
   add_child(Enemy)
   Enemy.global_transform.origin = spawnPosition

func isTooNearToOtherEnemy( spawnPosition:Vector3 ):
   var enemies = get_tree().get_nodes_in_group ( "enemies" )
   for enemy in enemies:
      if spawnPosition.distance_to(enemy.global_tranform.origin) < enemy.myRadius:
         return true
   return false
by (2,470 points)
edited by

Thanks, but I only have one enemy.

But with this he is still spawning in other object, isn't he?

–1 vote

Many ways of doing it. One I can think of quickly is that shapes have collision methods that you can work with if you get the shapes involved.

https://docs.godotengine.org/en/3.2/classes/class_shape2d.html#class-shape2d

An example would look like this:

extends Node2D

var actors = []

func make_actor_at(p_position):

    var s = Sprite.new()
    var k = KinematicBody2D.new()
    var r = RectangleShape2D.new()

    var tex = preload("res://icon.png")
    r.extents = tex.get_size() / 2.0
    s.texture = tex

    k.add_child(s)
    k.create_shape_owner(k)
    k.shape_owner_add_shape(0, r)

    var a1_xform = Transform2D(0.0, p_position) * k.shape_owner_get_transform(0)


    var collisions = 0

    for actor in actors:

        var shape = actor.shape_owner_get_shape(0, 0)
        var s_xform = actor.shape_owner_get_transform(0)

        print(s_xform)

        var a2_xform = actor.global_transform * s_xform

        print(shape)

        if(r.collide(a1_xform, shape, a2_xform)):
            print("Collides with: ", actor)
            collisions += 1

    if(collisions == 0):

        actors.append(k)

        add_child(k)
        k.global_position = p_position

    else:
        k.free()


func _input(e):

    if(e is InputEventMouseButton and e.pressed and e.button_index == BUTTON_LEFT):
        make_actor_at(get_global_mouse_position())

Another alternative is to get the direct space state, and construct queries. This can be accessed on any Node2D.

get_world_2d().direct_space_state

The methods and their requirements can be found here:

https://docs.godotengine.org/en/3.2/classes/class_physics2ddirectspacestate.html?highlight=cast_motion

And a more advanced option is to work directly with the Physics2DServer. Creating the bodies and shapes, and handling things using their RIDs. (Though it's more for situations when you want performance.)

https://docs.godotengine.org/en/stable/classes/class_physics2dserver.html

There are also other creative things you could do if you bring the actor in invisibly, and use other typical techniques to check collisions, resolve them, and then show the actor.

by (5,192 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 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 webmaster@godotengine.org with your username.