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 |
数据类型
值类型:null, bool, int, float, String, StringName等。
引用类型:Object, Array, Dictionary, 密存数组。
引用类型的实例会共享同一个值。
var a = 10
var b: float = 1.1
var arr = []
arr = [1,2,3]
var isTrue = true
整数、浮点数可以用_来分隔
12_345_678 # Equal to 12345678. 3.141_592_7 # Equal to 3.1415927. 0x8080_0000_ffff # Equal to 0x80800000ffff. 0b11_00_11_00 # Equal to 0b11001100.
字面量
例子 | 描述 |
---|---|
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类型的数组 |
字典
可以存储不一样的值的数据。可以有两种创建风格
字符串为键的风格:
var d = {
22: "value",
"key": 2
}
Lua风格:
var d = {
test22 = "value",
some_key = 2
}
可以像访问键一样添加值
var d = {}
d.waiting = 14
d[1] = "First"
var firstKey = 1
print(d[firstKey])
理论上可以用方括号读取任何 Object 的属性。但是,属性不存在的时候,读取会报错 所以更建议使用 Object.get() 和 Object.set()方法
静态成员
静态成员用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())
函数
使用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
流程控制
流程控制,包括判断和循环。
判断: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("及格")
类
面向对象的思想中,类是一个非常重要的概念。
举例来说,当我们谈到"蛋"的时候,一般就会想到坚硬外壳,近似圆形,能够孕育生命的这种物质。
而当说到"鸡蛋"的时候,形象与描述会更加具象化,会在之前的基础上再附加一些描述,例如椭圆形,通常来说只能孵出鸡,外壳是橙黄色。
所以,当我们说"蛋类"的时候,会想到更宽泛的描述,这也是编程世界中"类"这一概念所想要达到的目的。
在godot中,所有脚本文件都可以视作类。此时可以用文件路径引入。例如,假设有脚本character.gd:
# 扩展自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() 函数进行实例化
输入
输入使用的是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 ,然后再写修改对象的函数。