创建第一个脚本

In this lesson, you will code your first script to make the Godot icon turn in circles using GDScript. As we mentioned in the introduction, we assume you have programming foundations. The equivalent C# code has been included in another tab for convenience.

../../_images/scripting_first_script_rotating_godot.gif

参见

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

参见

To learn more about C#, head to the C# basics page.

项目设置

请从头开始创建一个新项目。您的项目应该包含一张图片: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”工作区,并打开新建的 Sprite.gd 文件,显示以下代码行:

GDScriptC#

  1. extends Sprite
  1. public class Sprite : Godot.Sprite
  2. // Declare member variables here. Examples:
  3. // private int a = 2;
  4. // private string b = "text";
  5. // Called when the node enters the scene tree for the first time.
  6. public override void _Ready()
  7. {
  8. }
  9. // // Called every frame. 'delta' is the elapsed time since the previous frame.
  10. // public override void _Process(float delta)
  11. // {
  12. //
  13. // }

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

备注

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

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

备注

By default, the Inspector displays a node’s properties in “Title Case”, with capitalized words separated by a space. In GDScript code, these properties are in “snake_case”, which is lowercase with words separated by an underscore.

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

你好,世界!

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

往脚本中添加以下代码:

GDScriptC#

  1. func _init():
  2. print("Hello, world!")
  1. public Sprite()
  2. {
  3. GD.Print("Hello, world!");
  4. }

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

备注

GDScript is an indent-based language. The tab at the start of the line that says print() is necessary for the code to work. If you omit it or don’t indent a line correctly, the editor will highlight it in red and display the following error message: “Indented block expected”.

Save the scene if you haven’t already, then press F6 (Cmd + R on macOS) to run it. Look at the Output bottom panel that expands. It should display “Hello, world!”.

../../_images/scripting_first_script_print_hello_world.png

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

四处旋转

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

GDScriptC#

  1. var speed = 400
  2. var angular_speed = PI
  1. private int Speed = 400;
  2. private float AngularSpeed = Mathf.Pi;

Member variables sit near the top of the script, after any “extends” lines, but before functions. Every node instance with this script attached to it will have its own copy of the speed and angular_speed properties.

备注

Angles in Godot work in radians by default, but you have built-in functions and properties available if you prefer to calculate angles in degrees instead.

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

备注

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

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

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

GDScriptC#

  1. func _process(delta):
  2. rotation += angular_speed * delta
  1. public override void _Process(float delta)
  2. {
  3. Rotation += AngularSpeed * delta;
  4. }

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() 函数中添加下面两行代码,确保每一行都和前一行的缩进保持一致。

GDScriptC#

  1. var velocity = Vector2.UP.rotated(rotation) * speed
  2. position += velocity * delta
  1. var velocity = Vector2.Up.Rotated(Rotation) * Speed;
  2. 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 游戏 中,你会学到另一种移动对象的方法,可以检测碰撞。

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

完整脚本

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

GDScriptC#

  1. extends Sprite
  2. var speed = 400
  3. var angular_speed = PI
  4. func _process(delta):
  5. rotation += angular_speed * delta
  6. var velocity = Vector2.UP.rotated(rotation) * speed
  7. position += velocity * delta
  1. using Godot;
  2. public class Sprite : Godot.Sprite
  3. {
  4. private int Speed = 400;
  5. private float AngularSpeed = Mathf.Pi;
  6. public override void _Process(float delta)
  7. {
  8. Rotation += AngularSpeed * delta;
  9. var velocity = Vector2.Up.Rotated(Rotation) * Speed;
  10. Position += velocity * delta;
  11. }
  12. }