Appearance
GDScript
GDScript是godot引擎官方推出的语言,旨在能够与引擎更加契合。
在引擎中创建脚本
点击某个节点后,点击下图所示的按钮,即可创建脚本。


或者直接在资源浏览器中创建

变量(标识符)
推荐以字母、数字、下划线的组合字符串作为变量名。
不能以数字开头,且大小写敏感。
尽管可以用一些Unicode字符,但是不建议。
关键字
那些不能被作为标识符的被称为关键字。下面是其中的一些:
| if | elif | else | for | while |
| match | break | continue | pass | return |
| class | class_name | extends | is | in |
| as | self | signal | func | static |
| const | enum | var | breakpoint | preload |
| await | yield | assert | void | PI |
| TAU | INF | NAN |
模板输出
模板输出是指在字符串中插入变量的值。例如:
gdscript
var name = "张三"
var age = 18
print("%s今年%d岁" % [name, age])数据类型
在 GDScript 中,数据类型可以归纳为以下几类:
一、基础数据类型(值类型)
| 类型 | 说明 | 示例 |
|---|---|---|
| null | 空值,表示没有值 | var a = null |
| bool | 布尔值,true 或 false | var flag = true |
| int | 整数(有符号 64 位) | var score = 100 |
| float | 浮点数(小数) | var speed = 3.5 |
| String | 字符串,Unicode 文本 | var name = "Player" |
二、向量与数学类型
| 类型 | 说明 |
|---|---|
| Vector2 / Vector2i | 2D 向量,含 x、y 字段 |
| Vector3 | 3D 向量,含 x、y、z 字段 |
| Rect2 | 2D 矩形,含 pos、size 字段 |
| Transform2D | 2D 变换矩阵(3×2) |
| Plane | 3D 平面(标准化形式) |
| Quat / Quaternion | 四元数,用于 3D 旋转 |
| AABB | 轴对齐包围盒(3D) |
| Basis | 3×3 矩阵,用于 3D 旋转和缩放 |
| Transform3D | 3D 变换(Basis + origin) |
三、引擎专用类型
| 类型 | 说明 |
|---|---|
| Color | 颜色,含 r、g、b、a 字段,也可访问 h、s、v |
| NodePath | 节点的编译路径 |
| RID | 资源 ID,引用不可见数据 |
| Object | 所有对象的基类 |
四、容器类型
| 类型 | 说明 |
|---|---|
| Array | 动态数组,元素类型可不同,索引从 0 开始 |
| Dictionary | 字典,键值对容器,键可以是任意类型 |
五、类型化数组(专用数组)
| 类型 | 说明 |
|---|---|
| PackedByteArray | 字节数组(0-255) |
| PackedInt32Array / PackedInt64Array | 整数数组 |
| PackedFloat32Array / PackedFloat64Array | 浮点数数组 |
| PackedStringArray | 字符串数组 |
| PackedVector2Array | Vector2 数组 |
| PackedVector3Array | Vector3 数组 |
| PackedColorArray | Color 数组 |
字面量
| 例子 | 描述 |
|---|---|
| null | 空值 |
| false, true | 布尔值 |
| 45 | 十进制整数 |
| 0x8f51 | 十六进制整数 |
| 0b101010 | 二进制整数 |
| 3.14, 58.1e-10 | 浮点数(实数) |
| "Hello", "Hi" | 常规字符串 |
| """Hello""", '''Hi''' | 常规字符串(用三对引号括住) |
| r"Hello", r'Hi' | 原始字符串 |
| r"""Hello""", r'''Hi''' | 原始字符串(用三对引号括住) |
| &"name" | StringName |
| ^"Node/Label" | NodePath |
注释
使用井号 # 对某一行进行注释。
行间语句接续
当代码太长,一行写不完,或者一行里写完影响美观时,可以换行续写。
var a = 1 + \
2 + \
3密存数组
当需要存储大量数据时,普通数组对内存的伤害就很大。使用密存数组此时成为了非常好的选择。
| 类型 | 说明 |
|---|---|
| PackedByteArray | 字节(从0到255的整数)数组 |
| PackedInt32Array | 32位整数数组 |
| PackedInt64Array | 64位整数数组 |
| PackedFloat32Array | 32位浮点数数组 |
| PackedFloat64Array | 64位浮点数数组 |
| PackedStringArray | 字符串数组 |
| PackedVector2Array | Vector2类型的数组 |
| PackedVector3Array | Vector3类型的数组 |
| PackedColorArray | Color类型的数组 |
数组
数组里只能存放相同类型的元素。
gdscript
var fruits = ["apple", "banana", "orange"]
# 下标访问
print(fruits[0])
# 增删改
fruits.append("peach")
print(fruits)
fruits.remove("banana")
print(fruits)
fruits[0] = "peach"
print(fruits)
# 插入、弹出元素
fruits.insert(0, "apple")
print(fruits)
fruits.pop_front()
print(fruits)
fruits.pop_back() # 弹出最后一个元素
print(fruits)
# 数组长度
print(fruits.size())
# 是否包含某个元素
print(fruits.has("peach"))
# 获取索引
print(fruits.find("peach"))
# 遍历数组
for fruit in fruits:
print(fruit)请注意,数组在godot中是引用传递的,所以如果在函数中修改了数组,那么在函数外部也会被修改。如果不想修改数组,可以使用数组的副本。
副本:
gdscript
var fruits_copy = fruits.duplicate()
print(fruits_copy)字典
可以存储不一样的值的数据。可以有两种创建风格
字符串为键的风格:
gdscript
var d = {
22: "value",
"key": 2
}Lua风格:
gdscript
var d = {
test22 = "value",
some_key = 2
}访问字典有两种方式:
gdscript
# 假设有字典dict
print(dict[22])
print(dict["key"])
print(dict.test22)可以像访问键一样添加值
gdscript
var d = {}
d.waiting = 14
d[1] = "First"
var firstKey = 1
print(d[firstKey])删除键值对:
gdscript
d.erase(22)
print(d)查:
gdscript
print(d.has(22))
print(d.size())遍历:
gdscript
# 遍历键
for key in d.keys():
print(dict[key])
# 遍历值
for value in d.values():
print(value)
# 直接遍历,等价于键遍历
for key in dict:
print(dict[key])理论上可以用方括号读取任何 Object 的属性。但是,属性不存在的时候,读取会报错 所以更建议使用 Object.get() 和 Object.set()方法 字典也是引用类型,所以如果在函数中修改了字典,那么在函数外部也会被修改
字符串
这里的字符串指的是String类型。
gdscript
var str = "Hello"
print(str.length()) # 长度
print(str[0]) # 第一个字符
print(str.substr(1, 3)) # 第二个到第三个字符
print(str.to_upper()) # 转换为大写
print(str.to_lower()) # 转换为小写
print(str.contains("l")) # 是否包含某个字符
print(str.begin_with("l")) # 是否以某个字符开头
print(str.end_with("o")) # 是否以某个字符结尾
print(str.replace("l", "L")) # 替换字符。仅替换第一个匹配项。
# 拼接字符串
print("Hello" + " World")
# 另一种拼接
print("、".join(["b", "c"])) # b、c
# 拆分字符串
print(str.split("l"))
# 去除空格
print(" Hello ".strip_edges()) # Hello
# 转数字
print("42".to_int()) # 42
print("42.0".to_float()) # 42.0静态成员
静态成员用static来标识。它们是直属于类的。由于直属于类,所以可以通过类名直接调用。
注意,由于直属于类,所以是全局同一的,也就是多处使用的时候一定要注意,不可以随意乱改动其值
枚举
一组常量,为某些常量连续分配整数时非常有用。
enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3
func _ready():
# Access values with Name.KEY, prints '5'
print(State.STATE_JUMP)
# Use dictionary methods:
# prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
print(State.keys())
# prints '{ "STATE_IDLE": 0, "STATE_JUMP": 5, "STATE_SHOOT": 6 }'
print(State)
# prints '[0, 5, 6]'
print(State.values())流程控制
流程控制,包括判断和循环。
判断:if/elif/else,match
循环:for,while
var a = 10
var b = 0
if 1 < a:
print("小")
elif 8 == a:
print("等于")
else 9 > a:
print("大于")
while b < a:
b += 1
print(b)
for x in [1,2,3]:
print(x)
var toMach = 50
match toMach:
50:
print("不及格")
60:
print("及格")函数
使用func关键字来声明函数
func onGetName:
# 空的内容可以用pass占位
pass
func onGetAge -> int:
return 18匿名函数
var lambda = func(x): print(x)
lambda.call(42)静态函数
静态函数不能访问实例成员变量,也不能使用 self,非常适用于创建辅助函数库。
static func sum2(a, b):
return a + b类
面向对象的思想中,类是一个非常重要的概念。
举例来说,当我们谈到"蛋"的时候,一般就会想到坚硬外壳,近似圆形,能够孕育生命的这种物质。
而当说到"鸡蛋"的时候,形象与描述会更加具象化,会在之前的基础上再附加一些描述,例如椭圆形,通常来说只能孵出鸡,外壳是橙黄色。
所以,当我们说"蛋类"的时候,会想到更宽泛的描述,这也是编程世界中"类"这一概念所想要达到的目的。
在godot中,所有脚本文件都可以视作类。此时可以用文件路径引入。例如,假设有脚本character.gd:
gdscript
# 扩展自character.gd
extends "res://path/to/character.gd"
# 加载并新建一个节点.
var Character = load("res://path/to/character.gd")
var character_node = Character.new() # 实例化可以使用class_name关键字为类起名,还可以配合@icon来定制类图标。
图标使用svg格式的比较好,不会在缩放过程中失真
# item.gd
@icon("res://interface/icons/item.png")
class_name Item
extends Node继承
想继承自某个类可以这样写:
class_name MyNode extends Node
var health = 5
func _ready():
print(health)类可以继承自全局类、另一个类文件、另一个类文件中的内部类。
不允许多继承。
使用is判断是否继承自给定类
# Cache the enemy class.
const Enemy = preload("enemy.gd")
# Use 'is' to check inheritance.
if entity is Enemy:
entity.apply_damage()使用super关键字调用父类中的方法。
# 父类中有some_func方法,使用下面这种写法调用父类的some_func
func some_func(x):
super(x)
# 父类中有overriding方法
func dont_override():
return super.overriding()构造函数
使用_init关键字作为构造函数的标识。父类构造函数带参,则子类也必须定义构造函数并适当传参。
func _init(arg):
super("some_default", arg)静态构造函数,在类载入,静态类成员变量初始化后自动调用。
static var my_static_var = 1
static func _static_init():
my_static_var = 2内部类用class关键字定义,用 类名.new() 函数进行实例化
as 和 is
子类的功能通常比父类更多。当你平时调用的是父类,但是某个时候需要调用子类的方法时,需要使用as关键字。
gdscript
var entity: Node = $SomeNode
if entity is CharacterBody2D:
# 先用 is 检查
var character: CharacterBody2D = entity as CharacterBody2D
character.move_and_slide()输入
输入使用的是Input类。以事件驱动,需要添加事件。



extends RigidBody2D
func _physics_process(delta: float) -> void:
if Input.is_action_pressed("move_right"):
apply_force(Vector2(25,0))获得场景节点
get_tree
get_tree().reload_current_scene() 可以重新加载当前场景
信号与连接
信号可以视作由事件触发的动作。当事件发生时会发送信号。需要与某些函数绑定。
信号可以使用signal关键字自定义,使用connect关键字连接,使用emit关键字触发
extends Node
# 可带参可不带参
# signal health_changed
signal health_changed(old_value, new_value)
# 在game.gd中
func _ready():
var character_node = get_node('Character')
character_node.health_depleted.connect(_on_character_health_depleted)
func _on_character_health_depleted():
get_tree().reload_current_scene()
# character.gd
signal health_changed
func take_damage(amount):
var old_health = health
health -= amount
# We emit the health_changed signal every time the
# character takes damage.
health_changed.emit(old_health, health)等待或开启协程
使用await关键字创建协程。会等待信号或协程函数的完成。
例如:
func wait_confirmation():
print("Prompting user")
await $Button.button_up # 等待按钮的抬起事件
print("User confirmed")
return true
# 上面的 wait_confimation() 中使用了await,所以它变成了协程函数
# 在其他调用它的地方,也需要使用await,否则会报错
# 下面是调用示例
func request_confirmation():
print("Will ask the user")
var confirmed = await wait_confirmation()
if confirmed:
print("User confirmed")
else:
print("User cancelled")对非信号和非协程函数使用await跟没写await一样
如果函数返回的是信号,那么对这个函数使用await时,会等待信号发生。例如:
func get_signal():
return $Button.button_up
func wait_button():
await get_signal()
# 等到按钮按下才会执行后面的打印语句
print("Button was pressed")assert断言
断言在调试模式下会中断运行。
绝对不要在发布的版本中留有断言!!!
assert(life > 0, "Player failed!" )一些规范
- 文件名、函数、变量要小写,单词之间使用下划线分隔。例如:find_obj.gd
- 类和节点要使用大驼峰命名(首字母大写,其后的单词首字母也大写)。例如:OnGetUserAge。
- 信号要用过去时态命名
- 常量和枚举的成员名均使用大写,下划线分隔单词。
代码顺序遵循如下规则:
1、先写信号和属性,然后再写方法。 2、先写公共成员,然后再写私有成员。 3、先写虚函数回调,然后再写类的接口。 4、先写对象的构造函数和初始化函数 _init 和 _ready ,然后再写修改对象的函数。