本文概述
本教程将指导我们进行第一个Godot项目。我们将学习Godot编辑器的工作原理, 如何构建项目以及构建2D游戏。
该项目是Godot引擎的简介。它假定我们已经有一定的编程经验。如果我们完全不熟悉编程, 则应该在这里。
游戏称为”躲开小兵!”我们的角色必须移动并尽可能长时间避开敌人。这是最终结果的预览:
为什么是2D?
3D游戏比2D游戏复杂得多。我们应该坚持2D, 直到我们对游戏开发过程有了充分的了解。
项目设置
启动Godot并创建一个新项目。然后, 下载dodge_assets.zip我们将用来制作游戏的图像和声音。将这些文件解压缩到我们的项目文件夹中。
该游戏将使用人像模式, 因此我们需要调整游戏窗口的大小。单击项目->项目设置->显示->窗口, 然后将”宽度”设置为480, 将”高度”设置为720。
组织项目
在这个项目中, 我们将制作三个独立的场景:Player, Mob和HUD, 我们将它们合并到游戏的主场景中。在较大的项目中, 创建文件夹来容纳各种视图及其脚本可能会很有用, 但是对于这个相对较小的游戏, 我们可以将场景和脚本保存在根文件夹中, 称为res://。我们可以在左上角的FileSystem Dock中看到项目文件夹:
玩家场景
我们将创建的第一个场景定义Player对象。创建单独的Player场景的好处之一是, 即使在创建游戏的其他部分之前, 我们也可以对其进行单独测试。
节点结构
首先, 单击”添加/创建新节点”按钮, 然后将Area2D节点添加到场景中
使用Area2D, 我们可以检测重叠或进入播放器的对象。通过单击节点名称将其名称更改为Player。这是场景的根节点。我们可以向播放器添加其他节点以添加功能。
在将任何子代添加到”播放器”节点之前, 我们要确保我们不会意外地通过单击它们来移动或调整它们的大小。选择节点, 然后单击锁右侧的图标;它的工具提示中说:”确保对象的子项不可选择。”
保存场景。单击场景->保存, 或者在Windows / Linux上按Ctrl + S, 在Mac上按Command + S。
注意对于此项目, 我们将遵循Godot命名约定。类(节点)使用ParcalCase, 变量和函数使用snake_case, 常量使用ALL_CAP。
雪碧动画
单击Player节点, 并将AnimatedSprite节点添加为子节点。 AnimatedSprite将为我们的播放器处理外观和动画。请注意, 节点旁边有一个警告符号。 AnimatedSprite需要SpriteFrames资源, 该资源是它可以显示的动画的列表。要创建一个, 请在检查器中找到Frames属性, 然后单击” <null>”->” NewSpriteFrames”。接下来, 在相同位置, 单击” <null>”->” NewSpriteFrames”。接下来, 在相同位置, 单击<SpriteFrames>以打开” SpriteFrames”面板:
左侧是动画列表。单击”默认”, 然后将其重命名为”正确”。然后单击”添加”按钮以创建另一个名为”向上”的动画。将每个动画的两个图像分别命名为playerGrey_up [1/2]和playerGrey_walk [1/2]拖到面板的”动画帧”侧:
玩家图像对于游戏窗口而言太大了, 因此我们需要按比例缩小图像。单击AnimatedSprite节点, 然后将Scale属性设置为(0.5, 0.5)。我们可以在检查器中的Node2D标题下找到它。
最后, 添加CollisionShape2D作为Player的子代。它将确定玩家的” hitbox”或其碰撞区域的边界。对于此角色, capsuleShape2D节点具有最佳的拟合效果, 因此在检查器中的” Shape”旁边, 单击” <null>””->” New CapsuleShape2D”。调整形状大小以覆盖精灵:
不要缩放形状的轮廓!仅使用尺寸手柄(红色圆圈)来调整形状!
完成后, 我们的Player场景应如下所示:
移动播放器
现在, 我们需要添加一些无法从内置节点获得的功能, 因此我们将添加一个脚本。单击播放器节点, 然后单击”添加脚本”按钮;
在脚本设置窗口中, 我们可以保留默认设置。只需单击”创建”:
注意:如果我们要创建C#脚本或其他语言, 请在点击创建之前, 从语言下拉菜单中选择语言。
如果这是我们第一次遇到GDScript, 请先阅读脚本, 然后再继续。
首先声明该对象将需要的成员变量:
GDScript
extends Area2D
Export (int) var speed # How fast the player will move (pixels/sec).
Var screensize # Size of the game window.
在第一个变速上使用export关键字, 使我们可以在检查器中设置其值。这对于我们希望能够调整的值非常方便, 就像节点的内置属性一样。单击”播放器”节点, 并将speed属性设置为400。
警告
如果使用的是C#, 则需要暂时重新启动Godot编辑器, 以在编辑器中查看导出的变量, 直到修复为止。
节点进入场景树时, 将调用_ready()函数, 这是查找游戏窗口大小的好时机:
GDScript
func _ready():
screensize=get_viewport_rect().size
现在, 我们可以使用_process()函数定义播放器将执行的操作。 _process()在每一帧都被调用, 因此我们将使用它来更新游戏元素, 我们希望它会经常更改。在这里, 我们将做到:
- 检查输入
- 沿给定方向移动。
- 播放适当的动画。
首先, 我们必须检查输入-玩家是否按下了键?对于游戏, 我们有四个输入方向要检查。输入动作在项目设置中的”输入映射”下定义。我们可以描述自定义事件, 并为其分配不同的键, 鼠标事件或其他输入。在此演示中, 我们将使用键盘上的箭头键附带的默认事件。
我们可以使用Input.is_action_pressed()来检测是否按下了某个键, 如果按下该键, 则返回true;否则, 返回false。
GDScript
func _process(delta):
var velocity = Vector2() # The player's movement vector.
if Input.is_action_pressed("ui_right"):
velocity.x += 1
if Input.is_action_pressed("ui_left"):
velocity.x -= 1
if Input.is_action_pressed("ui_down"):
velocity.y += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite.play()
else:
$AnimatedSprite.stop()
我们检查每个输入并从速度中加/减以获得总方向。例如, 如果我们同时按住向右和向下, 则所得的速度矢量将为(1, 1)。在这种情况下, 由于我们要添加水平和垂直移动, 因此播放器的移动速度要比仅水平移动的速度快。
如果我们对速度进行归一化, 就可以避免, 这意味着我们将其长度设置为1, 然后乘以所需的速度。这意味着没有更快的对角运动。
小费
如果我们以前从未使用过矢量数学, 或者需要复习, 则可以在Godot上的矢量数学中看到有关矢量用法的解释。知道这很高兴, 但是对于本教程的其余部分而言, 则不是必需的。
我们还检查播放器是否在移动, 以便我们可以启动或停止AnimatedSprite动画。
$返回该节点相对路径上的节点, 如果找不到该节点, 则返回null。由于AnimatedSprite是当前节点的子代, 因此我们可以使用$ AnimatedSprite。
$是get_node()的简写。因此, 在上面的代码中, $ AnimatedSprite.play()与get_node(” AnimatedSprite”)。play()相同。
现在我们有了一个运动方向, 我们可以通过将以下内容添加到_process函数的底部来更新Player的位置, 并使用clip()防止其离开屏幕:
GDScript
position += velocity * delta
position.x = clamp(position.x, 0, screensize.x)
position.y = clamp(position.y, 0, screensize.y)
小费
钳位值意味着将其限制在给定范围内。
单击” PlayScene”(F6)并确认我们可以在屏幕的各个方向上移动播放器。
警告
如果我们在”调试器”面板中看到一个指向”空实例”的错误, 则可能意味着我们将节点名称拼写错误。节点名称区分大小写, 并且$ NodeName或get_node(” NodeName”)必须与我们在场景树中看到的名称匹配。
选择动画
我们需要根据方向更改AnimatedSprite正在播放哪个动画。我们有一个”右”动画, 应使用flip_h属性将其水平翻转左移, 而有一个”上”动画, 应将其使用flip_v垂直翻转以向下移。让我们将此代码放在_process()函数的末尾:
GDScript
if velocity.x != 0:
$AnimatedSprite.animation = "right"
$AnimatedSprite.flip_v = false
$AnimatedSprite.flip_h = velocity.x < 0
elif velocity.y != 0:
$AnimatedSprite.animation = "up"
$AnimatedSprite.flip_v = velocity.y > 0
再次播放场景, 并检查各个方向的动画是否正确。当我们确定移动正常进行时, 请将此行添加到_ready(), 这样在游戏开始时玩家将被隐藏:
GDScript
hide()
准备碰撞
我们希望Player能够检测到敌人何时击中它, 但是我们还没有发现任何敌人!可以, 因为我们将使用Godot的信号功能使其正常工作。
扩展Area2D之后, 在脚本顶部添加以下内容:
GDScript
signal hit
这定义了一个称为”命中”的自定义信号, 当玩家与敌人碰撞时, 我们将使其发出(发送)。我们将使用Area2D来检测碰撞。选择”播放器”节点, 然后单击”检查器”选项卡旁边的”节点”选项卡, 以查看播放器可以发出的信号列表:
注意我们的自定义” hit”信号也在那里!由于我们的敌人将是RigidBody2D节点, 因此我们需要body_entered(Object body)信号;当身体与玩家接触时会发出此信号。点击”连接”。然后在”连接信号”窗口中再次”连接”。我们不需要更改任何设置-Godot将自动在播放器的脚本中创建一个名为_on_Player_body_entered的函数。
连接信号时, 除了让Godot为我们创建函数外, 我们还可以提供要将信号链接到的现有函数的名称。
将此代码添加到函数中:
GDScript
func _on_Player_body_entered(body):
hide() # Player disappears after being hit.
emit_signal("hit")
$CollisionShape2D.disabled = true
注意禁用区域的碰撞形状意味着它不会检测到碰撞。通过关闭它, 我们确保不会多次触发命中信号。
我们的玩家的最后一步是添加一个函数, 我们可以在启动新游戏时调用该函数来重置玩家。
GDScript
func start(pos):
position=pos
show()
$CollisionShape2D.disabled=false
敌人的场景
现在该让我们的玩家必须躲避的敌人了。它们的行为不会很复杂:小生物将在屏幕边缘随机生成, 并沿随机方向沿直线移动, 然后在离开屏幕时生成。
我们将其构建到Mob场景中, 然后实例化该场景以在游戏中创建任意数量的独立Mob。
评论前必须登录!
注册