Godot Engine 4.0 Documentation - First 2D Game

This article is the result of Google Translate's English translation, and DrGraph added some corrections on this basis. English original page:

Your first 2D game — Godot Engine (stable) documentation in English

First 2D game¶

In this step-by-step tutorial series, you'll create your first full 2D game using Godot. By the end of this series, you'll have yourself a simple but complete game, as shown in the image below.

You'll learn how the Godot editor works, build projects, and how to build 2D games.

Note: This project is an introduction to the Godot engine. It assumes you already have some programming experience. If you're completely new to programming, here's where you should start: scripting languages .

The game is called "Dodge the Creeps!". Your character must move and avoid enemies for as long as possible.

You will learn:

  • Create full 2D games with the Godot editor.

  • Build a simple game project.

  • Move the player character and change its sprite.

  • Generate random enemies.

  • Calculate the score.

and more.

You'll find another series in which you'll create a similar game, but in 3D. However, we recommend that you start with this.

Why start with 2D?

If you are new to game development or new to Godot, we recommend you start with 2D games. This will allow you to feel comfortable with both before tackling the often more complex 3D games.

You can find the full version of this project here:

Prerequisites¶ _

This step-by-step tutorial is for beginners following the complete  getting started guide .

If you are an experienced programmer, you can find the source code of the full demo here: Godot Demo Project .

We've prepared some game assets that you'll need to download so we can jump straight to the code.

You can download them by clicking the links below.

dodge_the_creeps_2d_assets.zip

Setting items¶

In this short first part, we'll set up and organize the project.

Start Godot and create a new project.

When creating a new project, you just need to select a valid project path . You can keep other default settings.

GDScript

Download dodge_the_creeps_2d_assets.zip . This zipped file contains the images and sounds you will use to make your game. Extract the archive and move the art/ and fonts/directory to your project directory.

Your project folder should look like this.

This game is designed for portrait mode, so we need to resize the game window. Click "Project" -> "Project Settings" to open the project settings window, then open the "Display" -> "Window" tab in the left sidebar. There, set the Viewport Width to 480, and the Viewport Height to 720.

Also, under the Stretch option, set the Mode to Aspectcanvas_items Ratio. This ensures that the game scales consistently across different sized screens.keep

Organizing projects¶

In this project, we will make 3 separate scenes: Player, , Moband  HUD[eventually] we will combine them into the game Mainscene.

In larger projects, it might be useful to create folders to hold the various scenes and their scripts, but for this relatively small game, you can keep the scenes and scripts in the root folder of the project, by . You res://can See your project folder in the file system dock at the bottom left:

With the project in place, we can design the player scene in the next lesson.

Create player scene¶

With the project setup in place, we can start working on the player controlled character.

The first scene will define Playerthe objects. One of the benefits of creating a separate Player scene is that we can test it in isolation, even before we create the rest of the game.

Node structure¶

First, we need to choose a root node for the player object. As a general rule, the root node of the scene should reflect the desired functionality of the object - what the object is . Click the "Other Nodes" button and add an Area2D node to the scene.

Godot will display a warning icon next to the node in the scene tree. You can ignore it for now. We'll get to that later.

We Area2Dcan detect objects that overlap or hit the player. Change the node name to Player. Now that we have set up the root node of our scene, we can add additional nodes to give it more functionality.

Before we Playeradd any children to the node, we want to make sure we don't accidentally move or resize them by clicking. Select the node and click the icon to the right of the lock. Its tooltip [is] "Make the child nodes of the selected node unselectable".

Save the scene. Click Scene -> Save, or press Ctrl + S on Windows/Linux or Cmd + S on macOS

Note: For this project, we will follow the Godot naming convention.

  • GDScript : Use PascalCase for classes (nodes), snake_case for variables and functions, and ALL_CAPS for constants (see  GDScript Style Guide ).

  • C# : Use PascalCase for classes, exported variables, and methods, _camelCase for private fields, and camelCase for local variables and parameters (see C# Style Guide ). When connecting signals, be careful to type the method name exactly.

Sprite Animation¶

Click on Playerthe node and add (Ctrl + A) the child node AnimatedSprite2D . 【AnimatedSprite2D】will handle appearance and animation for our player. Note that there is a warning symbol next to the node. AnimatedSprite2D 】Requires a SpriteFrames resource, which is a list of animations it can display. To create one, find the property [ ] under the Inspector's Animationtab Sprite Framesand click [empty] -> New SpriteFrames. Click again to open the "SpriteFrames" panel [at the bottom]:

On the left is the list of animations. Click "default" and rename it "walk". Then click the "Add Animation" button to create a second animation called "Up". artFind the player images in the "FileSystem" tab - they're in the folder you unzipped earlier . Drag the two images of each animation (named  playerGrey_up[1/2]and playerGrey_walk[1/2]) to the "Animation Frames" side of the corresponding animation panel:

The player images are a bit too big for the game window, so we need to scale them down. Click on the AnimatedSprite2Dnode and Scaleset the property to (0.5, 0.5). You can Node2Dfind it in the inspector under the heading [Scale property is declared and defined in Node2D class].

Finally, add a CollisionShape2D as Playera child of . This will determine the Player's "collision box", or the boundaries of the collision area. For this character, it would be best to use a CapsuleShape2Dnode to shape the collision area, so next to Shape in the Inspector, click [empty] -> New CapsuleShape2D. Using the two size handles, resize the shape to cover the sprite:

When complete, your Playerscene should look like this:

Make sure to save the scene again after these changes.

In the next section, we'll add a script to the player node to move and animate it. Then, we set up collision detection to know when the player is about to be hit by something.

Player encoding¶

In this lesson, we will add player movement, animation code, and set up collision detection.

To do this, we need to add some functionality that is not available from the built-in node, so we will add a script. Click on the Playernode and click the "Attach Script" button:

In the script settings window, you can keep the default settings. Just click "Create":

Note: If you are creating a C# script or another language, select a language from the Language drop-down menu before clicking Create.

Note: If this is your first encounter with GDScript, please read  Scripting Languages ​​before continuing .

First declare the member variables needed by this object:

GDScript

extends Area2D

@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

speedUsing the keyword on the first variable export,其作用是allows us to set its value later in the inspector. This is handy for values ​​that you want to be able to adjust like a node's built-in properties. Click on that Player node and you'll see the property now appear in the Script Variables section of the inspector. Remember, if you change the value here, it will overwrite the value written in the script.

NOTE: If you are using C#, you will need to (re)build your project assembly whenever you want to see new exported variables or signals. This build can be manually triggered by clicking the "Build" button in the upper right corner of the editor.

Manual builds can also be triggered from the MSBuild panel. Click on the word "MSBuild" at the bottom of the editor window to display the MSBuild panel, then click the Build button.

_ready()This function is called when a node enters the scene tree , which is a good time to find [get] the size of the game window:

GDScript

func _ready():
    screen_size = get_viewport_rect().size

Now we can use this _process()function to define what the player will do. It will be called every frame _process(), so we'll use it to update elements of the game, which we expect to change frequently. For the player, we need to do the following:

  • Check the input.

  • Move in the given direction.

  • Play the appropriate animation.

First, we need to check for input - did the player press a key? For this game, we have 4 directional inputs to check. Input actions are defined in the project settings under "Input Mapping". Here you can define custom events and assign them different keys, mouse events or other inputs. For this game, we map the arrow keys to four directions.

Click Project -> Project Settings to open the Project Settings window, then click the Input Maps tab at the top. Type "move_right" in the top bar and click the Add button to add move_rightan action .

We need to assign a key for this operation. Click the "+" icon on the right to open the Event Manager window.

The "Listen for input..." field should be automatically selected. Press the "Right" key on your keyboard and the menu should now look like this.

Select the OK button. The "Right" key is now move_rightassociated with an action.

Repeat these steps to add three more maps:

  1. move_leftMapped to the left arrow key.

  2. move_upMapped to the up arrow key.

  3. move_downMapped to the down arrow key.

Your input map tab should look like this:

Click the "Close" button to close the project settings.

Note: We only map one key to each input action, but you can map multiple keys, joystick buttons, or mouse buttons to the same input action.

[DrGraph]: Godot is a bit over-designed here. I feel that it is faster to call directly with key-value constants as parameters, and the code is easier to read.

You can use Input.is_action_pressed()Detect if a key is pressed, return if it is pressed true, and return if it is not pressed false.

GDScript

func _process(delta):
    var velocity = Vector2.ZERO # The player's movement vector.
    if Input.is_action_pressed("move_right"):
        velocity.x += 1
    if Input.is_action_pressed("move_left"):
        velocity.x -= 1
    if Input.is_action_pressed("move_down"):
        velocity.y += 1
    if Input.is_action_pressed("move_up"):
        velocity.y -= 1

    if velocity.length() > 0:
        velocity = velocity.normalized() * speed
        $AnimatedSprite2D.play()
    else:
        $AnimatedSprite2D.stop()

We first set it velocityto (0, 0),indicate that by default, the player should not move. Then we check each input and add/subtract velocityfrom it to get the total direction. For example, if you hold rightand at the same time down, the resulting velocityvector will be (1, 1). In this case, since we've added horizontal and vertical movement, the player will move diagonally faster than just moving horizontally.

We can prevent this from happening if we normalizevelocity the velocity ( ) , meaning we set its length to , and then multiply by the desired velocity. This means no more fast diagonal moves.1

Note: If you have never used vector math before, or need a refresher, you can check out an explanation of vector usage in Godot at Vector math. [But don't worry, vector math knowledge] is not necessary for the rest of this tutorial.

We also check to see if the player is moving so we can call on the AnimatedSprite2D play()or stop()

注:$get_node()Shorthand for yes . So in the above code,  $AnimatedSprite2D.play()with  get_node("AnimatedSprite2D").play()【是等价的】.

In GDScript, $returns the node at the relative path to the current node, nullor if the node is not found. Since the AnimatedSprite2D is a child of the current node, we can use the  $AnimatedSprite2D.

Now that we have a direction to move, we can update the player's position. We can also use to clamp()prevent it from going off screen. Clamping a value means limiting it to a given range. Add the following to the bottom of the function _process(make sure it's not indented under the else):

GDScript:

position += velocity * delta
position.x = clamp(position.x, 0, screen_size.x)
position.y = clamp(position.y, 0, screen_size.y)

Note: The delta parameter in the _process() function refers to the frame length - the time it took the previous frame to complete . Use this value to ensure that your motion remains consistent even if the frame rate changes.

Click Play Scene (F6, Cmd+R on macOS) and confirm that you can move the player in all directions on the screen.

WARNING: If you get an error message in the Debugger panel [ this probably means that you misspelled the name of the AnimatedSprite2D node. Node names are case sensitive and must match what you see in the scene tree.Attempt to call function 'play' in base 'null instance' on a null instance]$NodeName

Select animation¶

Now that the player can move, we need to change the animation that the AnimatedSprite2D is playing according to the direction of the animation. We have a "walk" animation that shows the player walking to the right. flip_hThis animation should be flipped horizontally using the left shift property. We also have the "up" animation, which should flip vertically flip_vto move down. Let's put this code at the end of the function _process():

GDScript

if velocity.x != 0:
    $AnimatedSprite2D.animation = "walk"
    $AnimatedSprite2D.flip_v = false
    # See the note below about boolean assignment.
    $AnimatedSprite2D.flip_h = velocity.x < 0
elif velocity.y != 0:
    $AnimatedSprite2D.animation = "up"
    $AnimatedSprite2D.flip_v = velocity.y > 0

Note: The Boolean assignment in the code above is a shorthand commonly used by programmers. Since we're doing a comparison test (boolean) and also assigning a boolean, we can do both at the same time. Consider this code versus the one-line boolean assignment above:

GDScript

if velocity.x < 0:
    $AnimatedSprite2D.flip_h = true
else:
    $AnimatedSprite2D.flip_h = false

Play the scene again and check that the animation is correct in each direction.

Note: A common mistake here is typing the wrong animation name. The name of the animation in the SpriteFrames panel must match what you type in the code. If you name your animation "Walk", you must also use a capital "W" in your code.

When you're sure movement is working, add this line to  _ready(), so the player will be hidden at game start:

GDScript

hide()

Preparing for Collisions¶

We want to Playerdetect when it is hit by an enemy, but we haven't made any enemies yet! That's okay, because we're going to use Godot's signal  functionality to make it work.

Add the following at the top of the script. If you are using GDScript, please add it in the extends Area2D之后. If you are using C#, please add it topublic partial class Player : Area2D之后

GDScript

signal hit

This defines a custom signal called "hit" that we'll have our player emit (send) when it collides with an enemy. We'll use this Area2Dto detect collisions. Select Playerthe node and click the Nodes tab next to the Inspector tabs to see a list of the signals the player can emit:

Note that our custom "hit" signal is also there! Since our enemy will become RigidBody2Da node [the characteristics of each character have been systematically considered in the program design], we need to [handle body_entered(body: Node2D)the signal sent when Area2D and RigidBody2D collide]. This signal is emitted when the body touches the player. Click "Connect..", and the "Connect a Signal" window appears. We don't need to change any of these settings, so click Connect again. Godot automatically creates a function in your player script.

Note that the green icon indicates that the signal is connected to this function. Add this code to the function:

GD script C# C++

func _on_body_entered(body):
    hide() # Player disappears after being hit.
    hit.emit()
    # Must be deferred as we can't change physics properties on a physics callback.
    $CollisionShape2D.set_deferred("disabled", true)

每次敌人击中玩家时,都会发出信号。我们需要禁用玩家的碰撞,这样我们就不会hit多次触发信号。

注:如果禁用区域的碰撞形状发生在引擎的碰撞处理过程中,可能会导致错误。使用set_deferred()告诉 Godot 等待disable形状,直到这样做是安全的。

【DrGraph】:因为Godot Engine的物理引擎使用了多线程来提高效率,因此发生碰撞时(调用_on_body_entered函数),CollisionShape2D的disabled属性会被线程锁定,无法通过set_disabled来修改,这时需要使用set_deferred()来延迟调用【相当于加到队列中,可以修改的时候再处理】

最后一部分是添加一个函数,我们可以在开始新游戏时调用该函数来重置玩家。

GDScript

func start(pos):
    position = pos
    show()
    $CollisionShape2D.disabled = false

随着玩家的工作,我们将在下一课中处理敌人。

制造敌人

现在是时候让我们的玩家必须躲避敌人了。它们的行为不会很复杂:生物会在屏幕边缘随机生成,选择随机方向,并沿直线移动。

我们将创建一个Mob场景,然后我们可以实例化以在游戏中创建任意数量的独立生物。

节点设置

单击顶部菜单中的 Scene -> New Scene 并添加以下节点:

不要忘记设置子节点,使他们无法被选中,就像您在 Player 场景中所做的那样。

RigidBody2D属性中,设置Gravity Scale为0,这样生物就不会向下掉落。此外,在 CollisionObject2D部分下,取消选中属性Mask中的1。这将确保生物不会相互碰撞。

像为播放器所做的那样设置AnimatedSprite2D 。这次,我们有 3 个动画:flyswimwalk。art 文件夹中的每个动画都有两个图像。

必须为每个单独的动画设置Animation Speed属性。将所有 3 个动画的属性值均调整为。​​​​3

您可以使用输入字段Animation Speed右侧的“播放动画”按钮来预览动画。

我们将随机选择其中一种动画,这样生物就会有一些变化。

与玩家图像一样,这些生物图像也需要按比例缩小。将 AnimatedSprite2DScale属性设置为(0.75, 0.75)

类似于Player场景,MOD场景也需要为碰撞添加一个CapsuleShape2D。要将形状与图像对齐,您需要将Rotation属性设置为Degrees90(在检查器的“变换(Transform)”下)。​​​​​​​

保存场景。

敌人脚本

像这样添加一个脚本Mob

GDScript

extends RigidBody2D

现在让我们看看脚本的其余部分。在_ready()我们播放动画并随机选择三种动画类型之一:

GD脚本C#C++

func _ready():
    var mob_types = $AnimatedSprite2D.sprite_frames.get_animation_names()
    $AnimatedSprite2D.play(mob_types[randi() % mob_types.size()])

首先,我们从 AnimatedSprite2D 的属性中获取动画名称列表frames 。这将返回一个包含所有三个动画名称的数组:["walk", "swim", "fly"]

然后我们需要【在0~2之间】选择一个随机数,并从列表中选择这些名称之一(数组索引从0开始)。randi() % n选择一个介于0和n-1之间的随机整数。​​​​​​​

最后一块是让小怪在离开屏幕时删除自己。将VisibleOnScreenNotifier2D节点的screen_exited()信号连接到Mob并添加以下代码:

GDScript

func _on_visible_on_screen_notifier_2d_screen_exited():
    queue_free()

这样就完成了Mob场景。

准备好玩家和敌人后,在下一部分中,我们将把他们放在一个新场景中。我们会让敌人在游戏板周围随机生成并向前移动,将我们的项目变成一个可玩的游戏。

主要游戏场景

现在是时候将我们所做的一切整合到一个可玩的游戏场景中了。

创建一个新场景并添加一个名为Main节点【Node】。(我们使用 Node 而不是 Node2D 的原因是因为这个节点将成为处理游戏逻辑的容器。它本身不需要 2D 功能。)

单击实例按钮(由链接图标表示)并选择您保存的 player.tscn.

现在,将以下节点添加为 的子节点Main,并如图所示命名它们(值以秒为单位):

  • 计时器(​​​​​​​名称MobTimer)- 控制暴徒产生的频率

  • 计时器(名称ScoreTimer) - 每秒递增分数

  • 计时器(名称StartTimer)- 在开始前延迟

  • Marker2D (名称StartPosition) - 指示玩家的起始位置

设置每个Timer节点的Wait Time属性如下:

  • MobTimer:0.5

  • ScoreTimer:1

  • StartTimer:2

此外,将StartTimerOne Shot属性设置为“On”并将StartPosition节点的Position设置为​​​​​​​​​​​​​​​​​​​(240, 450)

生成生物

主节点将生成新的生物,我们希望它们出现在屏幕边缘的随机位置。添加一个Path2D节点,命名MobPathMain的子节点。选择 时Path2D,您会在编辑器顶部看到一些新按钮:

选择中间的一个(“添加点”)并通过单击以在显示的角处添加点来绘制路径。要使点捕捉到网格,请确保同时选择了“使用网格捕捉”和“使用智能捕捉”。这些选项可以在“锁定”按钮的左侧找到,分别显示为一些点和相交线旁边的磁铁。

重要提示:按顺时针顺序画出路径,否则你的生物生成时会指向而不是指向内

在图像中放置4点后,单击“关闭曲线”按钮,您的曲线将完成。

现在路径已定义,添加一个PathFollow2D 节点作为MobPath的子节点并将其命名为MobSpawnLocation。该节点在移动时会自动旋转并跟随路径,因此我们可以使用它来选择沿路径的随机位置和方向。

你的场景应该是这样的:

主脚本

添加一个脚本到Main. 在脚本的顶部,我们使用@export var mob_scene: PackedScene允许我们选择我们想要实例化的 Mob 场景。

GDScript

extends Node

@export var mob_scene: PackedScene
var score

单击Main节点,您将在“脚本变量”下的检查器中Main看到该Mob Scene属性。

您可以通过两种方式分配此属性的值:

  • 从“文件系统”停靠栏拖放mob.tscnMob Scene 属性中。

  • 单击“[空]”旁边的向下箭头并选择“加载”。选择mob.tscn

接下来,在场景面板中选择Main节点下的Player场景实例,并访问侧边栏上的 Node面板。确保在 Node面板中选择了 Signals 选项卡。

您应该看到Player节点的信号列表。在列表中找到并双击hit信号(或右键单击它并选择“连接...”)。这将打开信号连接对话框。我们想创建一个名为game_over的新函数,它将处理游戏结束时需要发生的事情。在信号连接对话框底部的“Receiver Method”框中键入“game_over”,然后单击“Connect”。您的目标是让Player中发出hit信号并在Main脚本中进行处理。将以下代码添加到新函数,以及一个new_game函数,用于为新游戏设置所有内容:

GDScript

func game_over():
    $ScoreTimer.stop()
    $MobTimer.stop()

func new_game():
    score = 0
    $Player.start($StartPosition.position)
    $StartTimer.start()

现在将每个定时器节点(StartTimer、 ScoreTimerMobTimer)的timeout()信号连接到主脚本。StartTimer将启动其他两个计时器。ScoreTimer将使分数增加 1。

GDScript

func _on_score_timer_timeout():
    score += 1

func _on_start_timer_timeout():
    $MobTimer.start()
    $ScoreTimer.start()

在 中_on_mob_timer_timeout(),我们将创建一个生物实例,沿Path2D随机选择一个起始位置,然后让生物开始运动。节点 PathFollow2D会随着路径自动旋转,因此我们将使用它来选择生物的方向及其位置。当我们生成一个生物时,我们将在150.0250.0之间选择一个随机值作为每个生物移动的速度(如果它们都以相同的速度移动会很无聊)。

请注意,必须使用add_child()将新实例添加到场景中。

GDScript

func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on Path2D.
    var mob_spawn_location = get_node("MobPath/MobSpawnLocation")
    mob_spawn_location.progress_ratio = randf()

    # Set the mob's direction perpendicular to the path direction.
    var direction = mob_spawn_location.rotation + PI / 2

    # Set the mob's position to a random location.
    mob.position = mob_spawn_location.position

    # Add some randomness to the direction.
    direction += randf_range(-PI / 4, PI / 4)
    mob.rotation = direction

    # Choose the velocity for the mob.
    var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
    mob.linear_velocity = velocity.rotated(direction)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

重要提示:为什么用PI?在需要角度的函数中,Godot 使用弧度而不是度数。Pi 以弧度表示半圈,约 3.1415(也有TAU等于2 * PI)。如果您更习惯使用度数,则需要使用deg_to_rad()和​​​​​​​​​​​​​​rad_to_deg()函数在两者之间进行转换。

测试场景

让我们测试场景以确保一切正常。将此new_game 调用添加到_ready()

GDScript

func _ready():
    new_game()

让我们也指定Main为我们的“主场景”——游戏启动时自动运行的场景。按“播放”按钮并main.tscn在出现提示时选择。

注:如果您已经将另一个场景设置为“主场景”,则可以右键单击main.tscn文件系统停靠栏并选择“设置为主场景”。

您应该能够移动玩家,看到怪物生成,并看到玩家在被怪物击中时消失。

当您确定一切正常时,在_ready()中删除对new_game()的调用。

我们的游戏缺少什么?一些用户界面。在下一课中,我们将添加一个标题屏幕并显示玩家的得分。

顶层屏幕

我们的游戏需要的最后一块是用户界面 (UI),用于显示得分、“游戏结束”消息和重启按钮等内容。

创建一个新场景,并添加一个名为HUD的CanvasLayer节点 。“HUD”代表“顶层屏幕”,一种信息显示叠加层,显示在游戏视图顶部。

CanvasLayer节点让我们可以游戏其余部分之上的层上绘制 UI 元素,这样它显示的信息就不会被任何游戏元素(例如玩家或生物)覆盖。

HUD需要显示以下信息:

  • 得分,由ScoreTimer.

  • 消息,例如“游戏结束”或“准备好!”

  • 一个“开始”按钮开始游戏。

UI 元素的基本节点是Control。要创建我们的 UI,我们将使用两种类型的Control节点:LabelButton

创建以下节点作为HUD节点的子节点:

单击ScoreLabel并在检查器的Text字段中键入一个数字。Control节点的默认字体很小并且不能很好地缩放。游戏资源中包含一个名为“Xolonium-Regular.ttf”的字体文件。要使用此字体,请执行以下操作:

  1. 在“主题覆盖 > 字体”下,选择“加载”并选择“Xolonium-Regular.ttf”文件。

ScoreLabel上完成此操作后,您可以单击 Font 属性旁边的向下箭头并选择“复制”,然后将其“粘贴”到其他两个 Control 节点上的相同位置。设置ScoreLabel“主题覆盖 > 字体大小”下的“字体大小”属性。设置为64的效果就很好。

注:锚点: Control节点有位置和大小,但它们也有锚点。锚点定义原点——节点边缘的参考点。

如下图所示排列节点。您可以拖动节点以手动放置它们,或者为了更精确地放置它们,请使用具有以下设置的“锚点预设”:

Score标签

布局 :

  • 锚预设:Center Top

标签设置:

  • 文本 :0

  • 水平对齐 :Center

  • 垂直对齐 :Center

信息

布局 :

  • 锚预设:Center

标签设置:

  • 文本 :Dodge the Creeps!

  • 水平对齐 :Center

  • 垂直对齐 :Center

  • 自动换行模式:Word

开始按钮

布局 :

  • 锚预设:Center Bottom

按钮设置:

  • 文本 :Start

  • Y 位置:580(控制 - 布局/转换)

MessageTimer上,将Wait Time设置为2并将One Shot属性设置为“开”。

现在将此脚本添加到HUD

GDScript

extends CanvasLayer

# Notifies `Main` node that the button has been pressed
signal start_game

我们现在想临时显示一条消息,比如“Get Ready”,所以我们添加如下代码

GDScript

func show_message(text):
    $Message.text = text
    $Message.show()
    $MessageTimer.start()

我们还需要处理当玩家输了时发生的事情。下面的代码将显示“游戏结束”2 秒,然后返回到标题屏幕,并在短暂的暂停后显示“开始”按钮。

GDScript

func show_game_over():
    show_message("Game Over")
    # Wait until the MessageTimer has counted down.
    await $MessageTimer.timeout

    $Message.text = "Dodge the\nCreeps!"
    $Message.show()
    # Make a one-shot timer and wait for it to finish.
    await get_tree().create_timer(1.0).timeout
    $StartButton.show()

当玩家输了时调用此函数。它将显示“Game Over”2 秒,然后返回到标题屏幕,并在短暂暂停后显示“Start”按钮。

注:当您需要短暂暂停时,使用 SceneTree 的create_timer()函数是使用 Timer 节点的替代方法。这对于添加延迟非常有用,例如在上面的代码中,我们希望在显示“开始”按钮之前等待一段时间。

添加以下代码以HUD更新分数

GDScript

func update_score(score):
    $ScoreLabel.text = str(score)

连接MessageTimertimeout()信号和StartButton 的pressed()信号,在新函数中添加如下代码:

GDScript

func _on_start_button_pressed():
    $StartButton.hide()
    start_game.emit()

func _on_message_timer_timeout():
    $Message.hide()

将 HUD 连接到 Main

现在我们已经完成了HUD场景的创建,回到Main. 像你做Player场景一样,在Main中实例化HUD场景。场景树应该是这样的,所以【你要检查一下】确保没有遗漏任何东西:

现在我们需要将HUD功能连接到我们的Main脚本。这需要在Main场景中添加一些内容:

在“节点”选项卡中,通过单击“连接信号”窗口中的“选择”按钮并选择new_game()方法或在窗口中的“接收器方法”下方键入“new_game”,将 HUD 的start_game信号连接到主节点的【new_game()函数】。确认【在脚本界面中】,绿色连接图标现在出现func new_game()的旁边。

请记得从_ready()中删除对new_game()的调用。

new_game()中,更新分数显示并显示“准备好”消息:

GDScript

$HUD.update_score(score)
$HUD.show_message("Get Ready")

game_over()我们需要调用相应的HUD函数:

GDScript

$HUD.show_game_over()

提醒一下:我们不想自动开始新游戏,所以如果您还没有,请删除_ready()中new_game()的调用。

最后,将此添加到_on_score_timer_timeout()以更新显示与不断变化的分数同步:

GDScript

$HUD.update_score(score)

现在你可以玩了!单击“播放项目”按钮。你会被要求选择一个主场景,可选择main.tscn

移除旧的 creeps

如果您一直玩到“Game Over”,然后立即开始新游戏,则前一游戏的小兵可能仍会出现在屏幕上。如果他们在新游戏开始时全部消失会更好。我们只需要一种方法来告诉所有的暴徒自己离开。我们可以使用“组”功能来做到这一点。

Mob场景中,选择根节点并单击检查器旁边的“节点”选项卡(与您找到节点信号的位置相同)。在“信号”旁边,单击“组”,您可以输入新的组名称【mobs】并单击“添加”。

现在所有的生物都将在“mobs组中。然后我们可以将以下行添加到Main的函数new_game()中:

GDScript

get_tree().call_group("mobs", "queue_free")

call_group()函数在组中的每个节点上调用命名函数 - 在这种情况下,我们告诉每个mob【mobs组中的所有对象】删除自己。

至此游戏基本完成。在下一部分和最后一部分中,我们将通过添加背景、循环音乐和一些键盘快捷键来对其进行一些润色。

整理

我们现在已经完成了游戏的所有功能。以下是添加更多“果汁【小细节】”以改善游戏体验的一些剩余步骤。

随意用自己的想法扩展游戏玩法。

背景

默认的灰色背景不是很吸引人,所以让我们改变它的颜色。一种方法是使用ColorRect节点。使它成为下面的第一个节点,Main这样它就会被绘制在其他节点的后面。 ColorRect只有一个属性:Color. 选择您喜欢的颜色,然后在视口顶部的工具栏或检查器中选择“布局”->“锚点预设”->“全矩形”,使其覆盖屏幕。

如果有的话,您也可以通过使用节点来添加背景图像 TextureRect

声音特效

声音和音乐可能是增加游戏体验吸引力的最有效方式。在您的游戏资产文件夹中,您有两个声音文件:背景音乐“House In a Forest Loop.ogg”和玩家失败时的“gameover.wav”。

添加两个AudioStreamPlayer节点作为Main的子节点 。【分别命名为】MusicDeathSound。【分别】单击Stream属性,选择“加载”,然后选择相应的音频文件。

所有音频的Loop属性缺省为禁用(disabled)。如果您希望音乐无缝循环,请单击“流文件”箭头,选择Make Unique,然后单击“流文件”并选中Loop复选框。

要播放音乐,在new_game() 函数中添加$Music.play(),game_over()函数中添加$Music.stop()

最后,在game_over()函数中添加$DeathSound.play()

键盘快捷键

由于游戏是用键盘控制的,如果我们也可以通过键盘上的一个键来启动游戏就方便了。我们可以使用Button节点的“快捷方式”属性来做到这一点。

在上一课中,我们创建了四个输入动作来移动角色。我们将创建一个类似的输入操作来映射到开始按钮。

选择“项目”->“项目设置”,然后单击“输入【映射Input Map】”选项卡。以与创建移动输入操作相同的方式,创建一个名为start_game的新输入操作并为该Enter 键添加一个键映射。

如果您有可用的控制器支持,现在是添加控制器支持的好时机。连接或配对您的控制器,然后在您希望为其添加控制器支持的每个输入操作下,单击“+”按钮并按下相应的按钮、方向键或您想要映射到相应输入操作的摇杆方向.

HUD场景中,选择StartButton并在 Inspector 中找到其Shortcut属性。通过在框内单击创建一个新的快捷方式资源,打开事件数组并通过单击Array[InputEvent] (size 0)添加一个新的数组元素。

创建一个新的InputEventAction并将其命名为start_game

现在,当开始按钮出现时,您可以单击它或按下Enter 以开始游戏。

这样,您就完成了您在 Godot 中的第一个 2D 游戏。

你必须制作一个玩家控制的角色、在游戏板周围随机生成的敌人、计算分数、实现游戏结束和重播、用户界面、声音等等。恭喜!

还有很多东西要学,但你可以花点时间欣赏你所取得的成就。

When you're ready, you can move on to your first 3D game and learn to create a complete 3D game from scratch in Godot.

Guess you like

Origin blog.csdn.net/drgraph/article/details/130799546
Recommended