HOME / 第 3 部分 · 2D 游戏开发实战 / 第 26 课
MODULE 3 · 2D 游戏开发实战

第 26 课 · 生命值系统与受伤

⏱ 预计时长 35 分钟📊 难度 入门📚 模块 第 3 部分 · 2D 游戏开发实战

🎯 本课学习目标

角色该能「掉血」了。本课实现完整的生命值系统:受伤、无敌、死亡。

📖 详细教学步骤

设计生命值变量代码

在玩家脚本顶部定义:

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 让任意物体都能打伤玩家。