Godot继承场景实例化行为一致问题:击中一敌全敌受击求助
问题描述
我在Godot中创建了名为EnemyBase的父场景(对应EnemiesBase类,继承自CharacterBody2D),并基于它创建了名为Caius的继承子场景作为敌人。现在在关卡场景中实例化多个该子场景实例,但所有敌人的行为完全一致:当我击中其中一个敌人时,其他所有敌人也会触发受击效果。请求帮助修复。

父类(EnemiesBase)代码
class_name EnemiesBase extends CharacterBody2D #DICT var ANIMATIONS_ENEMY:Dictionary = { "idle" : "idle", "walk" : "walk", "attack" : "attack", "hit" : "hit", "dead" : "dead" } #VAR var _can_attack = false var _collision_weapon_default_positionX:float var _raycast_default_rotate_position:float var _player_ref:Player var _enemy_direction:Vector2 = Vector2.ZERO var _enemy_is_dead:bool = false var _show_blood:bool = false var _is_visible_on_screen:bool = false #ONREADY @onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D @onready var ray_cast_2d: RayCast2D = $RayCast2D @onready var collision_shape_2d_attack: CollisionShape2D = $AreaAttackBase/CollisionShape2D @onready var timer_attack: Timer = $TimerAttack @onready var collision_shape_2d: CollisionShape2D = $CollisionShape2D @onready var timer_to_free: Timer = $TimerToFree @onready var sound: AudioStreamPlayer2D = $Sound #EXPORT @export_category("EnemiesBase Cfg") @export var speed:float = 50 @export var life_health:int = 20 @export var damage_attack:int = 5 @export var enemy_name:String = "Unnamed" func _ready() -> void: _show_blood = GameSetup.show_blood() SignalManager.on_enemy_hit.connect(on_enemy_hit) _collision_weapon_default_positionX = collision_shape_2d_attack.position.x _player_ref = get_tree().get_first_node_in_group(GameManager.PLAYER_GROUP) _raycast_default_rotate_position = ray_cast_2d.rotation set_default_scale_enemy() func _physics_process(_delta: float) -> void: if(_enemy_is_dead): set_physics_process(false) return _flip_me() _enemy_move() func _enemy_move() -> void: if(_player_ref.is_player_upraycast_colliding() or _player_ref.is_player_downraycast_colliding()): animated_sprite.play(ANIMATIONS_ENEMY.idle) set_default_scale_enemy() return if((_player_ref != null) and (_can_attack == false) and (!ray_cast_2d.is_colliding()) and (_is_visible_on_screen)): var _player_collision:Vector2 = _player_ref.get_target_to_attack_position() var x_direction: float = sign(_player_collision.x - global_position.x) var y_direction: float = sign(_player_collision.y - global_position.y) _enemy_direction = Vector2(x_direction, y_direction) velocity = _enemy_direction * speed animated_sprite.play(ANIMATIONS_ENEMY.walk) set_default_scale_enemy() move_and_slide() func _flip_me() -> void: if(_player_ref.global_position.x > global_position.x): animated_sprite.flip_h = false collision_shape_2d_attack.position.x = _collision_weapon_default_positionX ray_cast_2d.rotation = _raycast_default_rotate_position elif(_player_ref.global_position.x < global_position.x): animated_sprite.flip_h = true collision_shape_2d_attack.position.x = -_collision_weapon_default_positionX ray_cast_2d.rotation = -_raycast_default_rotate_position func is_enemy_dead(damage:int) -> bool: life_health -= damage if(life_health <= 0): timer_to_free.start() call_deferred("disable_collisions") return true return false func disable_collisions() -> void: collision_shape_2d.disabled = true collision_shape_2d_attack.disabled = true ray_cast_2d.enabled = false func kill_enemy() -> void: queue_free() func on_enemy_hit(damage:int) -> void: print("Parent Enemy REceive Damage: ", damage) func set_default_scale_enemy() -> void: EnemiesCfg.set_default_scale_animatedsprite2d(animated_sprite) func _on_timer_to_free_timeout() -> void: kill_enemy() func _on_visible_on_screen_notifier_2d_screen_entered() -> void: print("enemy is on scene") _is_visible_on_screen = true SignalManager.get_enemies_on_screen.emit(self) func _on_visible_on_screen_notifier_2d_screen_exited() -> void: print("enemy out of scene") _is_visible_on_screen = false
子类(Caius)代码
class_name Caius extends EnemiesBase @onready var blood: Node2D = $Blood func _physics_process(delta: float) -> void: if(_enemy_is_dead): animated_sprite.play(ANIMATIONS_ENEMY.dead) return super._physics_process(delta) enemy_attack() func enemy_attack() -> void: if(ray_cast_2d.is_colliding() and _can_attack == false and !_enemy_is_dead): _can_attack = true animated_sprite.play(ANIMATIONS_ENEMY.attack) timer_attack.start() _player_ref.on_player_receive_hit(damage_attack) func set_idle_animation() -> void: if(_enemy_is_dead): return animated_sprite.play(ANIMATIONS_ENEMY.idle) func _on_timer_attack_timeout() -> void: _can_attack = false #enemy hit dead func on_enemy_hit(damage:int) -> void: if(_player_ref.get_key_state_selected() != "ARMS"): display_blood() if(!is_enemy_dead(damage)): animated_sprite.play(ANIMATIONS_ENEMY.hit) if(animated_sprite.flip_h == true): global_position.x += global_position.x * 0.1 elif(animated_sprite.flip_h == false): global_position.x -= global_position.x * 0.1 animated_sprite.play(ANIMATIONS_ENEMY.dead) if(_player_ref.get_key_state_selected() == "ARMS"): SoundManager.execute_sound_punch(sound) if(is_enemy_dead(damage)): _enemy_is_dead = true animated_sprite.play(ANIMATIONS_ENEMY.dead) SoundManager.execute_sound_argh(sound) super.set_physics_process(false) func display_blood() -> void: if(_show_blood): for b in blood.get_children(): SignalManager.on_execute_blood.emit(b) func _on_animated_sprite_2d_animation_finished() -> void: if(_can_attack): animated_sprite.play(ANIMATIONS_ENEMY.idle)
问题根源与修复方案
问题1:全局信号广播导致所有敌人响应
所有敌人实例都连接了SignalManager.on_enemy_hit全局信号,信号触发时未指定目标,导致所有订阅该信号的敌人都会执行受击逻辑,出现集体受击现象。
修复方式(二选一):
方案一:修改信号传递逻辑,携带目标敌人实例
- 更新
SignalManager中的信号定义:signal on_enemy_hit(damage: int, target_enemy: Node2D) - 在玩家攻击检测代码中,触发信号时传递被击中的敌人实例:
# 假设检测到击中enemy实例 SignalManager.on_enemy_hit.emit(attack_damage, enemy) - 在
EnemiesBase和Caius的on_enemy_hit方法中添加判断,仅当前实例为目标时才处理:# EnemiesBase中修改 func on_enemy_hit(damage:int, target_enemy:Node2D) -> void: if target_enemy != self: return print("Parent Enemy REceive Damage: ", damage) # Caius中修改 func on_enemy_hit(damage:int, target_enemy:Node2D) -> void: if target_enemy != self: return # 原有逻辑保留 if(_player_ref.get_key_state_selected() != "ARMS"): display_blood() # ... 后续代码
方案二:取消全局信号,直接调用目标敌人方法
删除EnemiesBase的_ready中SignalManager.on_enemy_hit.connect(on_enemy_hit)代码,在玩家攻击检测到具体敌人时,直接调用其on_enemy_hit方法:
# 玩家攻击检测代码中 if enemy != null: enemy.on_enemy_hit(attack_damage)
问题2:子类受击逻辑重复扣血
Caius的on_enemy_hit方法中两次调用is_enemy_dead(damage),导致敌人血量被重复扣除。
修复方式:
将is_enemy_dead的调用结果保存为变量,避免重复调用:
func on_enemy_hit(damage:int) -> void: if(_player_ref.get_key_state_selected() != "ARMS"): display_blood() var is_dead = is_enemy_dead(damage) if(!is_dead): animated_sprite.play(ANIMATIONS_ENEMY.hit) # 修复位移逻辑:原代码位移方向错误,改为向远离玩家的方向推 var push_dir = global_position.direction_to(_player_ref.global_position).opposite() global_position += push_dir * 8 # 可根据需求调整数值 if(_player_ref.get_key_state_selected() == "ARMS"): SoundManager.execute_sound_punch(sound) else: _enemy_is_dead = true animated_sprite.play(ANIMATIONS_ENEMY.dead) SoundManager.execute_sound_argh(sound) set_physics_process(false)
内容的提问来源于stack exchange,提问作者FernandoPaiva
相关产品推荐
相关产品推荐

