📜  Godot中的第一场比赛

📅  最后修改于: 2021-01-02 10:11:10             🧑  作者: Mango

戈多的第一场比赛

本教程将指导我们进行第一个Godot项目。我们将学习Godot编辑器的工作原理,如何构建项目以及构建2D游戏。

该项目是Godot引擎的简介。它假定我们已经有一定的编程经验。如果我们完全不熟悉编程,则应该在这里。

游戏称为“躲开小兵!”我们的字符必须移动并尽可能长时间避开敌人。这是最终结果的预览:


为什么是2D?

3D游戏比2D游戏复杂得多。我们应该坚持2D,直到我们对游戏开发过程有了充分的了解。

项目设置

启动Godot并创建一个新项目。然后,下载dodge_assets.zip我们将用来制作游戏的图像和声音。将这些文件解压缩到我们的项目文件夹中。

该游戏将使用人像模式,因此我们需要调整游戏窗口的大小。单击项目->项目设置->显示->窗口,然后将“宽度”设置为480 ,将“高度”设置720

组织项目

在这个项目中,我们将制作三个独立的场景: Player,MobHUD,我们将它们组合到游戏的场景中。在较大的项目中,创建文件夹来保存各种视图及其脚本可能会很有用,但是对于这个相对较小的游戏,我们可以将场景和脚本保存在根文件夹中,称为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”面板:

左侧是动画列表。单击“默认”,然后将其重命名为“ right” 。然后单击“添加”按钮以创建另一个名为“向上”的动画。将每个动画的两个图像分别命名为playerGrey_up [1/2]playerGrey_walk [1/2]拖到面板的“动画帧”侧:

玩家图像对于游戏窗口而言太大了,因此我们需要按比例缩小图像。单击AnimatedSprite节点,然后将Scale属性设置为(0.5,0.5) 。我们可以在检查器中的Node2D标题下找到它。

最后,将CollisionShape2D添加为Player的子代。它将确定玩家的“ hitbox”或其碰撞区域的边界。对于此字符, capsuleShape2D节点具有最佳的拟合效果,因此在检查器中的“ Shape”旁边,单击“ ””->“ 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)并确认我们可以在屏幕上向各个方向移动播放器。

警告

如果在“调试器”面板中出现错误,该错误指向“空实例”,则可能意味着我们将节点名称拼写错误。节点名称区分大小写,并且$ NodeNameget_node(“ NodeName”)必须与我们在场景树中看到的名称匹配。

选择动画

我们需要根据方向更改AnimatedSprite正在播放哪个动画。我们有一个“ right ”动画,应使用flip_h属性将其水平翻转,以实现向左移动,而有一个“ up ”动画,应将其使用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

敌人的场景

现在该让我们的玩家必须躲避的敌人了。他们的行为会不会很复杂:小怪会在一条直线在屏幕和移动的边缘随机产卵在一个随机的方向,那么,当他们去屏幕外despawn。

我们将其构建到Mob场景中,然后实例化该场景以在游戏中创建任意数量的独立Mob。