0 votes

Hi, I've been working on a FPS game and the enemy Ai stop working the second I'm in a different height or get too close. Here's the code. I should add that the problem only occur with a long range enemy, melee enemies are unaffected. My guess is it has to do with sight angle and los.

 extends Character
onready var character_mover = $CharacterMover
onready var anim_player = $Graphics/AnimationPlayer
onready var health_mananger = $HealthManager
onready var aimer = $AimAtObject
var start_pos : Vector3
var start_facing_dir : Vector3
var nav 

enum STATES {IDLE, CHASE, ATTACK, DEAD}
var cur_state = STATES.IDLE

var player = null
var cur_target : Character
var path = []

export var sight_angle = 45.0
export var turn_speed = 360.0
#export var jump_timer = 2.0
export var attack_angle = 5.0
export var attack_range = 2.0
export var attack_rate = 0.5
export var attack_anim_speed_mod = 0.5
var attack_timer : Timer
var can_attack = true

signal attack
signal chase
signal death
var alive = true

func _ready():
    var navigation = get_tree().get_nodes_in_group("Navigation")[0]
    player = get_tree().get_nodes_in_group("player")[0]
    nav = navigation
    attack_timer = Timer.new()
    attack_timer.wait_time = attack_rate
    attack_timer.connect("timeout", self, "finish_attack")
    attack_timer.one_shot = true
    add_child(attack_timer)
    start_pos = global_transform.origin
    start_facing_dir = -global_transform.basis.z
    var bone_attachments = $Graphics/Armature/Skeleton.get_children()
    for bone_attachment in bone_attachments:
        for child in bone_attachment.get_children():
            if child is HitBox:
                child.connect("hurt", self, "hurt")

    health_mananger.connect("dead", self, "set_state_dead")
    health_mananger.connect("gibbed", $Graphics, "hide")
    character_mover.init(self)
    set_state_idle()

func _process(delta):
    match cur_state:
        STATES.IDLE:
            process_state_idle(delta)
        STATES.CHASE:
            process_state_chase(delta)
        STATES.ATTACK:
            process_state_attack(delta)
        STATES.DEAD:
            process_state_dead(delta)


func set_state_idle():
    cur_state = STATES.IDLE
    anim_player.play("idle_loop")

func set_state_chase():
    cur_state = STATES.CHASE
    anim_player.play("walk_loop", 0.2)
    emit_signal("chase")

func set_state_attack():
    cur_state = STATES.ATTACK

func set_state_dead():
    cur_state = STATES.DEAD
    anim_player.play("death")


func process_state_idle(delta):
    if can_see_player():
        set_state_chase()

func process_state_chase(delta):
    if within_dis_of_player(attack_range) or has_los_player():
        set_state_attack()
    var player_pos = player.global_transform.origin
    var our_pos = global_transform.origin
    path = nav.get_simple_path(our_pos, player_pos)
    var goal_pos = player_pos
    if path.size() > 1:
        goal_pos = path[1]

    var dir = goal_pos - our_pos
    dir.y = 0
    character_mover.set_move_vec(dir)
    face_dir(dir, delta)

func process_state_attack(delta):
    character_mover.set_move_vec(Vector3.ZERO)
    if can_attack:
        if !within_dis_of_player(attack_range) or !can_see_player():
            set_state_chase()
        elif !player_within_angle(attack_angle):
                face_dir(global_transform.origin.direction_to(player.global_transform.origin), delta)

        else:
            start_attack(delta)



func process_state_dead(delta):
    if character_mover.body_to_move.is_on_floor():
        $CollisionShape.disabled = true
        alive = false
        emit_death_signal()
        character_mover.freeze()

func hurt(damage: int, dir: Vector3):
    if cur_state == STATES.IDLE:
        set_state_chase()
    health_mananger.hurt(damage, dir)

func start_attack(delta):
    can_attack = false
    anim_player.play("attack", -1, attack_anim_speed_mod)
    attack_timer.start()
    aimer.face_point(player.global_transform.origin + Vector3.UP, delta)


func emit_attack_signal():
    emit_signal("attack")

func emit_death_signal():
    return true

func finish_attack():
    can_attack = true

func can_see_player():
    return player_within_angle(sight_angle) and has_los_player()


func player_within_angle(angle: float):
    var dir_to_player = global_transform.origin.direction_to(player.global_transform.origin)
    var forwards = global_transform.basis.z
    return rad2deg(forwards.angle_to(dir_to_player)) <= angle

func has_los_player():
    var our_pos = global_transform.origin + Vector3.UP
    var player_pos = player.global_transform.origin + Vector3.UP

    var space_state = get_world().get_direct_space_state()

    var result = space_state.intersect_ray(our_pos, player_pos, [], 1)
    if result:
        return false
    return true

func face_dir(dir: Vector3, delta):
    var angle_diff = global_transform.basis.z.angle_to(dir)
    var turn_right = sign(global_transform.basis.x.dot(dir))
    if abs(angle_diff) < deg2rad(turn_speed) * delta:
        rotation.y = atan2(dir.x, dir.z)
    else:
        rotation.y += deg2rad(turn_speed) * delta * turn_right


func alert(check_los=true):
    if cur_state != STATES.IDLE:
        return
    if check_los and !has_los_player():
        return
    set_state_chase()

func within_dis_of_player(dis: float):
    return global_transform.origin.distance_to(player.global_transform.origin) < attack_range
Godot version 3.5.1.stable
in Engine by (16 points)
edited by

1 Answer

0 votes

Someone on reddit helped me figured it out. I had to make a vision manager with a Vector math based functions. Also had to change how the state attack process work to not use a else statement. Probably gonna need to fine tune it some more but at least it can keep up with the player.

by (16 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.