Godot Version
4.3
Question
I have logic to spawn troop and enemies in my game. I am currently trying to get them to attack each other when they collide. currently when they do collide they start to play the attack animation and then freeze. In when code, if you remove the start_attack function and just apply the damage directly it worked, however troops and enemies would almost die instantly as they would collide several times per second. To correct this I wanted to include an after_animation so that the damage would only be done after the attack animation . This introduced the error above. I am using a base script for both troops and enemies.
Here is the base troop script
#Base Troop
extends CharacterBody2D
var speed = 100
var target_position: Vector2 = Vector2.ZERO
var damage: int = 10
var current_health = 50
var max_health = 50
var separation_radius = 30.0
var separation_strength = 250.0
var pending_damage: int = 0
var pending_collider: Node = null
var attacking: bool = false
var troop_mode
var defend_position
var attack_position
func _ready():
defend_position = get_node(“/root/Main/defendposition”).global_position
attack_position = get_node(“/root/Main/attackposition”).global_position
var troop_mode_timer = Timer.new()
troop_mode_timer.wait_time = 0.1
troop_mode_timer.one_shot = false
troop_mode_timer.connect(“timeout”, Callable(self, “_on_troop_mode_timeout”)) # Use Callable
add_child(troop_mode_timer)
troop_mode_timer.start()
$AnimatedSprite2D.animation_finished.connect(Callable(self, “_on_animation_finished”))
add_to_group("troop")
func _on_troop_mode_timeout():
troop_mode = GlobalTroopMode.troop_mode
if troop_mode == ‘attack’:
target_position = attack_position
else:
target_position = defend_position
func move_to_target():
var direction = (target_position - global_position).normalized()
velocity = direction * speed
move_and_slide()
func _physics_process(delta: float) → void:
if not attacking: # Only move if not currently attacking
move_to_target()
for i in range(get_slide_collision_count()):
var collision = get_slide_collision(i)
var collider = collision.get_collider()
if collider and not attacking:
if collider.is_in_group(“enemy”):
start_attacking(collider)
break
else:
apply_separation()
if velocity.length() > 0 and not attacking:
$AnimatedSprite2D.play(“run”)
$AnimatedSprite2D.flip_h = velocity.x < 0
else:
$AnimatedSprite2D.stop()
$AnimatedSprite2D.animation = “idle”
func start_attacking(collider):
attacking = true
pending_collider = collider
pending_damage = damage
$AnimatedSprite2D.play(“attack”)
func _on_animation_finished(anim_name: String) → void:
if anim_name == “attack” and pending_collider:
pending_collider.take_damage(pending_damage)
pending_collider = null
pending_damage = 0
attacking = false
func die() → void:
$AnimatedSprite2D.play(“die”)
await get_tree().create_timer(3).timeout
queue_free()
func take_damage(amount: int) → void:
current_health -= amount
if current_health <= 0:
die()
func apply_separation():
var separation_force = Vector2.ZERO
for other_troop in get_tree().get_nodes_in_group(“troop”):
if other_troop != self: # Avoid self-comparison
var distance = global_position.distance_to(other_troop.global_position)
if distance < separation_radius:
var direction = (global_position - other_troop.global_position).normalized()
separation_force += direction * (separation_radius - distance) * separation_strength
velocity += separation_force
here is my base enemy script
#base Enemy
extends CharacterBody2D
var speed = 100
var enemy_target_position
var damage: int = 10
var current_health = 50
var health = 50
var separation_radius = 50.0
var separation_strength = 300.0
var pending_damage: int = 0
var pending_collider: Node = null
var attacking: bool = false
func _ready() → void:
enemy_target_position = get_node(“/root/Main/SpawnPoint/CastlePoint”).global_position
$AnimatedSprite2D.animation_finished.connect(Callable(self, “_on_animation_finished”))
add_to_group(“enemy”)
func move_to_target():
var direction = (enemy_target_position - global_position).normalized()
velocity = direction * speed
move_and_slide()
func _physics_process(delta: float) → void:
if not attacking: # Move only if not attacking
move_to_target()
for i in range(get_slide_collision_count()):
var collision = get_slide_collision(i)
var collider = collision.get_collider()
if collider and not attacking:
if !collider.is_in_group(“enemy”):
start_attacking(collider)
break
else:
apply_separation()
if velocity.length() > 0 and not attacking:
$AnimatedSprite2D.play(“run”)
$AnimatedSprite2D.flip_h = velocity.x < 0
else:
$AnimatedSprite2D.stop()
$AnimatedSprite2D.animation = “idle”
func start_attacking(collider):
attacking = true
pending_collider = collider
pending_damage = damage
$AnimatedSprite2D.play(“attack”)
func _on_animation_finished(anim_name: String) → void:
if anim_name == “attack” and pending_collider:
pending_collider.take_damage(pending_damage)
pending_collider = null
pending_damage = 0
attacking = false
func die() → void:
$AnimatedSprite2D.play(“die”)
await get_tree().create_timer(3).timeout
queue_free()
func take_damage(amount: int) → void:
current_health -= amount
if current_health <= 0:
die()
func apply_separation():
var separation_force = Vector2.ZERO
for other_troop in get_tree().get_nodes_in_group(“troop”):
if other_troop != self: # Avoid self-comparison
var distance = global_position.distance_to(other_troop.global_position)
if distance < separation_radius:
var direction = (global_position - other_troop.global_position).normalized()
separation_force += direction * (separation_radius - distance) * separation_strength
velocity += separation_force