教程:“从零开始使用 ACTION GAME MAKER”—第 3 章

第 3 章:发射子弹并击败敌人

第 3 章的目标

在第 2 章中,我们完成了以下目标:

  • 创建了一个玩家角色可以行走和跳跃的简单关卡。

在本章中,我们将更进一步,讲解如何实现以下功能:

  • 玩家可以通过发射子弹来击败敌人。

下面是完成第 3 章后效果的示例视频:


规划“玩家射击并击败敌人”的实现方式

让我们先来思考需要哪些步骤。

  • 玩家(Player) 需要新增一个 Shot(射击)状态
  • 为了发射子弹,我们还需要创建一个新的 Bullet(子弹)对象
  • 由于目前还没有敌人,我们也需要创建一个 Enemy(敌人)对象

因此,在第 3 章中,我们的目标如下:

  1. 创建 Bullet 对象
  2. 为 Player 添加 Shot 状态
  3. 创建 Enemy 对象

最后,为了真正实现击败敌人
我们还需要实现造成伤害的流程,也就是一个 攻击系统(Attack System)

ACTION GAME MAKER 提供了一个专门用于处理攻击与伤害的系统
因此,在正式开始制作之前,
让我们先花一些时间来了解这个系统是如何运作的。

ACTION GAME MAKER 中的“攻击(Attack)”与“伤害(Damage)”

攻击命中(Attack Hits)

你还记得 Player 对象中包含的
HitArea2D / HitCollision
以及
AttackArea2D / AttackCollision
这些节点吗?

ACTION GAME MAKER 中,
当来自 AttackArea2DAttackCollision
与来自 HitArea2DHitCollision 发生碰撞时,
系统会自动处理伤害计算

此外,通过 Godot 自带的
CollisionLayer(碰撞层)

CollisionMask(碰撞遮罩)
功能,你还可以精细地控制
“哪些对象可以发生碰撞,哪些不可以”。


CollisionLayer 与 CollisionMask

正如之前在讲解墙体碰撞时所提到的那样,
Godot 是通过 CollisionLayerCollisionMask
这一套系统来控制碰撞关系的。

你可以把它们理解为
为碰撞判定增加了一层“深度”规则

在设置面板中,它们的含义如下:

  • Layer(层) → “这个对象属于哪些层”
  • Mask(遮罩) → “这个对象可以与哪些层发生碰撞”

例如:

  • 如果一个 AttackCollision 的 Mask 只勾选了 第 1 层和第 2 层
    那么它将无法命中只存在于 第 3 层HitCollision

这种机制还可以避免一些不合理的情况,
例如角色的攻击判定撞到自己的攻击判定区域。


伤害处理流程(Damage Processing)

当一次攻击成功命中时,
ACTION GAME MAKER 会自动执行以下处理流程:

  1. 从被攻击对象的 HP 中,
    减去攻击对象的 攻击力(Attack Power)
    (攻击力数值在 BaseSettings 中设置)。

  2. 随后,可以启用 无敌时间(Invincibility)
    (同样在 BaseSettings 中设置),
    使受伤对象在一定时间内不会再次受到伤害。


:white_check_mark: 通过这一套系统,
ACTION GAME MAKER 会自动处理攻击与伤害的核心逻辑
你只需要正确配置相关的节点与参数即可。

1 Like

创建子弹对象(Bullet Object)

在了解了攻击系统的基本机制之后,
接下来我们就来实际创建一个 子弹(Bullet)

子弹同样是一个 Game Object(游戏对象)
因此整体流程和创建玩家角色时是类似的:

  • 指定图像
  • 调整移动速度等设置
  • 使用可视化脚本(Visual Script)进行处理

不过,子弹有一个关键的不同点:
子弹并不是由玩家的输入设备直接控制的

子弹的发射和移动,是通过 “Shoot Bullet(发射子弹)”动作 来完成的,
因此我们不需要为子弹单独编写复杂的移动逻辑。


为子弹创建 Game Object

  1. 打开一个 新的场景标签页
    在创建根节点时选择 Game Object

  1. Create New Object(创建新对象) 窗口中,进行如下设置:
  • Object Name:bullet
  • Template:bullets
  • Type:bullet_base

  1. 子弹对象模板会被加载完成。
  • 右键点击 [unsaved (*)]
    将场景保存为 bullet.tscn


设置子弹的图像

接下来,为子弹指定一张图像。
虽然也可以给子弹制作动画,让效果看起来更酷,
但在本教程中,我们先使用静态图像来保持简单。

  1. 右键点击下方图片,并将其保存到你的电脑中。

bullet

  1. 将下载好的 bullet.png
    拖拽到编辑器中的 FileSystem(文件系统) 面板内。

  2. Scene(场景)面板 中,选择 Sprite2D 节点。

  • 在 Inspector 中,
    bullet.png 拖拽到 Texture 属性(当前为 <empty>)上。

调整子弹的 MoveAndJumpSettings

我们希望子弹的速度比玩家更快。
玩家的移动速度是 300
因此我们将子弹的速度设置为 600

  1. Scene(场景)面板 中,选择 MoveAndJumpSettings 节点。

  1. 在 Inspector 中,将以下数值都设置为 600
  • Horizontal Speed(水平速度)
  • Vertical Speed(垂直速度)


:white_check_mark: 至此,我们已经创建了一个
具备图像显示与独立速度设置的基础子弹对象

调整子弹的攻击判定范围(Attack Hitbox)

在 AttackArea2D 中设置 Collision Layer 与 Mask

默认情况下,这个对象位于 Layer 1
Mask 被设置为 1 和 2
这意味着它可以命中位于第 1 层和第 2 层的对象。

然而,Player(玩家) 也位于 Layer 1
并且同样使用 Mask 1、2
如果保持默认设置,子弹在发射的瞬间就会与玩家发生碰撞。

为了避免这种情况,我们需要将子弹的碰撞设置移动到
Layer 3,并且只与 Layer 3 发生碰撞。

  1. Scene(场景)面板 中,选择 AttackArea2D

  2. Inspector(检查器) 中,展开 Collision(碰撞) 区域。

  1. 会显示一个用于选择 Collision Layer / Mask 的面板。
  • Layer1 → 3
  • Mask1, 2 → 仅勾选 3

现在,子弹就不会再与玩家发生碰撞了。


调整 AttackCollision 的大小

目前,子弹的攻击判定范围比子弹的精灵图像要大得多。
我们需要将它缩小,使其更贴合子弹本身。

  1. Scene(场景)面板 中,选择 AttackCollision

  2. 在编辑器视图中,拖动形状四周的橙色控制点来调整大小。

  • 调整到攻击判定区域 略微大于子弹图像 即可。


:white_check_mark: 至此,子弹的碰撞设置已经正确完成:

  • 子弹不会再命中玩家自身
  • 攻击判定范围也与子弹的大小相匹配

设置子弹的脚本(Bullet Script)

子弹的墙体碰撞(CollisionShape2D)尺寸已经合适,因此不需要再调整。
在完成基础设置后,接下来我们为子弹创建脚本。

子弹在以下情况下应该消失:

  • 命中 敌人
  • 命中 地块(Tile)
  • 移动到 摄像机视野之外

为此,我们只需要 两个状态MoveDisappear
下面来进行设置。


附加脚本

  1. 选择 Bullet(GameObject) 节点。

  2. 在 Scene(场景)面板中,点击 :scroll:+(Attach Script,附加脚本) 按钮。

  1. 保持默认选项,点击 Create
    创建一个名为 bullet.vs 的新脚本。


设置 “Move” 状态

我们将默认的 State001 作为 Move 状态来使用。
由于子弹的移动是由“射击者(Shooter)”来控制的,
因此这个状态本身不需要额外的动作设置。

  1. 选择 State001

  2. 在 Inspector 中,将 Title 改为 Move


创建 “Disappear” 状态

  1. 在 Move 状态右侧 右键点击,选择 Add State(添加状态)

  2. 选择新出现的 State001,将 Title 重命名为 Disappear

  3. 在 Inspector 中,点击 + Add Executable Action

  1. 选择 RemoveSelf,然后点击 Add

  • 这样一来,只要子弹进入 Disappear 状态,
    就会立刻从场景中被移除。

连接 Move → Disappear

在以下情况下,子弹都应该进入 Disappear 状态:

  • 敌人 发生碰撞
  • 地块 发生碰撞
  • 移动到 摄像机范围之外

我们可以在一条链接中,通过 AND(与)/ OR(或)
来组合多个条件。

在这里,我们使用 OR 逻辑:

命中敌人 OR 命中地块 OR 离开摄像机范围

操作步骤如下:

  1. 右键点击 Move 状态,选择 Add Link(添加链接)

  2. 将链接连接到 Disappear 状态。

  3. 选中新建的链接,在 Inspector 中点击 + Add Condition

  1. 选择 ContactWithHitArea

  2. 勾选 Select All Sides(选择所有方向),然后点击 Add

  1. 再次点击 + Add Condition

  2. 这一次选择 ContactWithTile

  3. 将方向设置为 All Directions
    并在逻辑条件中选择 OR,然后点击 Add

  1. 接着点击 + Add Other Condition,添加第三个条件。

  2. 向下滚动,选择 Offscreen(离开屏幕)

  3. Target Type 设置为 This Node
    并将逻辑设置为 OR,然后点击 Add

  1. 确认条件配置如下图所示。

  • 如果 AND / OR 设置不正确,
    可以点击图标在它们之间进行切换。

说明:为什么 “ContactWithHitArea” 不用 AND?

AND / OR 的设置,表示的是与前一个条件之间的关系
由于 ContactWithHitArea第一个条件
因此无论设置为 AND 还是 OR,结果都不会受到影响。

但如果你将它移动到其他条件的下面,
AND 就会生效。
为了避免混淆,这里建议保持为 OR


:white_check_mark: 子弹脚本的设置到此完成!
别忘了 保存场景

修改 Player 对象以发射子弹

为了让玩家能够发射子弹,我们需要完成 四个步骤

A. 创建射击动画
B. 添加并配置 BulletsSettings 节点
C. 添加并配置 Connector 节点(发射位置)
D. 在可视化脚本中创建 “Shoot Bullet(射击)” 状态

接下来,我们从第一步开始:添加射击动画

A. 创建射击动画

首先,我们来思考玩家在什么情况下会进行射击。
由于本教程采用的是 类似《洛克人(Mega Man)》的设计
直觉上似乎会需要三种射击状态:

  • 站立射击
  • 移动中射击
  • 空中射击

不过,我们之前制作的 Move(移动)动画
本身就是角色一边奔跑、一边将手向前伸出的姿势,
这正好可以直接作为“移动中射击”的动画使用。

也就是说,我们实际上只需要再创建 两个新的动画

  • Ground Shot(地面射击)
  • Air Shot(空中射击)

和之前一样,所需的射击姿势已经包含在加载好的精灵表中。
整体流程依然是:

创建动画 → 插入关键帧 → 加载到 Animation Set


创建 “Ground Shot” 与 “Air Shot” 动画

  1. Scene 标签页 切换到 Player
    并将编辑器视图设为 2D

  2. 选择 AnimationPlayer 节点。

  3. 在底部的 Animation 面板中,点击 Animation,创建一个新动画

  1. 将新动画命名为 Ground Shot

  1. 选择 Sprite2D
    在 Inspector 中展开 Animation 属性。

  2. Frame 设置为 1
    (角色站在地面上、手向前伸出的帧),
    然后点击 :key:+(Add Keyframe,添加关键帧) 按钮。

  1. 在弹出的窗口中,取消勾选 Create RESET Track
    然后点击 Create

  1. 重复步骤 2~3,再创建一个新的动画,
    这一次命名为 Air Shot

  1. 保持 Sprite2D 处于选中状态,
    Frame 设置为 2
    (角色在空中、手向前伸出的帧),
    然后点击 :key:+(Add Keyframe) 按钮插入关键帧。


将动画注册到 Animation Set 中

  1. 选择 Player(GameObject) 节点。

  2. 在 Inspector 中,点击 Bulk Load Animations(批量加载动画) 按钮。

  1. 此时,Ground ShotAir Shot
    就会被自动添加到 Animation Set 中。

B. 添加并配置 BulletsSettings 节点

  1. Scene(场景) 窗口左上角,点击 +(Add Child Node,添加子节点) 按钮。

  1. 选择 BulletsSettings,然后点击 Create

  1. Inspector(检查器) 中,点击 Array[BulletData] 字段,展开子弹列表。

  2. 点击 + Add Bullet 按钮。

  3. 点击出现的空白项,选择 New BulletData

  1. 点击新创建的 BulletData 条目,展开其详细设置。

  2. 展开 Bullet Base Settings
    Bullet Object Path 右侧点击 :page_with_curl: 图标,
    选择之前创建好的 bullet.tscn

  1. Unlimit Shots 设置为 On
    这个选项表示场景中可以同时存在无限数量的子弹

  1. 展开 Initial Behavior(初始行为)

  1. Initial Behavior 设置为 FireObjectDirection

  1. 现在,bullet.tscn 就可以被无限制地生成,
    并且会自动朝着发射该子弹的对象所面向的方向飞行。

C. 添加并配置 Connector 节点

Connector 节点用于在可视化脚本中指定一个“位置”,
例如:子弹的发射起点、对象生成的位置等。

这一次,Connector 将作为子弹的发射点
因此我们需要把它放在射击动画中角色手部前端的位置

  1. 选择 Player(GameObject) 节点。

  2. 点击左上角的 +(Add Child Node,添加子节点) 按钮,
    打开 Create New Node(创建新节点) 窗口。

  1. 选择 Connector,然后点击 Create

  1. 选择 AnimationPlayer 节点,
    将当前动画切换为 Ground Shot

  1. 再次选择 Connector 节点。

  2. 将编辑器工具切换到 Move Mode(移动模式)

  1. 拖动 ✛(连接点标记)
    将其放置到角色手部前端的位置。

至此,Connector 的设置就完成了

1 Like

D. 添加射击状态(Shoot State)

接下来,我们来添加用于发射子弹的状态。
我们需要以下几种射击状态:

  • Ground Shot(地面射击)
  • Moving Shot(移动中射击)
  • Air Shot(空中射击)

不过,空中射击需要一些特殊处理
如果我们只是简单地从 Air Shot 切回 Jump
那么每次射击都会再次触发跳跃动作,导致角色越跳越高。

为了解决这个问题,我们需要将 Jump 状态拆分成两个状态:

  • Jump
  • Air

它们的职责如下:

  • Jump 状态只负责“起跳”,并会立刻切换到 Air
  • Air 状态表示角色处于空中
  • Air 状态下,如果角色脚底接触到地面(下方碰撞到 Tile),则返回 Idle
  • Air Shot 在结束后返回 Air,从而避免重复触发跳跃动作

下面我们一步一步来构建。


创建 Ground Shot 与 Moving Shot 状态

  1. 切换到 Script(脚本) 标签页。

  2. Idle 状态左侧附近创建一个新的状态。

  3. 将新建的 State001 重命名为 Ground Shot

  4. Select Animation 中,指定动画为 Ground Shot

  1. 点击 + Add Execution Action(添加执行动作)

  2. 选择 FireBullet 作为执行动作。

  3. Bullet Data 中,指定之前创建的子弹对象。

  4. 点击 Connector 右侧的 Assign 按钮。

  5. Select Node 窗口中,选择之前创建的 Connector 节点。

  1. 确认 Bullet Data = bulletConnector = Connector,然后点击 Add

  2. 接下来创建 Moving Shot,直接复制 Ground Shot 状态:

    • 右键点击 Ground Shot,选择 Copy

- 在 **Move** 状态右侧附近右键,选择 **Paste State**
- 将新状态重命名为 **Moving Shot**
  1. 勾选 Continue Previous Animation
    因为 Moving Shot 使用的动画与 Move 相同,所以这里不需要切换动画。


连接 Idle ↔ Ground Shot 与 Move ↔ Moving Shot

当角色处于 Idle 或 Move 状态时,
按下 Attack 键即可进入对应的射击状态。
射击 0.3 秒后,再返回原来的状态。

  1. 右键点击 Idle,添加一条连接到 Ground Shot 的 Link。

  2. 在属性中勾选 Trigger On Input

  3. 展开 Input Operations List(Array[InputCondition])

  4. 点击 + Add Input 添加新的输入条件。

  5. 点击空白项,选择 New Input Condition

  1. 展开 Input Condition,设置 Input Key = Attack

  1. 右键点击该 Link,选择 Copy

  1. 右键点击 Move,选择 Paste Link

  1. 将该 Link 连接到 Moving Shot

  2. 右键点击 Ground Shot,添加一条返回 Idle 的 Link。

  3. 在 Inspector 中点击 + Add Condition

  1. 选择条件 Elapsed Time

  2. 将时间从 1 秒改为 0.3 秒

  1. 右键点击这条 Link,选择 Copy

  2. 右键点击 Moving Shot,选择 Paste Link,并连接回 Move

至此,地面射击相关状态就完成了。


测试地面射击

  1. 点击右上角的 Run Project(F5)

  2. IdleMove 状态下按 X,应能发射子弹。

  3. 如果没有正常工作,请检查:

    • 动画不播放 → 检查状态动画设置与 AnimationPlayer
    • 动画播放但没有子弹 → 检查 FireBullet 动作设置
    • 子弹出现但不移动或方向错误 → 检查 BulletsSettings

确认无误后,关闭测试窗口。


将 Jump 拆分为 Jump 与 Air 状态

  1. Jump 左侧创建一个新的状态。

  2. State001 重命名为 Air

  3. 将其 Animation 设置为 Jump(用于空中的显示)。

  1. 右键点击 Jump,添加一条连接到 Air 的 Link。

  2. 在 Inspector 中,将 Conditions for Switching 设置为 Unconditional
    (表示 Jump 会立刻切换到 Air)。

  1. 剪切原有的 Jump → Idle Link:选中后右键 → Cut

  1. 右键点击 Air,选择 Paste Link,并连接到 Idle

  2. 这样一来,落地判定就会从 Air 返回 Idle,而不是从 Jump。

  1. 进行一次测试,确认 跳跃 → 空中 → 落地 的流程是否正确。

创建 Air Shot 并进行连接

由于 Air ShotGround Shot 的逻辑相同,只是动画不同,
我们可以直接复用已有状态。

  1. 右键点击 Ground Shot,选择 Copy

  2. 将其粘贴到 Air 状态左侧。

  1. Ground Shot (1) 重命名为 Air Shot

  2. 在 Inspector 中,将 Animation 改为 Air Shot

  1. 复制 Idle → Ground Shot 的 Link。

  2. 右键点击 Air,粘贴该 Link,并连接到 Air Shot

  1. 复制 Ground Shot → Idle 的 Link。

  2. 右键点击 Air Shot,粘贴该 Link,并连接回 Air


最终测试

再次运行项目:

  • 在 Idle 或 Move 状态下按 X → 玩家发射子弹
  • Z 跳跃后,在空中按 X → 玩家进行空中射击

如果这两种情况都能正常工作,
:tada: 你的射击系统就已经完成了!

1 Like

创建敌人对象(Enemy Object)

接下来,我们来创建敌人对象
这次先从最简单的敌人开始——只会左右移动的敌人

整体流程和创建玩家角色时是一样的:

  • 创建一个场景
  • 设置图片与动画
  • 配置各个节点
  • 使用可视化脚本编写行为

创建 enemy GameObject

  1. 将编辑器视图从 Script 切换回 2D

  2. 新建一个场景标签页,在 Create Root Node 中选择 GameObject

  1. Create New Object 窗口中,按如下设置:

    • Object Nameenemy
    • Template:Character
    • Type:2DSprite Character Base
    • Object Group:Enemy

  1. 右键点击 Unsaved,将场景保存为 enemy.tscn

设置敌人的图片与动画

接下来为敌人设置图片和动画。
与子弹那种左右对称的圆形不同,这个敌人是非对称的角色
需要根据移动方向进行左右翻转。

由于左右翻转是通过 Animation Set 自动处理的,
这里的流程与玩家角色完全相同:

  • 使用 Sprite2D 设置贴图
  • 使用 AnimationPlayer 创建动画
  • 在敌人(GameObject)节点上配置 Animation Set

配置 Sprite2D

  1. 右键点击下方图片,将其保存到电脑中。

enemy

  1. 将下载好的图片拖拽到编辑器的 FileSystem 中进行导入。

  2. Scene 面板中选择 Sprite2D 节点。

  3. 在 Inspector 中,将 enemy.png 拖拽到 Texture 属性上。
    由于该图片不需要拆分成多帧,到此 Sprite 的设置就完成了。


使用 CollisionShape2D 调整碰撞形状

当前的碰撞区域明显比角色图片要小,因此需要调整大小。

  1. Scene 面板中选择 CollisionShape2D 节点。

  2. 拖动碰撞框四角的橙色控制点,
    将其调整为与角色图片大小大致一致。


配置 AnimationPlayer 并创建 Animation Set

为了使用 Animation Set,必须至少存在一个动画。
这个敌人不需要切换帧,因此只创建一个简单的动画即可。

  1. 选择 AnimationPlayer 节点。

  2. 在底部的动画窗口中,点击 AnimationNew

  1. 将新动画命名为 Move 并创建。
    由于不需要添加任何轨道,这个动画此时已经完成。

  1. 选择 enemy(GameObject) 节点。

  2. 在 Inspector 中点击 AnimationPlayer

  3. 确认已指定正确的 AnimationPlayer,然后点击 OK

  1. 在 Inspector 中点击 Bulk Load Animations(批量加载动画) 按钮。

  2. 展开 Animation Set,确认 Move 动画已正确注册。

调整敌人的 BaseSettings(基础设置)

为了让敌人能够真正被击败,我们需要配置它的 HP(生命值)无敌时间 等设置。


设置生命值(HP)

  1. 选择 BaseSettings 节点。

  2. 在 Inspector 中,将 HPMax HP 都从 1 改为 3

  • 这表示敌人在受到 3 点伤害 后,HP 会降为 0。

设置无敌时间(Invincibility)

无敌时间 用于防止对象在受到一次伤害后,在极短时间内连续扣除多次 HP。
如果没有这个机制,敌人可能会在同一瞬间被多颗子弹同时命中而瞬间死亡。

由于玩家的射击间隔设置为 0.3 秒
将无敌时间设置为 约 0.2 秒 是一个比较合适的数值。

  • 如果设置为 0.4 秒,敌人可能会无视快速连射中的第二发子弹。
  • 如果设置为 正好 0.3 秒,在近距离射击时,第二发子弹有可能无法正确判定命中。

此外,无敌系统还自带一个 闪烁效果
角色在无敌状态下会周期性闪烁。

在无敌时间为 0.2 秒 的情况下,
将闪烁间隔设为 0.1 秒,闪烁效果会比较容易被观察到。

  1. 在 Inspector 中,将 Invincibility Duration1.0 改为 0.2

  2. 紧接着,将 Blink Duration1.0 改为 0.1

调整敌人的受击判定(Hitbox)

接下来配置敌人的 受击判定区域
由于我们之前将子弹对象的碰撞层设置为 Layer 3
敌人也必须位于 Layer 3,才能正确地与子弹发生交互。


在 HitArea2D 中配置 Collision Layer 与 Mask

  1. 选择 HitArea2D 节点。

  2. 在 Inspector 中展开 Collision 区域。

  3. LayerMask 都设置为 3

  • 这样可以确保敌人的受击判定能够正确地与子弹的攻击碰撞发生作用。

调整 HitCollision 的大小

目前的 HitCollision 区域过大,需要进行调整。

  1. Scene 面板中选择 HitCollision 节点。

  2. 拖动碰撞框四角的橙色控制点,
    将其调整为略大于敌人可见图片的大小。

调整敌人的攻击判定(Attack Collision)

接下来配置敌人的攻击判定区域
与子弹不同,敌人的攻击判定只需要与玩家发生交互即可。

由于玩家的碰撞层(Collision Layer / Mask)保持的是默认设置,
因此这里不需要修改任何层或遮罩设置
只需调整攻击判定的大小即可。

  1. Scene 面板中选择 AttackCollision 节点。

  2. 拖动攻击碰撞框四角的橙色控制点,
    将其调整为与敌人受击判定(Hitbox)大小一致或接近。

设置敌人的脚本(Enemy Script)

敌人只需要左右移动,并在 HP 变为 0 时消失即可。
理论上可以用 Move Left(向左移动)Move Right(向右移动)Vanish(消失) 三个状态来实现,
但这里我们可以利用 Template Movement(模板移动) 动作,它可以自动处理简单的巡逻移动。

因此,这里只需要创建 两个状态

  • Template Move
  • Vanish

当敌人的 HP 变为 0 时,从 Template Move 切换到 Vanish,使敌人消失。


绑定可视化脚本(Visual Script)

  1. 选择 enemy(GameObject) 节点。

  2. 点击 Attach Script(绑定脚本) 按钮。

  1. 保持默认名称 enemy.vs,点击 Create


创建 “Template Move” 与 “Vanish” 状态

  1. 将默认的 State001 重命名为 Template Move

  2. 在 Inspector 中,将 Animation Category 设置为 Move

  3. 点击 + Add Executable Action(添加执行动作)

  1. 选择 MovementTemplate

  2. Move Time 设置为 3,并勾选 Can’t Fall Off Ledges(不会从边缘掉落)

    • 这表示敌人会向一个方向行走 3 秒,
      或在遇到悬崖时自动转向。

  1. 点击 Add

  2. Template Move 状态右侧的空白区域右键,选择 Add State

  3. 将新建的 State001 重命名为 Vanish

  4. 点击 + Add Executable Action

  1. 选择 RemoveSelf 并添加。

如果状态结构与下图一致,则说明状态创建完成。


连接 Template Move → Vanish

  1. 右键点击 Template Move 状态,选择 Add Link,并连接到 Vanish

  2. 在 Inspector 中点击 + Add Condition

  1. 选择 HPIsZero

  2. 保持默认设置(TargetType:This Node),点击 Add

如果连接状态与示例一致,则设置完成。


将敌人放置到 Stage1 场景中

敌人已经准备就绪,接下来将其放入 stage1 场景,与玩家一起进行测试。

  1. 将编辑器视图从 Script 切换回 2D

  2. 切换到 stage1 场景标签页。

  3. 在 Scene 面板中选择 Player 节点。

  4. 在 Player 被选中的状态下,将 enemy.tscn 从 FileSystem 拖拽到场景中,
    放置在 Player 的右侧。

  1. 运行游戏进行测试。

如果一切设置正确:

  • 敌人会每 3 秒 左右来回巡逻
  • 3 发子弹 命中后会消失


故障排查清单(Troubleshooting Checklist)

  • 敌人不左右移动
    → 检查 Template Move 状态中的设置

  • 敌人转向时没有翻转朝向
    → 检查敌人节点的 Animation Set,并确认 Template Move 状态指定了正确的动画

  • 敌人被 1 发子弹击中就消失
    → 检查敌人的 BaseSettings 中的 HP、Max HP、Invincibility 设置

  • 子弹无法命中敌人
    → 检查敌人的 HitArea Collision Layer / Mask
    以及子弹的 AttackArea Collision Layer / Mask

  • 子弹命中后没有消失
    → 检查子弹脚本中的 Move → Disappear 链接,
    并确认 Disappear 状态中已设置 RemoveSelf 动作

1 Like

为玩家添加受伤处理(Damage Handling)

到目前为止,我们已经完成了这一部分的目标:
“玩家可以通过射击来击败敌人。”

不过,现在还存在一个问题:

  • 敌人已经拥有攻击判定(Attack Hitbox)
    但玩家在受到攻击时没有任何反应
  • 此外,由于玩家自己也有攻击判定,
    目前玩家只要与敌人发生碰撞,就能直接击败敌人。

接下来,我们需要调整玩家的设置,使其行为更加合理:

  • 玩家在受到伤害时会产生反应
  • 播放一个**“受伤(Damage)动画”**
  • 受到攻击后会进入无敌时间(Invincibility)
  • 正确调整玩家的 受击判定(Hitbox)攻击判定(Attack Box)
  • 最后,在 可视化脚本(Visual Script) 中添加对应的受伤处理逻辑

添加“受伤(Damage Taken)”动画并注册到 Animation Set

  1. 将编辑器视图切换回 2D,并把场景标签切换到 player

  2. 选择 AnimationPlayer 节点。

  3. 在底部窗口中点击 Animation 按钮,然后选择 New

  1. 将动画名称输入为 DamageTaken,然后点击 OK

  1. 选择 Sprite2D 节点。

  2. “受伤”用的帧是第一行最右侧的图片(Frame 4)。
    在 Inspector 的 Animation 属性中,将 Frame 设置为 4

  1. 点击 Frame 右侧的 :key:+(Add Keyframe,添加关键帧) 按钮。

  2. 在弹出的窗口中,取消勾选 Create RESET Track,然后点击 Create

  1. 至此,“DamageTaken”动画就创建完成了。
    接下来,将它注册到 Animation Set 中。
    请选择 Player(GameObject) 节点。

  2. 在 Inspector 中点击 Bulk Load Animations(批量加载动画) 按钮。

  1. 乍一看可能像是什么都没有添加,但实际上动画在第二页
    点击 Load All Animations 按钮上方的 >(显示为 <1/2>
    切换到第 2 页。

  1. 确认列表中已经出现 DamageTaken 动画。

在 BaseSettings 中调整无敌时间(Invincibility Time)

接下来,我们来配置玩家的无敌时间
由于这是玩家角色,给他们稍微长一点的无敌时间会更合适。
这里我们将无敌时间设为 1 秒,并将闪烁间隔设为 0.2 秒

此外,在无敌期间我们还需要禁用受击判定(Hitbox)
即使在无敌状态下玩家不会减少 HP,
如果不禁用 Hitbox,系统仍然会判定与攻击发生碰撞。


设置步骤

  1. 选择 BaseSettings 节点。

  2. 在 Inspector 中,将 Blink Duration1.0 改为 0.2

  1. Blink Duration 下方,展开 Disabled Hit Collision List(Array…)
    然后点击 + Add Element

  2. 点击出现的空白项,并将其设置为 New DisableCollisionData

  1. 当项目变为 DisableCollisionData 后,点击它以展开设置。

  2. 点击 Assign 按钮。

  3. 在弹出的节点选择窗口中,选择 HitCollision,然后点击 OK


完成以上设置后:

  • 当玩家受到伤害时
  • 将进入 1 秒的无敌状态
  • 在此期间角色会以 0.2 秒间隔闪烁
  • 同时 HitCollision 会被禁用,避免再次触发受击判定

调整攻击判定与受击判定(Attack / Hit Collisions)

首先,我们来调整 HitCollision(受击判定) 的大小。
目前它的尺寸过大,需要缩小一些。

另外,由于本教程采用的是 洛克人(Mega Man)风格 的系统,
玩家角色本身并不需要攻击判定(Attack Hitbox)。

(如果是制作近战攻击角色,这个判定会很重要;
但在本教程中我们不会加入近战攻击。)

因此,这里我们将 完全删除 AttackArea2D


调整 HitCollision 的大小

  1. 选择 HitCollision 节点。

  2. 拖动碰撞框四角的控制点,
    将其调整为略大于墙体碰撞(CollisionShape2D) 的大小。


删除 AttackArea2D

  1. 右键点击 AttackArea2D 节点。

  2. 在菜单底部选择 Delete(删除)

  1. 会弹出一个确认窗口,询问是否要删除
    AttackArea2D 以及其子节点 AttackCollision
    点击 OK 即可完成删除。