创建第一个脚本

在本课中,你将用 GDScript 编写第一个脚本,使 Godot 图标转圈。正如我们在介绍中提到的,我们假设你有编程基础。

../../_images/scripting_first_script_rotating_godot.gif

参见

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

项目设置

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

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

../../_images/scripting_first_script_click_other_node.png

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

../../_images/scripting_first_script_add_sprite_node.png

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

../../_images/scripting_first_script_scene_tree.png

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

../../_images/scripting_first_script_setting_texture.png

备注

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

../../_images/scripting_first_script_dragging_sprite.png

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

../../_images/scripting_first_script_centering_sprite.png

新建脚本

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

../../_images/scripting_first_script_attach_script.png

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

把模板从默认改为空Empty,得到一个干净的脚本。其他选项保持默认,然后点击创建按钮来生成脚本。

../../_images/scripting_first_script_attach_node_script.png

此时将显示“Script”工作区,并打开新文件并显示以下代码行:

extends Sprite

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

备注

在GDScript中,如果你省略了带有 extends 关键字的一行,你的类将隐式扩展 Reference,Godot使用它来管理你的应用程序的内存。

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

备注

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

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

你好,世界!

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

往脚本中添加以下代码:

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

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

备注

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

如果你还没有保存场景,请保存,然后按 F6 来运行它。看一下底部展开的“输出”面板。它应该显示“Hello, world!”

../../_images/scripting_first_script_print_hello_world.png

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

四处旋转

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

extends Sprite

var speed = 400
var angular_speed = PI

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

备注

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

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

备注

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

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

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

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

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

备注

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

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

小技巧

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

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

../../_images/scripting_first_script_godot_turning_in_place.gif

前进

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

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

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

我们定义一个叫 velocity 的局部变量,它是 2D 向量,表示方向和速度。要让节点向前移动,我们可以从 Vector2 类的常量 Vector2.UP 开始,这是个朝上的向量,调用它的 Vector2.rotated() 方法去进行旋转。表达式 Vector2.UP.rotated(rotation) 是指向我们的图标上方的向量。与我们的 speed 属性相乘后,得到的就是可以用来将节点向前移动的速度。

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

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

../../_images/scripting_first_script_rotating_godot.gif

备注

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

我们的节点目前是自行移动的。在下一部分中,我们会让玩家的输入来控制它。