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

第4章:让我们把它变成一款“游戏”

在上一章中,我们通过让玩家发射子弹并击败敌人,完成了游戏玩法的基础部分。

在本章中,我们将进一步推进,使这个项目真正能够作为一款游戏进行游玩

要成为一款游戏还缺少什么?

在当前版本中,玩家可以移动,敌人也存在,但仍缺少几个要素。

  • 舞台太小,需要扩大。
  • 玩家虽然有受伤反应,但实际上无法被击败。我们也看不到玩家剩余的HP,因此需要将其可视化。
  • 最后,游戏没有目标。理想情况下,我们可以加入Boss战,但为了简化,我们设定为:击败5个敌人即可通关

完成示例

扩展阶段1

首先,让我们通过绘制更多图块来扩展阶段。由于阶段将变得更大,玩家最终会移动到当前摄像机范围之外,因此我们还需要确保摄像机跟随玩家。

绘制更多图块

图块是使用 基础(TileMapLayer) 节点创建的,因此让我们使用此节点来扩展阶段。

  1. 切换到 stage1 场景选项卡。

  2. 在场景窗口中,选择 基础(TileMapLayer) 节点。

  3. 在底部窗口中,打开 TileMap 选项卡并选择一个图块。

  4. 在右侧空间自由放置图块。为了保持阶段平衡:

    • 空中的平台和墙壁高度不应超过 4 个图块

    • 间隙或坑洞的宽度应约为 4 个图块 或更窄,以便可以轻松跳过。

一旦布置了足够的图块,请继续下一步。

让摄像机跟随玩家

ACTION GAME MAKER 中,摄像机跟随功能通过名为 目标 ID(Target ID) 的系统进行控制。还记得我们创建玩家并添加 CameraTargetSettings 节点时吗?该节点有一个名为 目标 ID 的属性。

摄像机节点 ZoomCamera2D 也拥有一个 目标 ID 属性。当摄像机和游戏对象共享相同的目标 ID 时,摄像机将自动跟随该对象。

现在让我们为 ZoomCamera2D 和玩家的 CameraTargetSettings 配置目标 ID。

为 InitialCamera (ZoomCamera2D) 设置目标 ID

  1. Stage1 场景选项卡中,选择 InitialCamera (ZoomCamera2D) 节点。

  2. 在检查器(Inspector)中,展开 目标 ID (数组…) 属性。

  3. 点击 + 添加元素 按钮。

  4. 将出现一个文本字段。删除占位符文本 <null> 并输入 player

将目标 ID 添加到玩家的 CameraTargetSettings 节点

  1. 切换到 player 场景选项卡。

  2. 选择 CameraTargetSettings 节点。

  3. 在检查器中,在 目标 ID 字段中输入 player

测试扩展后的场景

现在让我们测试运行扩展后的场景,以检查一切是否正常工作。尝试将玩家移动到场景的最右侧边缘。

如果设置正确,摄像机应能平滑地跟随玩家。


检查清单

  • 摄像机未跟随: 请仔细检查 Stage1 中的 InitialCamera 和玩家的 CameraTargetSettings 中的目标 ID 设置。

  • 无法到达场景最右侧:Base (TileMapLayer) 中调整坑洞的大小或墙壁的高度。

为角色添加“死亡”效果:创建粒子

就像敌人一样,当角色的生命值(HP)降至 0 时,角色也应被击败。由于没有适合死亡动画的帧,让我们使用粒子来创建视觉效果。

什么是粒子?

粒子本质上是大量散布的微小图像,用于创建某种效果。可以想象成彩纸屑——这样更容易理解。使用粒子,您可以自由调整每个“碎片”的外观及其运动方式。

Godot 引擎中,这通常通过GPUParticle2DCPUParticle2D节点来实现。
ACTION GAME MAKER中,粒子通过ParticleObject节点进行处理。

ParticleObject自带超过20 种粒子模板,您可以直接使用。


创建粒子对象

创建粒子对象与创建游戏对象完全相同。

  1. 创建一个新场景标签页

  2. 对于根节点,选择GameObject

  3. 对象名称设置为 DeathParticle,选择模板particles,然后点击创建

  4. 在场景窗口中,选择新创建的DeathParticle节点。

  5. 在检查器中,将粒子模板None更改为Fireworks

  6. 将自动添加一个GPUParticles2D节点。

  7. 在 GPUParticles2D 检查器中,勾选Emitting属性。这将启用粒子发射。

  8. 您现在应该能在场景视图中看到粒子正在发射,形成烟花效果。
    最后,右键点击 [未保存](*) 标签页,将场景保存为 deathparticle.tscn

为玩家的视觉脚本添加“死亡”状态

考虑“死亡”状态

当玩家的生命值(HP)降至 0 时,他们应停止移动并触发“死亡粒子”效果。
从**任意状态(AnyState)**连接到此状态是合理的,但存在一个问题:

目前,如下所示,无论玩家处于何种状态,只要与敌人的攻击发生碰撞,玩家就会进入**受到伤害(Take Damage)**状态。

由于 HP = 0 意味着玩家已经受到过攻击,他们甚至可能在“死亡”状态下仍然进入**受到伤害(Take Damage)**状态。

为了解决这个问题,我们将为伤害转换添加**“HP 不为 0”**的条件,以防止玩家在已经死亡时触发该转换。


创建“死亡”状态

由于没有完全匹配的动画,我们将复用**受伤(Damage)动画。但为了在视觉上有所区分,我们还将使用滤镜(Filter)**效果,以明确显示玩家已被击败。

  1. 打开**玩家(Player)**场景。

  2. 将编辑器从2D切换为脚本(Script)

    image

  3. 受到伤害(Take Damage)状态附近的上方区域,右键单击并选择添加状态(Add State)

  4. 将新状态重命名为死亡(Death)

  5. 动画(Animation)设置为DamageTaken

  6. 展开**动作设置(Action Settings)**部分。

  7. 启用忽略移动输入(Ignore Movement Input)

  8. 单击+ 添加可执行动作(Add Executable Action)

  9. 选择显示粒子(DisplayParticle)

  10. 在粒子对象路径字段中,单击:page_with_curl:图标,并选择之前创建的DeathParticle.tscn

  11. 单击添加(Add)

  12. 再次单击+ 添加可执行动作(Add Executable Action)

  13. 选择应用对象滤镜(ApplyObjectFilter)

  14. 滤镜类型(Filter Type)设置为透明(Transparent),将结束时间(Finish Time)设置为3.0 秒。这将使玩家逐渐在 3 秒内淡出。

  15. 单击添加(Add)

  16. 如果动作列表看起来正确,则死亡状态已准备就绪。


将任意状态链接到“死亡”状态

  1. 右键单击任意状态(AnyState)添加链接(Add Link) → 将其连接到死亡(Death)

  2. 单击+ 添加其他条件(Add Other Condition)

  3. 选择HP 为零(HPIsZero)并单击添加(Add)


为“伤害”转换添加条件

最后,我们需要确保当 HP 已经为 0 时,**受到伤害(Take Damage)转换不会触发。我们可以通过启用反向(Is Reversed)**选项来实现。

  1. 选择从**任意状态(AnyState)受到伤害(Take Damage)**的链接。

  2. 单击+ 添加条件(Add Condition)

  3. 选择HP 为零(HPIsZero)

  4. 启用反向(Is Reversed)。这将把条件更改为**“HP 不为零”**。

  5. 如果“其他条件”面板显示反转后的条件(≠ 图标高亮),则设置正确。


测试“死亡”状态

现在让我们进行测试。由于玩家的初始 HP 设置为 1,他们在受到一次敌人攻击后就会进入死亡状态。

如果一切设置正确:

  • 受到攻击时,应出现**死亡粒子(Death Particle)**效果。

  • 玩家应逐渐淡出。

  • 击败后,按F5重置游戏。


故障排除

  • 粒子不显示: 检查DeathParticle.tscn和**显示粒子(DisplayParticle)**动作。

  • 玩家未淡出: 检查**应用对象滤镜(ApplyObjectFilter)**动作的设置。

  • 死亡后仍触发伤害反应: 仔细检查链接条件以及**受到伤害(TakeDamage)**转换上的反转条件。

创建 HP 条

目前,玩家被一击击败过于严苛,因此我们需要增加玩家的 HP。但如果玩家拥有更多 HP,我们还需要一种方式来显示剩余量。为此,我们将创建一个 HP 条

ACTION GAME MAKER 中,您可以使用 SimpleGaugeImageGauge 节点来显示计量条。本教程将使用 SimpleGauge

由于相机现在会跟随玩家移动,如果将 HP 条放置在与玩家相同的图层中,它将会移出视野。对于 HP 条等需要始终可见的元素,您应该使用 UI 图层


关于 UI 图层

您可能还记得,之前的 ACTION GAME MAKER 图层 结构如下。UI 图层屏幕效果图层不受相机影响的特殊图层

这意味着放置在 UI 图层中的任何内容将始终保持可见,非常适合用于 HP 条。另一方面,屏幕效果图层专用于由动作触发的特殊效果,因此我们将在此处使用 UI 图层。


将玩家的 HP 和最大 HP 设置为 10

  1. 选择玩家的 BaseSettings 节点。

  2. 在检查器中,将 HPMax HP 的值从 1 更改为 10


添加并配置 SimpleGauge 节点

  1. 切换到 stage1 场景标签页,并将编辑器视图从 Script 改回 2D

    image

  2. 选择 UI 节点。

  3. 点击场景窗口左上角的 + (Add Child Node) 按钮。

  4. 选择 SimpleGauge 并点击 Create

  5. 调整大小。最初,它可能会显得被压扁。

  6. 拖动橙色手柄将其扩展为合适的大小(参见教程中的参考图片)。

  7. UI 图层的可见范围由细蓝线标记。将计量条移动到此边界内的 左上角

  8. 接下来,将计量条链接到玩家的 HP。在检查器中,将 Variable Type 设置为 Object

  9. 会出现一个新属性:Specify Target Object Path。点击 :page_with_curl: 图标。

  10. 选择 player.tscn 并点击 Open

  11. Variable Name 字段应自动显示 hp。这指定了用作 当前值 的变量,因此保持原样即可。

  12. 勾选 Use Variable as Max Value 复选框。

  13. 重复步骤 9–10,再次指定 player.tscn

  14. 在最大变量字段中,它可能默认为 object_id。将其更改为 max_hp。这告诉计量条使用玩家的最大 HP 作为其最大值。


测试 HP 条

点击 Test Play 按钮运行游戏。如果设置正确:

  • HP 条将显示在屏幕左上角。

  • 当玩家受到伤害时,HP 条将相应减少。


故障排除

  • 计量条不可见: 确保 SimpleGauge 是 UI 图层 的子节点,并且位于蓝色边界内。

  • HP 初始值过低: 请再次检查 BaseSettings 中玩家的 HP 是否已设置为 10

  • 即使最大 HP 存在,HP 也瞬间降至 0: 确认 BaseSettings 中的 Max HP 已设置为 10,并且 SimpleGauge 的 Max Value Variable 已正确设置为 max_hp

设置“坠落死亡”

在测试游玩时,您可能已经注意到,当玩家掉入坑中时,他们会无限坠落。让我们通过将玩家设置为在掉入坑中时“失败”来解决这个问题。

解决方案是限制摄像机的移动范围,使其不会无限向下追逐,然后当玩家离开摄像机范围时,将其设置为进入“死亡”状态。


限制 InitialCamera (ZoomCamera2D) 的移动范围

  1. 选中 InitialCamera 节点。

  2. 在检查器中,展开 Limits 部分。

  3. Bottom 限制值从 10000000 更改为 500

    • 这将确保摄像机仅在红色原点线下方移动 500 像素。
  4. 运行测试游玩。

    • 如果设置正确,摄像机仍会水平跟随玩家并向上移动,但在向下超过某一点后将停止跟随。

故障排除

  • 摄像机显示空白:
    图块可能与原点(红色和绿色轴的交点)存在偏移。尝试调整摄像机的底部限制值,例如 10002000,直到其正确对齐。

将坠落失败添加到玩家的可视化脚本

由于 “死亡” 状态已存在,我们只需在现有链接中添加一个新条件:“OffScreen”

  1. 切换到 Player 场景,并将编辑器视图更改为 Script

  2. 选中从 AnyState → Death 的链接。

  3. 点击 + Add Condition

  4. 选择条件 OffScreen

  5. Data TypeUnset 更改为 This Node

  6. Connection With Previous Condition 设置为 OR,然后添加。

  7. 确认条件列表现在正确显示:


测试坠落死亡

运行测试游玩并掉入坑中。
如果设置正确,当玩家掉出摄像机范围时,应播放烟花粒子效果,表示失败。


故障排除

  • 坠落失败未触发:
    检查 AnyState → Death 链接上的条件。确保它们显示为:HP = 0 OR OffScreen

思考“通过击败 5 名敌人来通关游戏”:变量处理

我们需要一种方法来从数字 5 开始倒计时。每次击败一名敌人,这个数字应减少,当它达到 0 时,游戏应触发通关事件。为了实现这一点,我们将使用一种称为变量的东西。


什么是变量?

变量就像一个存储值的容器。例如,我们之前处理过的玩家 HP(生命值)实际上就是一个变量。当玩家受到敌人攻击时,HP 容器内的值会减少 1。

ACTION GAME MAKER 中,定义变量主要有两种方式:

  1. 使用对象上的 VariableSettings 节点。
  2. 使用项目范围的 Project Variables 数据库。

我们之前使用的 HP 就是第一种类型的例子。


对象变量与项目变量的区别

  • 对象变量 (VariableSettings): 与特定对象绑定的变量。如果对象被移除,变量也会随之消失。

  • 项目变量: 可以在项目中任何地方访问的全局变量。只要项目正在运行,它们就保持可用,适用于保存数据等情况。

在实际应用中:

  • HP、攻击力、跳跃力 → 最好作为 对象变量 处理。

  • 最高分、金币数量、生命数 → 最好作为 项目变量 处理,因为它们会在多个关卡中持久存在,并且应在整个游戏中共享。


应该使用哪种变量来跟踪“剩余敌人”?

从技术上讲,两种类型都可以。然而,由于多个对象(敌人)将与这个数字交互,我们将使用 项目变量

因此流程将是:

  • 当敌人进入 “消失” 状态 → 将“剩余敌人”计数减 1。

  • 当“剩余敌人”达到 0 → 触发游戏通关序列。


如何实现通关序列

我们将创建一个专门的 游戏对象 来处理击杀计数。该对象将放置在 UI 层,以便始终可见。与 HP 条一样,它将显示已击败敌人的数量,让玩家知道还剩多少敌人需要清除。

步骤如下:

  1. 创建 项目变量 “剩余敌人”
  2. 敌人的“消失”状态 中添加一个减少击杀计数的动作。
  3. 在 UI 层创建一个 通关事件对象,用于监控“剩余敌人”,并在其达到 0 时触发通关序列。

添加“剩余敌人”作为项目变量

项目变量位于数据库中。现在让我们添加一个。

  1. 点击左上角菜单中的数据库按钮。

    image

  2. 将打开一个名为数据管理的新窗口。

  3. 用户数据库选项卡切换到项目变量选项卡。

  4. 点击窗口左上角的按钮。

  5. 底部将出现一个名为variables1的新行。将其重命名为剩余敌人

  6. 将其设置为5.0,因为我们希望玩家击败 5 个敌人。

  7. 点击确定关闭窗口。设置完成。

为敌人的“消失”状态添加动作

ACTION GAME MAKER 中,您可以使用 更改属性 动作来修改变量。
由于支持基本的算术运算(加、减、乘、除),我们可以将逻辑实现为:剩余敌人 -= 1

  1. 打开 enemy 场景标签页,并将编辑器视图切换为 脚本

  2. 选择 Vanish 状态,然后点击 + 添加可执行动作

  3. 选择 ChangeObjectProperty

  4. 按如下方式配置字段:

    • 目标对象类型:项目数据库

    • 数据库类型:项目变量

    • 记录名称:Remaining Enemies

    • 表达式-=

    • 常量值1

    共有 五个 字段需要设置,请仔细核对每一项。
    通过此动作,项目变量 Remaining Enemies 将减少 1。

  5. 最后,重新排列执行动作的顺序。动作是 从上到下 执行的,因此如果 Vanish Self更改属性 之前执行,变量将不会被更新。

    • 拖动 更改属性 旁边的 汉堡菜单(三条横线)图标,将其移动到 顶部,使 更改属性 首先 执行。


为什么使用 -= 而不是单独的 -

在编程中,-= 是一个简写运算符,表示:

新值 = 旧值 - 1

它在一个步骤中同时完成 减法 并将结果 赋值回变量
如果只使用 -(减号)而不加 =,则无法指定将结果存储到何处,因此变量实际上不会发生变化。

创建 UI 对象“剩余敌人管理器”

我们将创建一个 UI 对象来管理“剩余敌人”变量。

该对象将显示“剩余敌人”的当前值,并在该值达到 0 时触发清除序列。为了清晰起见,我们将通过 Sprite2D 显示敌人的图标,并使用动作在其旁边显示 剩余敌人 变量。对于敌人图标,我们可以直接复用现有的敌人精灵图像

  1. 将编辑器视图切换为 2D

  2. 打开一个新场景标签页,并将根节点选择为 GameObject

  3. 对象名称设置为 RemainingEnemiesManager,选择模板UI类型Empty,然后点击创建

  4. 保存新创建的场景。

  5. 文件系统中,将 enemy.png(敌人使用的精灵)拖入编辑器视图中原点(红绿轴交汇处)左侧的空白区域。

  6. 将自动添加一个名为 EnemySprite2D。在 Godot 中,当你直接将图像文件拖放到编辑器视口中时,它会自动创建一个 Sprite2D 节点并为你分配纹理。

配置“剩余敌人管理器”可视化脚本(显示变量)

首先,我们只关注显示变量。我们只需要一个名为**“计数”的状态,用于显示剩余敌人**变量的值。

要显示变量,请使用DisplayText动作。

创建“计数”状态

  1. 在场景窗口中,选择 RemainingEnemiesManager(游戏对象)节点,然后点击:page_with_curl:+(附加脚本)

  2. 创建 RemainingEnemiesManager.vs

  3. 将默认的 State001 重命名为 Count

  4. 添加 DisplayText 执行动作,并按如下方式配置基本设置

    • 文本类型:变量

    • 变量来源:数据管理

    • 数据库类型:项目变量

    • 记录名称:剩余敌人

  5. 按如下方式配置布局与动作部分:

    • 无限持续时间:开

    • 字体:New SystemFont

    • 字体大小:64

    • 显示大小x = 80, y = 80

    • 边距(上/左/右/下):均为 0

    • 水平对齐:居中

    • 垂直对齐:居中

注意:关于“DisplayText”动作中的放置
此动作会创建一个指定大小的文本框,以参考点为中心,然后在您选择的持续时间内在其中显示指定的文本(变量)。
当参考点为**“此对象的中心”时,意味着原点**(红色和绿色轴相交的点)。
在此设置中,您正在创建一个以原点为中心的 80×80 像素 文本框,将文本对齐到中心,并将显示持续时间设置为无限


测试“计数”显示

让我们将其放入场景并进行测试。因为我们希望它始终可见,所以我们将把它放在 UI 层 中。

  1. 切换到 stage1 场景标签,并将编辑器更改为 2D

    image

  2. 选择 UI (CanvasLayer) 下的 SimpleGauge 节点。

  3. 在文件系统中,选择 RemainingEnemiesManager.tscn,并将其拖放至 UI 层蓝色框架内右上角 区域。

  4. 运行测试播放并确认:

    • 开始时正确显示 剩余敌人 = 5

    • 击败一个敌人后,数字减少为 4


故障排除

  • 图标和数字均未显示
    确保该对象是 UI 层的子对象,并且放置在 蓝色框架内(UI 显示区域)。

  • 图标显示但数字未显示
    在剩余敌人管理器对象中,确认图标位于原点附近,并仔细检查 Count 状态中的 DisplayText 动作设置。

  • 击败敌人时数字未变化
    在敌人对象的 Vanish 状态中,验证执行顺序是否为先“更改属性”,然后**“移除自身”**。

创建清除序列

接下来,让我们构建清除序列。我们将同样使用 DisplayText 动作来实现。

我们将在屏幕中央醒目地显示 “STAGE CLEAR”,以营造正式通关的感觉。
过渡条件将是 Remaining Enemies(剩余敌人)变量达到 0 时触发。

设置“Stage Clear”状态

  1. 切换到 RemainingEnemiesManager 场景标签页,并将编辑器视图更改为 Script(脚本)。

  2. Count 状态附近,点击 Add State(添加状态)。

  3. 将新状态重命名为 Stage Clear

  4. 点击 + Add Executable Action(添加可执行动作)。

  5. 选择 DisplayText

  6. 按如下方式配置(字段较多,请仔细核对每一项):

    • Text Body(文本内容): STAGE CLEAR

    • Unlimited Duration(无限持续时间): 开启

    • Font(字体): New SystemFont

    • Font Size(字体大小): 96

    • Display Area(显示区域): x = 1200, y = 120

    • Horizontal Alignment(水平对齐): Center(居中)

    • Vertical Alignment(垂直对齐): Center(居中)

    • Reference Point(参考点): Use Scene as Base(使用场景作为基准)

    • Anchor(锚点): Center(居中)

    “Use Scene as Base”是什么意思?
    这意味着显示不是基于该对象的位置,而是锚定在整个游戏场景上——即摄像机显示的区域。在此设置中,文本框(1200×120)位于场景中央,而 STAGE CLEAR 文本则位于该文本框的中央。

  7. 右键点击 Count 状态 → Add Link(添加链接)→ 将其连接到 Stage Clear

  8. 点击 + Add Condition(添加条件)。

  9. 选择 SwitchVariableChanged

  10. 配置条件:

    • Variable Type(变量类型): Variable

    • Target Type(目标类型): Project Variable(项目变量)

    • Database Record Name(数据库记录名称): Remaining Enemies

    • Variable Condition(变量条件): =
      (这将使过渡在 Remaining Enemies == 0 时发生。)


测试清除序列

首先,我们需要足够的敌人——总共放置 五个——然后测试清除序列是否正确触发。

  1. 切换到 stage1 场景标签页,并将编辑器视图设置为 2D

  2. BaseLayer 下,选择一个子节点,例如 enemyplayer,以准备放置。

  3. FileSystem(文件系统)中,将 enemy.tscn 拖入场景以放置另一个敌人。

  4. 重复步骤 1–3,直到放置了 enemy5

  5. 开始测试运行。


故障排除

  • “我放置的五个敌人不见了”:
    它们可能掉出了屏幕范围。请确保每个敌人都放置在坚实的地面上,并且在 Template Move 中启用了 Don’t fall off ledges(不从边缘掉落)选项。

  • “计数达到 0 时没有任何反应”:
    请验证 Stage Clear 状态的 DisplayText 设置,并确认链接的过渡条件(项目变量 Remaining Enemies 等于 0)是否正确。

第4章复习

在本章中,我们学习了摄像机跟随机制粒子效果用户界面(UI)变量,使游戏能够作为一个完整的游戏运行。

然而,目前游戏的呈现仍然相当简单,且没有声音。

在下一章,即第5章中,我们将通过添加声音设置背景设置,并最终导出项目,使任何人都可以游玩,从而提升游戏的完整性。