Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

创建第一个脚本

在本课中,你将用 GDScript 编写第一个脚本,使 Godot 图标转圈。正如我们在介绍中提到的,我们假设你有编程基础。方便起见,我们在单独的选项卡中包含了等价的 C# 代码。

../../_images/scripting_first_script_rotating_godot.gif

参见

要了解更多关于GDScript、其关键字和语法的信息,请前往 GDScript 参考

参见

要了解更多关于 C# 的信息,请前往 C# 基础页面。

项目设置

请从头开始创建一个新项目。您的项目应该包含一张图片:Godot 图标,我们经常在社区中使用它来制作原型。

我们需要创建一个 Sprite2D 节点来在游戏中显示它。在“场景”停靠面板中,点击“其他节点”按钮。

../../_images/scripting_first_script_click_other_node.webp

在搜索栏中输入“Sprite2D”来过滤节点,双击 Sprite2D 来创建节点。

../../_images/scripting_first_script_add_sprite_node.webp

你的“场景”选项卡现在应该只有一个 Sprite2D 节点。

../../_images/scripting_first_script_scene_tree.webp

Sprite2D 节点需要用于显示的纹理。在右边的“检查器”中,你可以看到 Texture(纹理)属性写着“[空]”。要显示 Godot 图标,请点击瓶拖拽“文件系统”面板中的 icon.svg 文件到 Texture 插槽上。

../../_images/scripting_first_script_setting_texture.webp

备注

您可以通过将图像拖放到视口上来自动创建 Sprite2D 节点。

然后,点击并拖动视口中的图标,使其在游戏视图中居中。

../../_images/scripting_first_script_centering_sprite.webp

新建脚本

在场景面板的 Sprite2D 上点击右键并选择“添加脚本”,来创建或附加一个新的脚本到我们的节点上。

../../_images/scripting_first_script_attach_script.webp

弹出“附加节点脚本”窗口。你可以选择脚本的语言和文件路径,以及其他选项。

Change the Template field from "Node: Default" to "Object: Empty" to start with a clean file. Leave the other options set to their default values and click the Create button to create the script.

../../_images/scripting_first_script_attach_node_script.webp

此时 Script 工作区将自动打开并显示你新建的 sprite_2d.gd 文件,显示以下代码行:

extends Sprite2D

每个 GDScript 文件都是一个隐含的类。extends 关键字定义了这个脚本所继承或扩展的类。本例中它是 Sprite2D,意味着我们的脚本将获得 Sprite2D 节点的所有属性和函数,包括它继承的 Node2DCanvasItemNode 等类。

备注

在GDScript中,如果你没有写带有 extends 关键字的一行,你的类将隐式地扩展自 RefCounted,Godot使用这个类来管理你的应用程序的内存。

继承的属性包括你可以在“检查器”面板中看到的属性,例如节点的 texture

备注

“检查器”默认使用“Title Case”形式展示节点的属性,将单词的首字母大写、用空格分隔。在 GDScript 代码中,这些属性使用的是“snake_case”,全小写、单词之间使用下划线分隔。

你可以在检查器中悬停任何属性的名称来查看它的描述和在代码中的标识符。

你好,世界!

我们的脚本目前没有做任何事情。让我们开始打印文本“Hello, world!”到底部输出面板。

往脚本中添加以下代码:

func _init():
    print("Hello, world!")

让我们把它分解一下。 func 关键字定义了一个名为 _init 的新函数。这是类构造函数的一个特殊名称。如果你定义了这个函数,引擎会在内存中创建每个对象或节点时调用 _init()

备注

GDScript 是基于缩进的语言。行首的制表符是 print() 代码正常工作的必要条件。如果你省略了它或者没有正确缩进一行,编辑器将以红色高亮显示,并显示以下错误信息:“Indented block expected”(应有缩进块)。

如果你还没有保存场景为 sprite_2d.tscn,请保存,然后按 F6(macOS 上为 Cmd + R)来运行它。看一下底部展开的输出面板。它应该显示“Hello, world!”。

../../_images/scripting_first_script_print_hello_world.webp

_init() 函数删除,这样你就只有一行 extends Sprite2D 了。

四处旋转

是时候让我们的节点移动和旋转了。为此,我们将向脚本中添加两个成员变量:以像素每秒为单位的移动速度,和以弧度每秒为单位的角速度。将下述内容添加到 extends Sprite2D 行的后面。

var speed = 400
var angular_speed = PI

成员变量位于脚本的顶部,在“extends”之后、函数之前。附加了此脚本的每个节点实例都将具有自己的 speedangular_speed 属性副本。

备注

与其他一些引擎一样,Godot 中的角度默认使用弧度为单位,但如果您更喜欢以度为单位计算角度,则可以使用内置函数和属性。

为了移动我们的图标,我们需要在游戏循环中每一帧更新其位置和旋转。我们可以使用 Node 类中的虚函数 _process() 。如果你在任何扩展自 Node 类的类中定义它,如 Sprite2D,Godot将在每一帧调用该函数,并传递给它一个名为 delta 的参数,即从上一帧开始经过的时间。

备注

游戏的工作方式是每秒钟渲染许多图像,每幅图像称为一帧,而且是循环进行的。我们用每秒帧数(FPS)来衡量一个游戏产生图像的速度。大多数游戏的目标是60FPS,尽管你可能会发现在较慢的移动设备上的数字是30FPS,或者是虚拟现实游戏的90至240。

引擎和游戏开发者尽最大努力以恒定的时间间隔更新游戏世界和渲染图像,但在帧的渲染时间上总是存在着微小的变化。这就是为什么引擎为我们提供了这个delta时间值,使我们的运动与我们的帧速率无关。

在脚本的底部,定义该函数:

func _process(delta):
    rotation += angular_speed * delta

func 关键字定义了一个新函数。在它之后,我们必须在括号里写上函数的名称和它所接受的参数。冒号结束定义,后面的缩进块是函数的内容或指令。

备注

请注意 _process()_init() 一样都是以下划线开头的。按照约定,这是 Godot 的虚函数,也就是你可以覆盖的与引擎通信的内置函数。

函数内部的那一行 rotation += angular_speed * delta 每一帧都会增加我们的精灵的旋转量。这里 rotation 是从 Sprite2D 所扩展的 Node2D 类继承的属性。它可以控制我们节点的旋转,以弧度为单位。

小技巧

在代码编辑器中,你可以按住 ctrl 单击任何内置的属性或函数,如 positionrotation_process 以在新标签页中打开相应的文档。

运行该场景,可以看到 Godot 的图标在原地转动。

../../_images/scripting_first_script_godot_turning_in_place.gif

备注

在 C# 中,请注意 _Process() 所采用的 delta 参数类型是 double 。 故当我们将其应用于旋转时,需要将其转换为 float

前进

现在我们来让节点移动。在 _process() 函数中添加下面两行代码,确保每一行都和之前的 rotation += angular_speed * delta 行的缩进保持一致。

var velocity = Vector2.UP.rotated(rotation) * speed

position += velocity * delta

正如我们所看到的,var 关键字可以定义新变量。如果你把它放在脚本顶部,定义的就是类的属性。在函数内部,定义的则是局部变量:只在函数的作用域中存在。

We define a local variable named velocity, a 2D vector representing both a direction and a speed. To make the node move forward, we start from the Vector2 class's constant Vector2.UP, a vector pointing up, and rotate it by calling the Vector2 method rotated(). This expression, Vector2.UP.rotated(rotation), is a vector pointing forward relative to our icon. Multiplied by our speed property, it gives us a velocity we can use to move the node forward.

我们在节点的 position 里加上 velocity * delta 来实现移动。位置本身是 Vector2 类型的,是 Godot 用于表示 2D 向量的内置类型。

运行场景就可以看到 Godot 头像在绕圈圈。

../../_images/scripting_first_script_rotating_godot.gif

备注

使用这样的方法不会考虑与墙壁和地面的碰撞。在 你的第一个 2D 游戏 中,你会学到另一种移动对象的方法,可以检测碰撞。

Our node currently moves by itself. In the next part, 监听玩家的输入, we'll use player input to control it.

完整脚本

这是完整的 sprite_2d.gd 文件,仅供参考。