让我们通过制作 RPG 来学习 Godot 4 — 第 2 部分:玩家设置和移动
现在我们已经完成了项目设置,现在可以继续创建玩家场景了。我们的玩家场景将包含节点和脚本,使我们能够看到角色并控制他们,以便我们可以四处奔跑并与世界互动。
您将在本部分中学到什么:
- 如何创建、运行和实例化新场景。
- 如何向节点添加输入动作、碰撞和动画。
- 如何向场景添加脚本和代码。
- 如何向节点添加运动。
玩家设置
在我们的主场景中,我们可以继续删除之前添加的 Sprite2D 节点。我们将为玩家创建一个全新的场景,然后在主场景中实例化它。实例化允许我们从模板复制对象,以便我们可以单独修改它们。换句话说,我们将把 Player 与主场景分开,这样我们就可以将它视为一个单独的对象。一分钟后您就会更清楚地看到它是如何工作的。
在工作区中,单击 Main(*) 旁边的加号图标以创建新场景。在场景 Dock 中,添加一个新的根节点。我们需要这个根节点是一个CharacterBody2D节点,它是一个专门的2D物理身体节点,用于可以通过脚本移动的角色。我们将此节点用于需要物理处理进行运动的实体。
双击CharacterBody2D节点将其重命名为“Player”。我们还可以继续将新场景保存为场景文件夹中的“Player”。
您会注意到根节点旁边有一个警告图标。如果将鼠标悬停在其上,您将看到警告消息。它告诉我们,我们的角色没有形状,因此无法与其他角色发生碰撞或互动。为了解决这个问题,我们需要添加一个CollisionShape2D节点。该节点将允许我们的玩家与其他碰撞体发生碰撞,这将阻止、损坏或触发我们玩家中的事件。
该节点还将返回一条警告消息,因为我们尚未在检查器面板中为其分配形状。
让我们通过单击“形状”旁边的<空>框并选择一个形状来解决此问题。您为对象选择的形状类型取决于您希望它们完成的任务(从它们需要碰撞的位置)以及它们的身体形状。因此,圆形角色可能会发生圆形碰撞,等等。对于我们的精灵,我们选择 CapsuleShape2D 形状。您现在将看到我们有一个碰撞形状,因此我们可以与游戏中的其他对象碰撞,但我们甚至还没有身体!
要查看我们的角色,我们需要将其分配给 Sprite。现在,我们可以通过两种方式实现这一点:通过Sprite2D节点和AnimatedSprite2D节点。如果我们想要一个静态对象,即不移动或没有动画的对象,Sprite2D 节点是完美的。然而,AnimatedSprite2D 对象则恰恰相反。当我们有一个需要精灵表动画的对象时,它是完美的。
选择 Player 的根节点(确保选择它而不是 CollisionShape2D 节点,否则它会将我们的节点添加到其中作为其子节点),添加一个新的 AnimatedSprite2D 节点。
如果您选择了错误的节点,请右键单击要替换的节点并说出更改类型。
该节点将再次返回警告,这是因为它没有分配给它的精灵表,因此它无法返回动画帧。让我们通过进入 Inspector 面板并在 Sprite Frames 旁边的
这个 SpriteFrames 资源允许我们导入图像文件(或包含所述文件的文件夹)来为精灵提供动画帧。要配置我们的 SpriteFrames 资源,请单击它,您将在下面的面板中看到一个新选项弹出。
在此面板中我们可以添加新动画、删除动画、播放动画或编辑动画。可以删除或重命名默认动画。我们将在下一部分中添加所有玩家的动画,以便我们可以更详细地探索该节点。现在,我们只需单击“从 Sprite Sheet 添加帧”选项即可为默认动画分配一个 Sprite。
将弹出一个窗口,以便我们可以选择要添加的 Sprite Sheet,因此请进入“Assets”>“Player”,然后选择“Front Sheet”。
将会弹出另一个窗口,在这里我们需要裁剪单独的精灵来制作动画。默认的水平和垂直值为 4。在水平方向上,我们可以算出我们的玩家帧数最多为 14,因此将水平值更改为 14。
在垂直方向上,我们可以算出我们的玩家帧数达到了 5,因此将垂直值更改为 5。
现在我们可以(按顺序)选择我们想要的帧来创建一个动画。该表有五种可能的动画:闲置、行走、攻击、伤害和死亡。现在,我们只选择第二行来创建基本的行走动画。确保按顺序选择它,否则,您将不得不在 SpriteFrames 面板中重新排列它。
我们的默认动画现在添加了 8 帧。如果您按播放,我们可以在工作区中看到动画播放。我们的玩家走得有点慢,所以让我们将 FPS 值从 5 提高到 10。保留循环值处于启用状态。
运动输入
添加步行动画后,我们现在可以添加输入和功能来移动它们。在我们开始编码之前,我们需要添加一个输入动作。这是物理控件,例如我们的键盘按键、操纵杆、按钮等,我们将它们映射到我们的功能以在游戏中移动我们的角色。
要添加输入操作,我们转到“项目设置”>“输入映射”。
Godot 4 带有预先存在的输入操作。要查看它们,请启用“显示内置操作”开关。 ui_代表“用户输入”,您不必为将来的输入操作遵守此命名约定,但对于这个项目,我们只是保持代码一致。
您会看到 Godot 已经预先配置了输入以将角色向左、向右、向上和向下移动。他们将键盘按键“向上”、“向下”、“向左”和“向下”分配给这些操作。您可以通过按它们旁边的铅笔图标来编辑它们,甚至可以删除它们。您还可以通过单击旁边的加号图标为此输入分配其他键。
让我们将 WASD 键分配给 ui_up(W 键)、ui_down(S 键)、ui_left(A 键)和 ui_right(D 键)。
稍后我们将回来添加我们自己的用于攻击、消耗、交互和冲刺的输入动作。目前,我们无需在此处执行任何操作,因为大多数操作已为我们添加,因此您可以完全关闭此菜单。
现在让我们向播放器添加一个脚本,以便我们可以使用这些输入移动它们。您可以将脚本分配给任何节点,但请记住 - 该脚本只会影响该节点,并且只有该节点的类对象是可调用的。但是,根节点除外。如果将脚本添加到根节点,它将能够影响并调用其所有子节点的类。
要将脚本分配给节点,请选择要添加功能的节点(在本例中是我们的根节点),然后在场景停靠栏中单击它旁边的小滚动图标。将此脚本另存为 Player.gd 在 Scripts 文件夹下。
一旦节点附加了脚本,您将看到滚动图像出现在它旁边。如果右键单击节点,则可以分离此脚本并附加一个新脚本。单击滚动条打开脚本,这将打开脚本工作区。
在顶部,它说“扩展CharacterBody2D”,因为它是从基类CharacterBody2D扩展的。这意味着 CharacterBody2D 的所有属性和子函数都可以从此脚本中调用。
现在为了让我们的角色移动,我们需要首先定义一个移动速度变量。
### Player.gd
extends CharacterBody2D # 玩家移动
速度
@export var speed = 50
@export意味着我们可以导出变量以在检查器面板中进行编辑。因此,选择节点后,您现在可以更改属性中的速度值,而无需为其分配常量值。当我们创建敌人之类的东西并希望在场景中多次实例化它们并希望它们具有不同的移动速度时,这会很有帮助。
接下来,我们将使用_physical_process(delta)函数,该函数不断处理角色的运动物理。在这个函数中,我们需要获取玩家输入的方向(左、右、上、下)。该输入需要标准化以获得更平滑的对角线移动,因为我们的玩家在笛卡尔平面水平上移动。这意味着它们需要向上、向下、向左或向右移动 1 个空间,并且归一化会将向量在任何方向上设置为相同的长度。
何时使用 _processing() 和 _physical_processing()?
对于图形化或需要快速响应的事物,请使用 _process() 函数。对于基于物理或需要以一致、可预测的速率发生的事情,请使用
_physical_process() 函数。
什么是向量?
矢量是表示力、速度和位置等量的对象。在 2D 游戏中,我们使用向量来确定和计算实体在 X 轴和 Y 轴上的位置。
图 5:矢量运动在平面尺度上的样子。
然后,我们将使用 Godot 中的内置move_and_collide() 方法来移动并启用物理系统来移动我们的玩家,同时还强制碰撞,以便它们在弹到其他对象时停止。
何时使用 move_and_slide 和 move_and_collide()?
当您希望角色沿一般方向移动时(例如在平台游戏中),请使用 move_and_slide()
。当您需要有关角色碰撞的详细信息以执行自定义操作时(例如在益智游戏中角色需要移动或躲避障碍物),请使用
move_and_collide()。
### Player.gd
extends CharacterBody2D
# Player movement speed
@export var speed = 50
func _physics_process(delta):
# Get player input (left, right, up/down)
var direction: Vector2
direction.x = Input.get_action_strength("ui_right") -Input.get_action_strength("ui_left")
direction.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
# If input is digital, normalize it for diagonal movement
if abs(direction.x) == 1 and abs(direction.y) == 1:
direction = direction.normalized()
# Apply movement
var movement = speed * direction * delta
# Moves our player around, whilst enforcing collisions so that they come to a stop when colliding with another object.
move_and_collide(movement)
保存您的脚本,让我们回到主场景。单击加号图标旁边的链图标,这将帮助我们将玩家场景实例(添加)到模式场景中。选择播放器场景。
按 F 键将焦点放在主场景中新实例化的 Player 场景上,您将看到它已被添加,但我们看不到它的子节点(碰撞和精灵节点)。这是因为它现在被视为一个完整的对象,而不是单个节点。
现在缩小,直到您在主场景中看到蓝色边框。这个蓝色窗口是可见游戏屏幕或视口的边框,简单来说,就是运行游戏时看到的屏幕尺寸。
我们的玩家将在左上角生成,因此我们无法正确看到他们。为了防止这种情况,我们选择它们并将它们拖动到靠近中间或内边缘的空间。确保为此启用了选择工具,即光标。
如果我们现在运行游戏,玩家将会非常小。这是因为我们的游戏窗口被缩小到离玩家所在的位置很远。这是因为我们的视口尺寸太大,我们的拉伸模式需要改变。
进入项目设置 > 显示 > 窗口,然后进行以下更改:
我们的角色现在将被放大,我们可以通过单击播放按钮或按 F5 来运行我们的游戏。
当我们输入 WASD 或方向键时,玩家正在地图上移动,但没有任何动画!这是因为我们还没有为我们的输入分配任何动画,但是当我们将动画作为一个独立的主题来关注时,我们将在下一部分中这样做。
设置好玩家角色后,我们就可以继续制作动画了。您可能想知道世界地图以及我们角色的相机设置,但不用担心,我们会在适当的时候完成所有内容。请记住保存您的游戏项目并进行备份,我们将在下一部分中见到您。