说明:Godot的专用脚本语言

历史总结一下就是由于其他语言的总总不适合Godot,最后GDScript就诞生了。此处省略一万字……想详细了解的可以看原文


示例

通过语法能够更好的学习,所以这里有一个简单的示例


1. #a file is a class! # 一个文件是一个类
2. 
3. # inheritance # 继承
4. extends BaseClass
5. 
6. # member variables # 成员变量
7. 
8. var a=5 
9. var s="Hello"
10. var arr=[1,2,3]
11. var dict={"key":"value", 2:3}
12. 
13. # constants # 常量
14. 
15. const answer=42
16. const thename="Charly"
17. 
18. # built-in vector types # 内置vector类型
19. 
20. var v2 = Vector2(1,2)
21. var v3 = Vector3(1,2,3)
22. 
23. # function # 函数
24. 
25. func some_function(param1,param2):
26.     var local_var=5
27.    if param1 < local_var:
28.         print(param1)
29.     elif param2 > 5:
30.         print(param2)
31.     else:
32.         print("fail!")
33. 
34.     for i in range(20):
35.         print(i)
36. 
37.     while(param2!=0):
38.         param2-=1
39. 
40.     var local_var2 = param1+3
41.     return local_var2
42. 
43. 
44. # subclass # 子类
45. 
46. class Something:
47.     var a=10
48. 
49. # constructor # 构造器
50. 
51. func _init():
52.     print("constructed!")
53.     var lv = Something.new()
54.     print(lv.a)
55.

复制代码语言

标识符

         标识符可以是一个包含任何字母、数字、下划线的字符串,但是不能以数字开头,并区分大小写。

关键字

        下表列出了支持的关键字,由于关键字都是保留字,所以不能用来做标识符。


操作符

下表是支持的操作符和他们的优先级:




字面值



注释:用“#”来注释一行语句

  1. # This is a comment

复制代码内置类型基础内置类型

在GDScript中的一个内置变量可以被指定多种内置类型


null:空类型

bool:布尔类型,只有true或false

int:整型,可以包含正整数和负整数

float:浮点型,包含浮点值

String:unicode格式的字符序列,包含标准的C转义序列


矢量内置类型

Vector2/Size2

2D矢量类型,包含x和y字段,能够访问可读的宽和高字段,也能做为数组被访问。


Rect2

2D矩形类型,包含两个矢量字段,“pos”和“size”,另外还包含一个“pos+size”的“end”字段。


Vector3

3D矢量类型,包含x,y,z字段。也能被当做数组来访问。


Matrix32

用做2D转换的3x2矩阵。


Plane

标准化形式的3D平面,包含一个“标准”矢量字段和一个“d”标量距离。


Quat

四元数,用于代表一个3D旋转的数据类型,对于插值旋转很有用。


AABB/Box3

轴对齐包围盒(或可选的,3D盒)。包含2个矢量字段,“pos”和“size”。另外包含一个“pos+size”的“end”字段


Matrix3

用做3D旋转和缩放的3x3矩阵。包含3个矢量字段x,y,z,能做为数组或3D矢量被访问。


Transform

3D转换,包含一个类型是Matrix3的“basis”字段和Vector3类型的“origin”字段


引擎内置类型

Color

Color数据类型,包含r,g,b,a字段。也能为hue/saturation/value做为h,

s,v被访问。


Image

包含一个能够直接访问其像素的自定义格式图片。


NodePath

节点的编译路径,主要用于场景系统,可以很容易的从字符串指定或指定到字符串。


RID

资源ID(RID),服务器用通用的资源ID来引用不可见的数据。


Object

所有事物的基类,不是一个内置类型


InputEvent

InputEvent对象非常紧凑的包含了从输入设备获取的事件。他们能够从帧到帧被大量的接收,他们能够用自己的数据类型优化。


容器内置类型

Array

数组是对象序列,长度可变,索引从0开始。


1. var arr=[]
2. arr=[1,2,3]
3. arr[0]="Hi!"

复制代码数组在内存中以线性分配,所以很快,但是很大的数组(超过几万的元素)可能生成碎片。

对于一些内置数据类型,有专门的数组(如下),用更少的内存,但是他们运行会更慢,所以只适合处理很大的数据。


Dictionary

字典,关联式容器,其中包含了唯一的键对值的引用


1. var d={4:5, "a key":"a value", 28:[1,2,3]}
2. d["Hi!"]=0

复制代码同样支持lua样式的语法


1. var d= {
2.   somekey=2,
3.   otherkey=[2,3,4],
4.   morekey="Hello"
5. }

复制代码ByteArray

字节数组,只能包含bytes(从0到255的整数)。

IntArray

整形数组,只能包含整数。

FloatArray

浮点型数组,只能包含浮点数。

StringArray

字符串型数组,只能包含字符串。

Vector2Array

Vector2型数组,只能包含2D矢量。

Vector3Array

Vector3型数组,只能包含3D矢量。

ColorArray

Color型数组,只能包含颜色。


数据

变量

变量的存在方式有类成员变量和方法中的局部变量。用“var”来创建变量,并可以在初始化时随意的赋值。


1. var a # datatype is null by default # 默认数据类型是null
2. var b = 5
3. var c = 3.8
4. var d = b+c # variables are always initialized in order # 变量通常按顺序初始化

复制代码常量

常量类似于变量,但是必须是常量或常量表达式,并且在初始化时应该被赋值


1. const a = 5
2. const b = Vector2(20,20)
3. const c = 10+20 # constant expression # 常量表达式
4. const d = Vector2(20,30).x # constant expression: 20 # 常量表达式:20
5. const e = [1,2,3,4][0] # constant expression: 1 # 常量表达式:1
6. const f = sin(20) # sin() can be used in constant expression # sin()会被常量表达式所使用
7. const g = x+20 # invalid, not a constant expression! # 无效的声明方式,没有常量表达式!

复制代码函数

函数总是属于一个类。变量的范围优先级查找顺序是:局部变量→类成员变量→全局变量。“self”被用来访问类成员,但是也不是永远需要如此(不必像Python中一样定义为第一个参数)。由于性能的原因,函数不被看作是类成员,所以他们不能直接被引用。函数可以在任何一点返回,默认返回值是null。

1. func myfunction(a,b):
2.     print(a)
3.     print(b)
4.     return a+b # return is optional, otherwise null is returned # return是可选的,否则返回null

复制代码控制语句

“,”逗号可以做为分隔符

if/else/elif

这个不多说了

1. if [expression]:
2.     statement(s)
3. elif [expression]:
4.     statement(s)
5. else:
6.     statement(s)

复制代码while

可以用break或continue


1. while [expression]:
2.     statement(s)

复制代码for

范围迭代。

1. for i in [0,1,2]:
2.     statement # loop iterates 3 times, i being 0,1 and 2 # 循环迭代三项,i的值变化是0,1,2
3. 
4. var dict = {"a":0, "b":1, "c": 2}
5. for i in dict:
6.     print(dict[i]) # loop iterates the keys, i being "a","b" and c". It prints 0, 1 and 2. # 循环迭代字典中的键,i值变化是 a,b,c,打印结果是0,1,2
7. 
8. for i in range(3):
9.     statement # similar to [0,1,2] but does not allocate an array # 类似[0,1,2],但是不分配数组
10. 
11. for i in range(1,3):
12.     statement # similar to [1,2] but does not allocate an array # 类似[1,2]但是不分配数组
13. 
14. for i in range(2,8,2):
15.     statement # similar to [2,4,6] but does not allocate an array # 类似[2,4,6]但是不分配数组

复制代码

默认情况下,脚本的主体文件是一个未命名的类,只能做为外部资源或者文件被引用。类语法注定很紧凑,只能包含类成员变量或方法。类中允许有静态方法但是不允许有静态成员变量(基于线程安全的精神,因为脚本是可以在用户不知道的情况下在独立的纯种中被初始化)以同样的方式,成员变量(包括数组和字典)在实例被创建时初始化。


类文件示例

类文件示例,想像它已经以文件的形式被存储,类似于myclass.gd

1. var a=5
2. 
3. func print_value_of_a():
4.     print(a)

复制代码继承

一个类文件可以继续自全局类,另外一个文件或另外一个文件的子类。不允许多继承。用“extends”语法:

1. # extend from some class (global) # 继承自其他类(全局)
2. extends SomeClass 
3. 
4. # optionally, extend from another file # 可随意继承自其他文件
5. extends "somefile.gd" 
6. 
7. # extend from a subclass in another file # 继承自其他文件的子类
8. extends "somefile.gd".Subclass

复制代码继承测试

它可以检测一个实例是否继承自一个给定的类。这样,“extends”关键字可以被用来做为操作符:


1. [..]
2. 
3. if ( entity extends enemy_class ):
4.     entity.apply_damage()

复制代码构造函数

一个类有一个可靠的构造函数,一个名叫“_init”的函数,当类被实例化的时候被调用。


子类

一个类文件可以有子类,语法非常简单直接:

1. class SomeSubClass:
2.     var a=5
3.     func print_value_of_a():
4.         print(a)
5. 
6. func _init():
7.     var sc = SomeSubClass.new() #instance by calling built-in new # 用内置的new关键字来实例化
8.     sc.print_value_of_a()

复制代码类对象

可能需要在某个时候从文件加载一个类并且实例化它,由于全局范围不存在,类必须做为资源被加载。在类对象中调用“new”函数来完成实例化:


1. #load the class (loaded every time the script is instanced) # 加载类(每次加载完成脚本会被实例化)
2. var MyClass = load("myclass.gd")
3. 
4. # alternatively, using the preload() function preloads the class at compile time # 或者在编译时用preload()方法预加载类
5. var MyClass2 = preload("myclass.gd")
6. 
7. func _init():
8.     var a = MyClass.new()
9.     a.somefunction()

复制代码导出

类成员变量能被导出。这意味着他们的值和场景一起被保存。如果类成员有初始常量表达式,他们将在属性编辑器是可编辑。用export关键字完成导出:


1. extends Button
2. 
3. export var data # value will be saved # 值将会被保存
4. export var number=5 # also available to the property editor # 对属性编辑器同样有效

复制代码导出成员变量的基本好处是在属性编辑器中可见。这样美术和策划就可以修改值影响后来的运行情况。对此,为更详细的导出变量提供了一个特别的导出语法:


1. #if the exported value assigns a constant or constant expression, the type will be infered and used in the editor # 如果导出的值是一个常量或常量表达式,该类型会被推测并在编辑器中使用
2. 
3. export var number=5
4. 
5. # export can take a basic datatype as argument, which will be used in the editor # export可以有一个基本数据类型做为参数,将被在编辑器在使用
6. 
7. export(int) var number
8. 
9. # export can also take a resource type as hint # export也能够有一个资源类型做为暗示
10. 
11. export(Texture) var character_face
12. 
13. # integers and strings hint enumerated values # 整形和字符串暗示的枚举值
14. 
15. export(int,"Warrior","Magician","Thief") var character_class # (editor will set them as 0,1 and 2)  # (编辑器将设置他们为0,1,2)
16. export(String,"Rebecca","Mary","Leah") var character_name 
17. 
18. # strings as paths # 做为路径的字符串
19. 
20. export(String,FILE) var f # string is a path to a file # 字符串是文件路径
21. export(String,DIR) var f # string is a path to a directory # 字符串是目录
22. export(String,FILE,"*.txt") var f # string is a path to a file, custom filter provided as hint # 字符串是一个文件路径,自定义过滤器提示
23. 
24. # integers and floats hint ranges # 整型和浮点型暗示的范围
25. 
26. export(int,20) var i # 0 to 20 allowed # 允许0到20
27. export(int,-10,20) var j # -10 to 20 allowed 允许 -10到20
28. export(float,-10,20,0.2) var k # -10 to 20 allowed, with stepping of 0.2 # 允许-10到10,以0.2为步长增加
29. 
30. # color can hint availability of alpha 颜色可以暗示透明度的可用性
31. 
32. export(Color,RGB) var col # Color is RGB # Color是RGB
33. export(Color,RGBA) var col # Color is RGBA # Color是RGBA

复制代码必须说明一下,即使脚本在编辑器中没有被执行,被导出的属性依然可被编辑(查看正面的“tool”)


静态函数

一个函数能被定义为静态的,如果是静态函数,则不能用访问实例的成员变量或“self”。这主要是有益于编写库和helper函数。


1. static func sum2(a,b):
2.     return a+b

复制代码工具模式

脚本在默认的情况下并不在编辑器中运行,并且只有导出的属性能被改变。有某些情况下期望他们可以这样做(只要他们不执行游戏代码或手动避免这样做),为此“tool”关键字出现了,并且必须放置在文件顶部。


1. tool
2. extends Button
3. 
4. func _init():
5.     print("Hello")

复制代码内存管理

如果一个类从Reference继承,当实例不再使用时应该被释放。没有垃圾回收器的存在,只是简单的引用计数器。默认情况下,所有的类没有定义从Reference继承。如果这样不理想,那么一个类必须手动继承Object并且必须调用instance.free()。为了避免这种不能被释放的环引用情况,提供了一个创建弱引用的函数weakref()


函数引用

函数不能被引用,因为他们不能被看做是类成员。这里有两个备选方案,分为是“call”函数和funcref()助手。


1. instance.call("funcname",args) # call a function by bane # 调用一个函数
2. 
3. var fr = funcref(instance,"funcname") #create a function ref # 创建一个函数ref
4. fr.exec(args)

复制代码