HOME / 第 3 部分 · 2D 游戏开发实战 / 第 26 课
MODULE 3 · 2D 游戏开发实战
第 26 课 · 生命值系统与受伤
🎯 本课学习目标
- 设计玩家生命值与受伤逻辑
- 实现无敌帧(受击闪烁)
- 自定义并发射死亡信号
角色该能「掉血」了。本课实现完整的生命值系统:受伤、无敌、死亡。
📖 详细教学步骤
设计生命值变量代码
在玩家脚本顶部定义:
GDSCRIPTextends CharacterBody2D
signal died # 自定义死亡信号(第9课学过的信号!)
@export var max_hp: int = 3
var hp: int = 3
var invincible: bool = false
func _ready():
hp = max_hp
func take_damage(amount: int):
if invincible:
return # 无敌中,忽略伤害
hp -= amount
print("受到伤害, 剩余HP:", hp)
if hp <= 0:
die()
else:
start_invincible()
无敌帧 + 闪烁代码
受伤后短暂无敌,精灵闪烁表示「无法再被打」:
GDSCRIPT@onready var sprite = $Sprite2D
func start_invincible():
invincible = true
# 用 tween 让精灵闪烁 1 秒
var tween = create_tween()
for i in range(6):
tween.tween_property(sprite, "modulate:a", 0.2, 0.1)
tween.tween_property(sprite, "modulate:a", 1.0, 0.1)
tween.tween_callback(func():
invincible = false
)
死亡处理代码
HP 归零:发射信号 + 删除自己:
GDSCRIPTfunc die():
print("玩家死亡")
died.emit() # 通知其他节点(如 Game 根节点)
queue_free() # 从场景树移除自己
让敌人造成伤害集成
敌人身上加 Area2D(攻击范围),玩家碰到就受伤:
GDSCRIPT# enemy.gd
@onready var attack_area: Area2D = $AttackArea
func _ready():
attack_area.body_entered.connect(_on_attack_body_entered)
func _on_attack_body_entered(body):
if body.has_method("take_damage"):
body.take_damage(1) # 调用玩家受伤方法
Game 根节点监听死亡集成
在 main.gd 监听玩家死亡,触发游戏结束(第 28 课细讲):
GDSCRIPTextends Node2D
@onready var player = $Player
func _ready():
player.died.connect(_on_player_died)
func _on_player_died():
print("触发游戏结束流程")
⚠️
玩家瞬间被打死多次?
无敌帧没有生效。检查 invincible 是否被正确设置/重置,以及 tween 回调是否执行。
⚠️
has_method("take_damage") 判断不生效?
确认玩家脚本确实定义了 take_damage 函数名,拼写一致。
🧪 动手试一试
🏋️ 课后练习
给玩家加一个 get_hit 伤害函数(扣 1 血 + 无敌 0.8 秒闪烁),敌人碰到玩家就调用它。
参考 take_damage + start_invincible 的组合。
💡
用 has_method 检查对方是否有某方法,代码更健壮,不会因对象类型不同报错。
💡
无敌帧是动作游戏手感的核心,时长 0.5-1 秒比较舒服。
🎓 本课小结
生命值系统 = 变量 + take_damage + 无敌帧 + 死亡信号;用 has_method 让任意物体都能打伤玩家。