第三章:发射子弹击倒敌人
第三章目标
在第二章中,我们完成了“创建玩家角色可以行走和跳跃的简单关卡”。
本次将讲解如何实现“玩家使用射击击倒敌人”。
第三章的完成效果如下所示:
思考“玩家使用射击击倒敌人”的实现方法
首先来思考一下实现方法。
玩家似乎需要“射击”状态,但由于需要发射子弹,因此还需要创建“子弹”对象。
此外,由于尚未创建“敌人”对象,因此也需要创建它。
在第三章中,我们将以创建“子弹”对象、为玩家创建“射击”状态,最后创建“敌人”为目标。
而“击倒敌人”意味着需要实现造成伤害的“攻击”处理。在“ACTION GAME MAKER”中,专门提供了攻击和受击处理的系统。因此,在正式开始制作之前,让我们先学习一下这个系统。
“ACTION GAME MAKER”中的“攻击”与“受击”
攻击命中
您是否还记得玩家对象中的 HitArea2D/HitCollision 和 AttackArea2D/AttackCollision?
在“ACTION GAME MAKER”中,当 AttackArea2D 的 AttackCollision 与 HitArea2D 的 HitCollision 发生碰撞时,系统会自动执行伤害处理。
此外,借助 Godot 的 CollisionLayer(碰撞层)和 CollisionMask(碰撞掩码)功能,可以调整“是否发生碰撞”的行为。
CollisionLayer 与 CollisionMask
在前一部分的“墙壁判定”中已有所说明,在 Godot 中,碰撞行为可以通过 CollisionLayer 和 CollisionMask 功能进行控制。正如名称所示,这些功能为碰撞判定引入了“层级”概念。
您可以在如下设置面板中配置层级和掩码:
理解起来最直观的方式是:层级表示对象位于哪一层,掩码表示对象希望与哪些层级发生碰撞。
例如,如果 AttackCollision 的掩码设置为 1 和 2,而 HitCollision 的层级仅设置为 3,则前者无法命中后者。利用这一机制,可以避免自身攻击判定误伤自身等情况。
伤害处理
当攻击命中时,系统会自动执行伤害处理。具体而言,会从防御方对象的 HP 中扣除攻击方对象在 BaseSetting 中设定的攻击力数值。
随后,通过在 BaseSettings 中设置“无敌”状态,可使对象在一定时间内免受伤害。
创建“子弹”对象
基础攻击处理说明结束后,让我们实际创建子弹。
“子弹”用的对象也是游戏对象,因此基本创建方法与玩家相同。设置图像,调整移动速度等,并在可视化脚本中创建处理逻辑。
不同的一点是,它不是由控制器操作的对象。因此,虽然需要移动处理,但对于发射的“子弹”,可以通过“发射子弹”动作赋予移动,所以无需在“子弹”端创建移动处理。
那么,让我们开始创建并设置对象吧。
创建“子弹”用游戏对象
- 打开新场景标签页,在生成根节点时选择“游戏对象”。
- 在“创建新对象”窗口中,设置对象名:bullet,使用模板:子弹,类型(类)为 bullet_base。
- 子弹用对象模板将被加载,请右键点击[未保存(*)] 进行保存。名称保持不变,保存为 bullet.tscn。
“子弹”的图像设置与动画设置
让我们为“子弹”添加图像。虽然可以设置“动画”来制作更酷的子弹,但本次我们将制作一个简单的、不带动画的子弹。因此,只需将图像设置为 Sprite2D 的纹理即可。
- 请右键点击以下图像下载子弹图像。

- 将下载的 bullet.png 从 PC 拖放加载到文件系统中。
- 在场景窗口中选择 Sprite2D,将 bullet.png 拖放到纹理的<空>处。
调整“子弹”的 MoveAndJumpSettings
由于希望移动速度比玩家更快,让我们进行更改。玩家的速度是 300,因此将其设为 600。
-
在场景窗口中选择 MoveAndJumpSettings 节点。
-
在检查器的属性中,将左右移动量和上下移动量都设为 600。
调整“子弹”的攻击判定。
在“AttackArea2D”中设置碰撞层和掩码。
初始状态下,该对象的层为 1,掩码为 1 和 2。这意味着它会命中层 1 和 2 的对象。但实际上,玩家的层也是 1,掩码也是 1 和 2。如果保持现状,子弹发射的瞬间就会击中玩家。因此,为了避免这种情况,请将层设置为 3,掩码也设置为 3。
-
在场景窗口中选择“AttackArea2D”。
-
在检查器窗口的属性中,点击“碰撞”并展开它。
- 将出现用于选择碰撞层/掩码的面板。将层从 1 更改为 3,将掩码从 1,2 更改为仅 3。这样,攻击就不会再击中玩家了。
调整“AttackCollision”的大小。
目前,攻击判定的范围比子弹大得多,请将其调小。
-
在场景窗口中选择 AttackCollision。
-
在编辑器画面中,拖动 AttackCollision 四周的点,将其调整为比子弹稍大一点的大小。
设置“子弹”的脚本。
墙壁碰撞体(CollisionShape2D)的大小刚好合适,似乎无需修改。基本设置完成后,让我们开始创建脚本。
只要实现“移动”过程中“碰到敌人”、“碰到瓦片”或“超出摄像机范围”时消失即可。也就是说,所需的状态只有“移动”和“消失”两种。让我们立刻开始制作吧。
附加脚本
- 选择 Bullet(GameObject)节点。
- 点击场景窗口右上角的
+(附加脚本)按钮。
- 无需特别更改,直接点击“创建”,生成 bullet.vs 文件。
设置状态“移动”
复用默认状态 001 来创建“移动”状态。如前所述,移动的处理由“发射子弹的一方”执行,因此无需特别设置动作。
- 选择状态 001。
- 在检查器窗口中将标题更改为“移动”。
创建“消失”状态
- 在“移动”右侧右键点击,选择“添加状态”。
- 选择新创建的“状态 001",在检查器中将标题更改为“消失”。
- 继续在检查器中点击“+添加执行动作”。
- 选择执行动作“使自身消失”,然后点击“添加”。
- 至此,进入此状态后,该子弹将会消失。
创建从“移动”到“消失”的链接
让我们设定在“移动”过程中,若碰到敌人、碰到瓦片或超出摄像机范围,则进入“消失”状态。在这种情况下,条件共有“三个”,但通过“AND(且)”和“OR(或)”将这三个条件连接起来,即可在一个链接中处理多个条件。
本例中,通过连接“碰到敌人”“OR(或)”“碰到瓦片”“OR(或)”“超出摄像机范围”,即可处理这三个条件。
- 右键点击“移动”状态以添加链接。
- 点击“消失”状态以连接链接。
- 选择创建的链接,在检查器中点击“+添加其他条件”。
- 选择“与物体碰撞检测接触”。
- 勾选所有方向,然后点击“添加”按钮。
- 再次点击“+添加其他条件”。
- 接下来选择“与瓦片接触”条件。
- 再次勾选所有方向,将与前一个条件的逻辑关系设为"OR",然后点击“添加”按钮。
- 通过“+添加其他条件”添加第三个条件。
- 滚动条件列表,选择其他选项中的“超出摄像机范围”。
- 将数据类型(判定目标对象)设为“自身”,将与前一个条件的逻辑关系设为"OR",然后添加。
- 请确认其他条件如下所示。如果 AND/OR 设置不正确,可以通过点击 AND/OR 图标进行修改。
补充:与物体碰撞检测接触使用"AND"是否合适?
AND/OR 顾名思义,是表示与**“前一个”**条件的逻辑关系,即针对上一层条件进行判定。“与物体碰撞检测接触”位于最顶层,由于没有前一个条件,因此无论是 AND 还是 OR 都可以。不过,如果调整顺序将其置于第二层或更下层,则会按 AND 逻辑运行,因此设为 OR 会更稳妥。
至此,子弹的设置已完成。请记得“保存场景”。
修改“玩家”对象以使其能够发射子弹
要使对象能够发射子弹,需要四个步骤:
A. 创建发射子弹的动画。
B. 添加并配置“BulletsSettings”节点以设置子弹参数。
C. 添加并配置作为子弹发射点的“Connector”节点。
D. 在视觉脚本中创建“发射子弹”状态。
那么,让我们从添加发射动画开始吧。
A. 创建发射子弹的动画
首先,让我们思考一下想要发射子弹的场景。既然是洛克人风格,似乎需要三种状态:“站立发射”、“行走中发射”和“跳跃中发射”。每种状态都需要相应的动画……但实际上,已经制作好的“移动”动画本身就是在手持武器奔跑的动画,可以直接用于射击,因此可以直接使用。
因此,我们只需创建“地面发射”和“空中发射”两种动画即可。
制作方法与前一部分完全相同。请从已加载的精灵表中选择适合发射动作的素材。
创建动画的步骤是:“添加动画”、“插入关键帧”、“将动画加载到动画集”。
创建“地面发射”和“空中发射”动画
- 将场景标签页设置为 player,并将编辑器视图更改为 2D。
- 选择 AnimationPlayer 节点。
- 点击底部动画窗口中的“动画”以创建新动画。
- 将动画名称设为“地面发射”。
- 选择 Sprite2D,展开检查器中的“动画”属性。
- 将帧设置为 1(左手向前伸出的图像),然后点击“
+”(添加关键帧)按钮。
- 取消勾选“RESET 轨道”以创建轨道。
- 重复步骤 2 至 3,创建“空中发射”动画。
- 在 Sprite2D 的检查器中,将帧设置为 2(在空中向前伸手出的动画),然后点击“
+”(添加关键帧)按钮以插入关键帧。
注册到动画集
-
选择 Player (GameObject) 节点。
-
点击检查器中的“批量加载动画”按钮进行加载。
- “地面发射”和“空中发射”将被添加。
C: 添加和设置连接点“Connector”节点
连接点是用于在视觉脚本中指示“位置”的节点,例如子弹的发射源或对象的生成位置。由于此连接点是发射点,因此应在发射动画中手持的手的末端设置连接点。
-
选择 Player(GameObject) 节点。
-
点击左上角的“+”(添加子节点)按钮,打开“创建节点”画面。
-
选择 Connector 节点并点击“创建”。
- 选择 AnimationPlayer 节点,并将动画切换为“地面发射”。
-
再次选择 Connector 节点。
-
将编辑器的工具切换为“移动模式”。
- 将连接点的✛移动到手掌的末端。至此,连接点的设置已完成。
D. 添加发射子弹的状态
接下来添加发射子弹的状态。当然需要“地面发射”、“移动发射”和“空中发射”这三种状态,但“空中发射”似乎需要一些特别的处理。
从“地面发射”或“移动发射”状态,通过转移到“待机”或“移动”状态即可返回,但如果将“空中发射”状态通过转移回到“跳跃”状态,则会再次触发跳跃,导致每次发射子弹时都会通过跳跃上升。
因此,需要一种除“跳跃”以外的空中状态。这次我们将“跳跃”状态拆分为“跳跃”和“空中”两个状态。从“跳跃”状态立即转移到“空中”状态,并在“空中”状态下,当“接触到图块”时转移到“待机”状态即可。
然后,从“空中发射”状态返回到“空中”状态,就不会出现问题了。
那么,我们立即开始制作。
制作“地面发射”和“移动发射”状态
-
在“待机”状态的左侧附近创建一个新的状态。
-
将创建的“状态001”重命名为“地面发射”。
-
在动画类别名称中指定“地面发射”动画集。
-
点击“添加执行动作”。
-
在执行动作中选择“发射子弹”。
-
在子弹数据中指定刚才创建的“子弹”。
-
点击连接点的“分配”按钮。
-
出现“选择节点”窗口,指定刚才创建的连接器(Connector)。
- 确认子弹数据“子弹”的连接点“连接器”已正确指定后,点击“添加”。
- 接下来是“移动发射”,但“移动发射”除了动画外,与“地面发射”完全相同,因此可以复制并重复使用。右键点击“地面发射”状态,选择“复制”。
- 在“移动”状态的右侧附近右键点击,选择“粘贴状态”。
-
会出现名为“地面发射(1)”的状态,在检查器中将其重命名为“移动发射”。
-
启用“保持转移前的动画”复选框。“移动射击”的动画与转移源“移动”完全相同,因此无需更改,在这种情况下可以使用此选项。
连接“待机”与“地面发射”、“移动”与“移动发射”状态
如果从“待机”或“移动”状态按下“攻击”按钮后转移到“地面发射”或“移动发射”状态,然后再返回“待机”或“移动”状态,应该就能正常工作。为了避免连射过多,可以设置经过“0.3秒”后返回,这样就能实现适度的动作。
-
右键点击“待机”状态创建链接,并连接到“地面发射”状态。
-
在属性中启用“已输入以下操作”的复选框。
-
展开输入操作列表右侧的“Array[InputCondition…]”。
-
点击“添加输入”以添加输入栏。
-
点击“<空>”以添加“新输入条件”。
-
点击“输入条件”以展开。
-
将“输入键”更改为“攻击”。
-
右键点击创建的链接,选择“复制”。
-
右键点击“移动”状态,选择“粘贴链接”。
- 将链接连接到“移动发射”状态。现在,“待机”和“移动”的链接已完成,接下来是返回链接。
-
右键点击“地面发射”状态,选择“添加链接”,并连接到“待机”状态。
-
在检查器中点击“添加其他条件”按钮。
-
在“其他”栏中选择“经过一定时间”条件。
-
将“数值”项目中的秒数从1秒更改为0.3秒。
-
添加完成后,右键点击链接,选择“复制”。
-
右键点击“移动发射”状态,选择“粘贴链接”并连接到“移动”。如果如下所示,则地面发射状态已完成。
测试子弹是否能在地上正确发射
到目前为止,我们完成了“子弹”对象的创建、“BulletSettings”的设置以及“地面发射”和“移动发射”脚本的制作,共三个阶段。现在终于可以测试子弹发射了,让我们在测试运行中尝试一下。
- 点击编辑器右上角的“运行项目(F5)”按钮。
- 如果设置正确,在“待机”或“移动”状态下按下X键应该会发射子弹,但由于设置项目较多,可能会有遗漏。请通过以下列表进行再次检查。
如果无法正常工作时的检查清单
动画未播放:请重新检查各发射状态的设置。如果没有问题,请重新检查AnimationPlayer的动画。
动画播放但子弹未出现:请重新检查“发射子弹”动作的设置。
子弹出现但不移动或朝奇怪的方向移动:请重新检查“BulletsSettings”。
确认没有问题后,点击关闭按钮结束测试运行。
将“跳跃”状态拆分为“跳跃”和“空中”状态
现在,让我们拆分“跳跃”状态。
-
在“跳跃”状态的左侧附近添加“状态”。
-
将状态001重命名为“空中”。
-
将动画类别名称设置为“跳跃”,“空中”状态即完成。
-
继续连接链接,右键点击“跳跃”状态,选择“添加链接”,并连接到“空中”。
-
在检查器中将“切换状态的条件”设置为“无条件”。“无条件”是指无需条件即可立即切换状态的条件。“跳跃”动作会立即执行,且“跳跃”动画仅切换帧,因此使用此条件即可。
-
接下来,将“跳跃”返回“待机”的链接更改为从“空中”返回“待机”的链接,选择该链接。
-
右键点击并选择“剪切”。
- 右键点击“空中”状态,选择“粘贴链接”。
- 连接到待机即可完成。
- 为保险起见,进行测试运行,检查从跳跃到落地的流程是否正确。
创建“空中发射”状态并连接到“空中”
“空中发射”状态及其链接条件与刚才创建的“地面发射”相同,只是动画不同,因此可以重复使用。
-
右键点击“地面发射”状态,选择“复制”。
-
粘贴到“空中”状态的左侧。
-
在检查器中将“地面发射(1)”重命名为“空中发射”。
-
将“动画类别”更改为“空中发射”。
-
复制“待机”→“地面发射”的链接。
-
右键点击“空中”状态,选择“粘贴链接”,并连接到“空中发射”。
-
复制“地面发射”→“待机”的链接。
-
右键点击“空中发射”状态,选择“粘贴链接”,并连接到“空中”。
- 至此完成,再次进行测试运行以检查动作。如果按下Z键跳跃,然后在跳跃过程中按下X键能正常发射子弹,则成功。
创建“敌人”对象
接下来,让我们创建敌人对象。我们将创建一个最简单的敌人类型,即仅左右移动的敌人。创建方法与角色相同:创建场景、制作图像和动画、配置各个节点,并编写脚本。
创建用于“enemy”的游戏对象
-
将编辑器画面从“Script”切换回“2D”。
-
创建一个新的场景标签页,在根节点生成时选择“游戏对象”。
- 将对象名称设置为“enemy”,使用模板:角色,类型:2DSprite Character Base,对象组:Enemy 进行创建。
- 右键点击 [未保存] (*),将其保存为 enemy.tscn。
敌人(enemy)的图像设置与动画设置
请使用以下准备好的图像。与子弹一样,这是一个仅由单张图像构成的简单角色。与不需要正圆翻转的子弹不同,该敌人左右不对称,需要根据移动方向进行翻转。自动翻转功能使用了动画集(AnimationSet)功能,因此像玩家一样,使用 Sprite2D 设置纹理,使用 AnimationPlayer 创建动画,并在 enemy 节点(GameObject)中设置动画集。
Sprite2D 的设置

-
右键点击上述图像并保存到 PC。
-
通过拖放将其加载到文件系统中。
-
选择 Sprite2D 节点。
-
将 enemy.png 拖放到检查器(Inspector)的纹理(Texture)属性中。由于此图像无需分割帧,图像设置至此完成。
使用 CollisionShape2D 调整碰撞判定大小
目前的碰撞判定大小似乎比设定的精灵(Sprite)小很多,因此请将其放大。
-
在场景窗口(Scene Window)中选择 CollisionShape2D。
-
拖动碰撞判定四角的橙色圆点,调整到合适的大小。
AnimationPlayer 的设置与动画集创建
为了设置动画集,需要创建动画。但与玩家不同,由于无需切换帧,只需创建动画即可。
-
选择 AnimationPlayer 节点。
-
点击底部中央窗口的“动画(Animation)”,然后选择“新建(New)”。
- 输入新动画名称“移动(Move)”并创建。由于轨道(Track)无需输入任何内容,动画至此完成。接下来创建动画集。
-
选择 enemy(GameObject)节点。
-
点击检查器中的“指定 AnimationPlayer(Assign AnimationPlayer)”。
-
确认 AnimationPlayer 已正确指定,然后点击确定(OK)按钮。
-
点击检查器中的“批量加载动画(Batch Load Animations)”按钮。
-
展开动画集,确认“移动”已正确设置。
调整“enemy”的“BaseSettings”
必须确保敌人能够通过攻击被击败,因此请设置其生命值和无敌时间。
设置“生命值”
-
选择 BaseSettings 节点。
-
在 Inspector 中将“生命值”和“最大生命值”从 1 修改为 3。这样,当受到 3 点伤害时,HP 将变为 0。
设置“无敌时间”
“无敌时间”是指在受到伤害时,使 HP 在一定时间内不减少的设置。如果不进行此设置,攻击命中的瞬间可能会导致连续受到伤害。
玩家子弹的发射间隔已设置为 0.3 秒,因此将无敌时间设置为约 0.2 秒即可。如果将无敌时间设为 0.4 秒,则第二次射击的子弹将无法命中;如果正好设为 0.3 秒,在敌人移动并接近的场景中,第二次射击的子弹也可能无法命中。
此外,无敌功能包含在无敌期间让对象闪烁以提示无敌时间的机制。由于无敌时间为 0.2 秒,若以 0.1 秒的间隔进行闪烁,即可看到闪烁效果。
-
在 Inspector 中将“无敌时间(秒)”属性从 1 秒修改为 0.2 秒。
-
将其下方的“闪烁间隔(秒)”属性从 1 秒修改为 0.1 秒。
调整“enemy”的“碰撞检测”。
接下来,设置“enemy”的“碰撞检测”。由于已将“子弹”对象的对撞机层更改为“3”,因此需要将其设置为与层“3”对应。
在“HitArea2D”中设置对撞机层和掩码。
- 选择“HitArea2D”节点。
- 展开检查器中的“碰撞”栏。
- 将层设置为3,掩码设置为3。这样,“子弹”的“攻击判定”就能相互接触了。
调整“HitCollision”的大小。
目前的HitCollision似乎太大了,让我们将其调小一些。
- 在场景窗口中选择HitCollision。
- 拖动墙壁判定四角的橙色圆点,将其调整为比墙壁判定稍大的尺寸。
调整“enemy”的“攻击判定”。
接下来是“攻击判定”,只要攻击判定能击中玩家即可。与子弹不同,玩家的对撞机层(Collision Layer)保持默认设置,因此无需更改。只需调整碰撞(Collision)的大小即可。
-
在场景窗口中选择 AttackCollision。
-
拖动墙壁判定四角的橙色圆点,将其调整为与攻击判定相同的大小。
设置「enemy」脚本
「enemy」只需左右移动,因此只要实现左右移动和HP归零时消失即可。简单来说,只需「向左移动」、「向右移动」、「消失」这三个状态,但本次可以使用可自动设置简单移动的「模板移动」动作。
因此,创建「模板移动」和「消失」两个状态,并设置当HP归零时链接到「消失」状态。
附加视觉脚本
-
选择 enemy(GameObject) 节点。
-
点击附加脚本按钮附加脚本。
- 名称保持为 enemy.vs,点击创建。
制作「模板移动」和「消失」状态
-
将状态001重命名为「模板移动」。
-
在检查器中将动画类别设置为「移动」。
-
点击「+ 添加执行动作」按钮。
-
选择「模板移动设置」。
-
将「移动时间」设置为3,并将「不从台阶跌落」设为开启。这样将移动3秒,若遇到类似台阶的落差则折返。
- 点击「添加」。
-
接着,右键点击「模板移动」状态右侧的空间,选择「添加状态」。
-
将状态001重命名为「消失」。
-
点击「+ 添加执行动作」按钮。
-
选择「销毁自身」并添加。
-
若显示如下,则状态设置完成。
创建从「模板移动」到「消失」的链接
-
右键点击「模板移动」状态,选择「添加链接」,连接到「消失」状态。
-
点击检查器中的「+ 添加其他条件」按钮。
-
选择「HP变为0」。
-
设置项保持默认(数据类型:自身)即可添加。
- 若显示如下,则设置完成。
将「enemy」放置到「stage1」场景中
现在,enemy的设置已完成,将其放置到stage1中进行测试。请将其放置在与Player相同的图层上。
-
将编辑器画面从Script切换为2D。
-
将场景标签切换为stage1。
-
在场景窗口中选择Player节点。
-
保持选中状态,从文件系统将「enemy.tscn」拖放到Player的右侧。
- 放置完成后,进行测试游玩。
若确认敌人每3秒左右移动,且被击中3次后消失,则成功。
不动时的检查清单
若敌人不左右移动:请检查「模板移动」状态。
若敌人折返时不翻转:请重新检查enemy节点的动画集和「模板移动」状态的动画指定。
若敌人被1发子弹击中就消失:请重新检查 enemy的basesettings中的「体力」「最大体力」和「无敌」设置。
若子弹无法击中:请检查 enemy的「HitArea」的CollisionLayer/Mask,以及bullet的「AttackArea」的CollisionLayer/Mask。
若子弹不消失:请检查「bullet」从移动到消失状态的链接,以及消失状态的动作。
为“玩家”添加受伤害处理。
好了,现在我们已经实现了本部分的目标:“玩家用射击击败敌人”,但敌人虽然有攻击判定,玩家却没有任何反应。
此外,由于玩家也拥有攻击判定,会导致玩家通过冲撞直接击败敌人。
接下来,我们将调整玩家设置,使其在受到伤害时做出反应。添加“受伤害”动画,调整“无敌时间”、“碰撞判定”和“攻击判定”,最后添加可视化脚本。
在「BaseSettings」中调整无敌时间。
接下来设置无敌时间。由于无敌时间是针对玩家的,建议设置得长一些。我们将无敌时间设为1秒,闪烁间隔设为0.2秒。
此外,为了避免在无敌期间受到攻击时产生反应,请在无敌期间取消碰撞判定。虽然无敌期间HP不会减少,但系统仍会判定为“受到攻击”。
-
选择 BaseSettings 节点。
-
在检查器中将“闪烁间隔(秒)”从 1.0 修改为 0.2。
-
展开“闪烁间隔(秒)”下方的“禁用碰撞列表”中的“Array…”,然后点击“添加元素”。
-
点击 <空> 以设置新的“DisableCollisionData”。
-
<空> 将变为“DisableCollisionData”,请点击并展开它。
-
点击“分配”按钮。
-
将弹出节点选择窗口,选择“HitCollision”并点击“确定”。这样,当受到攻击时,碰撞判定将在1秒内无效。