Problem with collision of KinematicBodies

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By tam0705
:warning: Old Version Published before Godot 3 was released.

I have 2D topdown shooter game where there are zombies - and the player shoots them.

When I used “move_to” in the bullet script to check if it collided with a zombie when I shot them, it seemed that the bullet stopped in front of the zombie’s face and it meant the collision system worked.

Then I changed “move_to” to “set_pos” by adding bullet’s position little by little in an idle process to simulate real bullets’s movements, and added a code that would print something if the bullet was colliding with a zombie.

But it seemed that “set_pos” made the collision system fails. I didn’t get any printed text in the output.

Is there any ways to fix this? I also think to change the zombie and bullet to rigidbodies and use “set_linear_velocity” or others such as apply force, impulse, etc, instead of “set_pos”, but I really don’t understand how to use those codes to make the bullet shoots to the mouse pointer with static velocity.

Here’s my bullet’s code:

extends KinematicBody2D

var damage
var xmove
var ymove
onready var hand 
=get_tree().get_root().get_child(0).get_node("player").get_node("hand")

const SCREEN_WIDTH = 960
const SCREEN_HEIGHT = 640

signal hit(damage)

func _ready():
    set_pos(Vector2(hand.get_global_pos().x,hand.get_global_pos().y))

func shoot(attack,posx,posy):
    damage = attack
    look_at(Vector2(posx,posy))
    xmove = (get_pos().x - posx) * -1 / 100
    ymove = (get_pos().y - posy) * -1 / 100
    if abs(xmove) && abs(ymove) < 1.5:
	    xmove *= 3
	    ymove *= 3
    if abs(xmove) && abs(ymove) > 4:
	    xmove /= xmove / 4
	    ymove /= ymove / 4
    print (xmove,ymove)
    show()
    set_process(true)

func _process(delta):
    if is_colliding():
        print("HI")
        emit_signal("hit",damage)
        queue_free()
    set_pos(Vector2(get_pos().x+xmove,get_pos().y+ymove))
    if get_pos().x < (0-SCREEN_WIDTH)/2:
	    queue_free()
    elif get_pos().x > SCREEN_WIDTH/2:
	    queue_free()
    if get_pos().y < (0-SCREEN_HEIGHT)/2:
	    queue_free()
    elif get_pos().y > SCREEN_HEIGHT/2:
	    queue_free()

Thank you really much for helping, and sorry for my bad English.

:bust_in_silhouette: Reply From: kidscancode

You shouldn’t use set_pos() with physics bodies. When you do, you circumvent the physics and collision detection. The proper way to move a KinematicBody2D is with move() or move_to(). If you want a steady velocity, just move the same amount every frame. See below:

Also, you can greatly simplify your code by using a VisibilityNotifier2D node and calling queue_free() on its exit_screen signal instead of that chain of if statements.

Finally, you’re using way too many unnecessary x/y variables when things like get_pos()already return Vector2 objects which you’re changing them to anyway. Look how much simpler you can make this:

extends KinematicBody2D

var damage
var velocity  = Vector2(500, 0) # use this instead of xmove/ymove
onready var hand  = get_tree().get_root().get_child(0).get_node("player").get_node("hand")

# this should be queried in _ready() from the project settings
const SCREEN_WIDTH = 960 
const SCREEN_HEIGHT = 640

signal hit(damage)

func _ready():
    set_pos(hand.get_global_pos())

func shoot(attack, pos):
    damage = attack
    look_at(pos)
    velocity = velocity.rotated(get_pos.angle_to(pos))

    # I'm not clear what you're trying to do in this part
    # but none of it is necessary with a constant speed
    #if abs(xmove) && abs(ymove) < 1.5:  # what is this for?
    #xmove *= 3
    #ymove *= 3
    #if abs(xmove) && abs(ymove) > 4:  # again, I don't think this if is doing what you want
    #    xmove /= xmove / 4  # you are dividing twice here
    #    ymove /= ymove / 4  # this is equivalent to 
    #                          xmove = xmove / (xmove / 4)
    #                          which is xmove = xmove * 4
    show()
    set_process(true)

func _process(delta):
    if is_colliding():
        print("HI")
        emit_signal("hit",damage)
        queue_free()
    move(velocity * delta)  # you should use delta here

func _on_VisibilityNotifier2D_exit_screen():
    queue_free()

I recommend you look at some of the sample projects and docs about how to use Godot, as I think you are missing some of the fundamental concepts. Physics introduction — Godot Engine (stable) documentation in English is a good place to start.

Thank you for giving the simpler version! I am really confused out seeing the version of mine :smiley: I’m sorry for my inefficient way to code things, maybe not only the doc you linked to me that I need to read, but also docs how to code things efficiently, since I’m not even a programmer.

I really didn’t find out to use move() or move_to() instead of set_pos(), how foolish I am. Sorry for my carelessness :v

I’m not clear what you’re trying to do in this part

but none of it is necessary with a constant speed

I’m doing it not for a constant speed, but it is because the mouse pointer’s pos is sometimes further and sometimes nearer from the player. The further the mouse pointer’s pos is, the bigger xmove, ymove and bullet’s velocity will (in my version of code). That’s why I checked first if a bullet is too slow or too fast, caused by how far the mouse pointer is from the player.

Frankly speaking I also want to ask how to fix the velocity, but when I posted that some days ago, it seemed the forum was bugged and my question didn’t appear. Before asking the same question again, I was faced by another problem, which is the problem I was talking about in this page.

tam0705 | 2017-06-11 13:36

In the case of kinematic bodies you can use set_pos but only if you want them to act as moving static bodies, ignoring overlaps and pushing everything that is “aware” of overlaps/collisions (like on the rest of the common engines).

eons | 2017-06-11 15:57