创建第一个脚本
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.
参见
要了解更多关于GDScript、其关键字和语法的信息,请前往 GDScript 参考。
参见
To learn more about C#, head to the C# basics page.
项目设置
请从头开始创建一个新项目。您的项目应该包含一张图片:Godot 图标,我们经常在社区中使用它来制作原型。
我们需要创建一个 Sprite 节点来在游戏中显示它。在“场景”面板中,点击“其他节点”按钮。
在搜索栏中输入“Sprite”来过滤节点,双击 Sprite 来创建节点。
你的“场景”选项卡现在应该只有一个 Sprite 节点。
Sprite 节点需要用于显示的纹理。在右边的“检查器”中,你可以看到 Texture(纹理)属性写着“[空]”。要显示 Godot 图标,请点击“文件系统”面板中的 icon.png
文件并将其拖到 Texture 插槽上。
备注
您可以通过将图像拖放到视区上来自动创建 Sprite 节点。
然后,点击并拖动视区中的图标,使其在游戏视图中居中。
新建脚本
在场景面板的 Sprite 上点击右键并选择“附加脚本”,来将一个新的脚本附加到我们的节点上。
弹出“附加节点脚本”窗口。你可以选择脚本的语言和文件路径,以及其他选项。
把模板从默认改为空Empty,得到一个干净的脚本。其他选项保持默认,然后点击创建按钮来生成脚本。
此时将显示“Script”工作区,并打开新建的 Sprite.gd
文件,显示以下代码行:
GDScriptC#
extends Sprite
public class Sprite : Godot.Sprite
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
每个 GDScript 文件都是一个隐含的类。extends
关键字定义了这个脚本所继承或扩展的类。这里它是 Sprite
,意味着我们的脚本将获得 Sprite 节点的所有属性和函数,包括它继承的 Node2D
、CanvasItem
、Node
等类。
备注
在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#
func _init():
print("Hello, world!")
public Sprite()
{
GD.Print("Hello, world!");
}
让我们把它分解一下。 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!”.
将 _init()
函数删除,这样你就只有一行 extends Sprite
了。
四处旋转
是时候让我们的节点移动和旋转了。为此,我们将向脚本中添加两个成员变量:以像素每秒为单位的移动速度和以弧度每秒为单位的角速度。
GDScriptC#
var speed = 400
var angular_speed = PI
private int Speed = 400;
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#
func _process(delta):
rotation += angular_speed * delta
public override void _Process(float delta)
{
Rotation += AngularSpeed * delta;
}
func
关键字定义了一个新函数。在它之后,我们必须在括号里写上函数的名称和它所接受的参数。冒号结束定义,后面的缩进块是函数的内容或指令。
备注
请注意 _process()
和 _init()
一样都是以下划线开头的。按照约定,这是 Godot 的虚函数,也就是你可以覆盖的与引擎通信的内置函数。
函数内部的那一行 rotation += angular_speed * delta
每一帧都会增加我们的精灵的旋转量。这里 rotation
是从 Sprite
所扩展的 Node2D
类继承的属性。它可以控制我们节点的旋转,以弧度为单位。
小技巧
在代码编辑器中,你可以按住 ctrl 单击任何内置的属性或函数,如 position
、rotation
、_process
以在新标签页中打开相应的文档。
运行该场景,可以看到 Godot 的图标在原地转动。
前进
现在我们来让节点移动。在 _process()
函数中添加下面两行代码,确保每一行都和前一行的缩进保持一致。
GDScriptC#
var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta
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 头像在绕圈圈。
备注
使用这样的方法不会考虑与墙壁和地面的碰撞。在 您的第一个 2D 游戏 中,你会学到另一种移动对象的方法,可以检测碰撞。
我们的节点目前是自行移动的。在下一部分 监听玩家的输入 中,我们会让玩家的输入来控制它。
完整脚本
这是完整的 Sprite.gd
文件,仅供参考。
GDScriptC#
extends Sprite
var speed = 400
var angular_speed = PI
func _process(delta):
rotation += angular_speed * delta
var velocity = Vector2.UP.rotated(rotation) * speed
position += velocity * delta
using Godot;
public class Sprite : Godot.Sprite
{
private int Speed = 400;
private float AngularSpeed = Mathf.Pi;
public override void _Process(float delta)
{
Rotation += AngularSpeed * delta;
var velocity = Vector2.Up.Rotated(Rotation) * Speed;
Position += velocity * delta;
}
}