0 votes

I cannot for the life of me figure out how to get enemies to chase the player. I've looked at multiple tutorials but nothing is working.
Here is the script for the enemy (its really messy because I've been messing around trying to figure this out for a while now):

extends KinematicBody

export var speed = 3

onready var raycast = $RayCast
onready var anim_player = $AnimationPlayer
onready var collisionshape = $Area2/CollisionShape
onready var area = $Area/CollisionPolygon

var player = null
var dead = false
var target
var space_state

func _ready():
    anim_player.play("walk")
    add_to_group("Zombies")
    space_state = get_world().direct_space_state

func _physics_process(delta):
    if dead:
        return
    if player == null:
        return

    if target:
        var result = space_state.intersect_ray(global_transform.origin, target.global_transform.origin)
        if result.collider.is_in_group("Player"):
            look_at(target.global_transform.origin, Vector3.UP)
            move_to_target(delta)

    var vec_to_player = player.translation - translation
    vec_to_player = vec_to_player.normalized()
    raycast.cast_to = vec_to_player * 1.5

    move_and_collide(vec_to_player * speed * delta)

    if raycast.is_colliding():
        var coll = raycast.get_collider()
        if coll != null and coll.name == "Player":
            coll.kill()


func kill():
    dead = true
    $CollisionShape.disabled = true
    collisionshape.disabled = true
    anim_player.play("die")
    area.disabled = true

func set_player(p):
    player = p


func _on_Area_body_entered(body):
    if body.is_in_group("Player"):
        target = body
        print(body.name + " entered")


func _on_Area_body_exited(body):
    if body.is_in_group("Player"):
        target = null
        print(body.name + " exited")

func move_to_target(delta):
    var direction = (target.transform.origin - transform.origin).normalized()
    move_and_slide(direction * speed * delta, Vector3.UP)


func _on_Area2_body_entered(body):
    if body.is_in_group("Player"):
        print(body.name + " was killed")
        target = body
        get_tree().reload_current_scene()

I'm really not sure what the problem is so I'll include the Player script here too for reference:

extends KinematicBody

export var speed = 10
export var acceleration = 5
export var gravity = 1.38
export var jump_power = 34
export var mouse_sensitivity = 0.1

onready var head = $Head
onready var camera = $Head/Camera
onready var anim_player = $Head/Camera/AnimationPlayer
onready var raycast = $Head/Camera/RayCast

var velocity = Vector3()
var camera_x_rotation = 0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
    yield(get_tree(), "idle_frame")
    get_tree().call_group("zombies", "set_player", self)
    add_to_group("Player")

func _input(event):
    if event is InputEventMouseMotion:
        head.rotate_y(deg2rad(-event.relative.x * mouse_sensitivity))

        var x_delta = event.relative.y * mouse_sensitivity
        if camera_x_rotation + x_delta > -90 and camera_x_rotation + x_delta < 90: 
            camera.rotate_x(deg2rad(-x_delta))
            camera_x_rotation += x_delta

func _process(delta):
    if Input.is_action_just_pressed("ui_cancel"):
        Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

func _physics_process(delta):
    var head_basis = head.get_global_transform().basis
    if Input.is_action_pressed("exit"):
        get_tree().quit()
    if Input.is_action_pressed("restart"):
        kill()

    var direction = Vector3()
    if Input.is_action_pressed("move_forward"):
        direction -= head_basis.z
    elif Input.is_action_pressed("move_back"):
        direction += head_basis.z

    if Input.is_action_pressed("strafe_left"):
        direction -= head_basis.x
    elif Input.is_action_pressed("strafe_right"):
        direction += head_basis.x

    if Input.is_action_pressed("primary fire") and !anim_player.is_playing():
        anim_player.play("shoot")
        var coll = raycast.get_collider()
        if raycast.is_colliding() and coll.has_method("kill"):
            coll.kill()

    direction = direction.normalized()

    velocity = velocity.linear_interpolate(direction * speed, acceleration * delta)
    velocity.y -= gravity

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y += jump_power

    velocity = move_and_slide(velocity, Vector3.UP)

func kill():
    get_tree().reload_current_scene()

Can anyone help me out here? I've been struggling with this for weeks now. I would greatly appreciate help.

asked May 10 in Engine by thebraveknight999 (12 points)

The problem here is that enemies move away from the target instead of toward it.

3 Answers

0 votes

I'm pretty new to 3d programming myself, so I could be wrong about this, but I'm pretty sure that the problem is that you are mixing local and global coordinates in your movement logic. As far as I know, move_and_collide() and move_and_slide() expect the velocity vector to be in world space, but the transform.origin and the .translation properties that you are using to get your directional vectors for those functions return the node's local transform.

If you want to test to see if this is the issue, try adding a debug print statement to those calculations, something like:

var vec_to_player = player.translation - translation
vec_to_player = vec_to_player.normalized()
print("vec_to_player directional vector is: ", vec_to_player)
var g_vec_to_player = player.get_global_transform().origin - get_global_transform().origin
g_vec_to_player = g_vec_to_player.normalized()
print("g_vec_to_player directional vector is: ", g_vec_to_player)
raycast.cast_to = vec_to_player * 1.5

move_and_collide(vec_to_player * speed * delta)

Comparing the two vectors that print to the debug output should tell you if that is the issue or not.

This is how I usually debug vector calculations, but there is probably a better way, so if anyone else knows a better method, feel free to correct me.

Other than that, I don't see anything wrong with the logic or your math either, so I hope this helps you solve the problem.

Cheers.

answered May 11 by Svalokai (20 points)
–1 vote

IF distance to player is LESS than react_distance, calculate vector from enemy to player and, add that to ' enemy location ', every frame . . .

You can use look_at to make the enemy face the player, and then get the rotation, after that has been calculated, to get the vector <3

answered May 11 by jasperbrooks79 (97 points)
0 votes

Imo, first thing you need to do, is add the look_at function / method, then try printing rotation, after that, you can use degrees to radians ( deg2rad in visual script ) and, then use sin and, cos ( cosine ), to calculate the vector, towards the player, if you have that, just make the enemy move ' that ' direction, every frame . . . Made it work, a while ago, coding a look_at function from scratch can be done, but it's difficult, because the engine goes from -180, to 180 degrees, I think, so you have to write some nasty code, to take that into account . . . I used look_at, that was enough, it's implemented code, but you can also code everything, yourself . . . Good luck, it took me a few days, to figure it out, really nasty problem, used look_at in the end, got lost, it worked, somehow . . . :O :O <3

Almost had it, but in the end, it was easier to used a pre-made function, that worked, but bit noob, still, stuff works . . . . Good luck, you'll figure it out, easier if you debug, by printing variables, like rotation, so you can see what happens, as well . . .

answered May 11 by jasperbrooks79 (97 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.