官方文档
因为和python很像, 但又和python完全独立, 所以就说下区别, 没有说的就是和python一样:

class_name extends

因为一个文件就是一个类,跟java类似.
class_name是类名, 只有想让其他类(文件)引用的时候才要起名字.
extends就是扩展父类,也就是继承.

func

定义函数的关键字. python是def. 所以这个像是js的function的缩写

match

match相当于C的switch,但python一直没有类似switch的语句, 他们局的if/else足够了...

match param3:
  3:
    print("param3 is 3!")
  _:
    print("param3 is not 3!")

_和default一个意思
match还支持一些更复杂的匹配方式, 这儿不罗列了, 需要的时候再去看官方文档.

is

检测变量是否继承自给定的类,或检测该变量是否为给定的内置类型。

as

尝试将值转换为给定类型的值

变量常量

跟js类似分别是var/const. python是不需要变量关键词的.
定义数组的方式没有什么不同,但定义dict的方式比较多样, 除了冒号还可以使用等号, 使用冒号的时候语法同python, key需要用双引号, 使用等号的时候类似js, key可以不用双引号(官方文档说这个事lua风格,我没用过lua):

var dict = {"key": "value", 2: 3}
var other_dict = {key = "value", other_key = 2}

访问key的方式和js一样, 比python方便: dict.key或者dict["key"]

另外还支持严格变量类型检查:

var typed_var: int
var inferred_type := "String"
var s : String = 'hello'

enum和vector

enum类似于C

# Enums.
enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types.
var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)

static

可以声明静态变量, 这个也类似C

signal

定义信号

breakpoint

用来设置脚本编辑器辅助调试断点的关键字。与在脚本编辑器每行最左侧点击红点创建断点不同, breakpoint 关键字可以储存在脚本内部。在不同机器上使用版本工具时,由 breakpoint 关键字创建的断点仍旧有效。

preload

预加载一个类或变量,参见 类作为资源 。

await

等待信号或协程完成,参见等待信号和协程。似乎类似于ES6

assert

断言条件,如果失败则记录错误。在非调试版本中忽略掉断言语法。参见 Assert 关键字。
少见的原生带断言且把断言作为关键字的语言了. 别的语言大多需要原生库或者第三方库实现.

void

用于代表函数不返回任何值. 借用的C语法.

特殊常数 PI TAU INF NAN

PI(π)常数。
TAU(τ)常数。
INF 无穷常量,用于比较和计算结果. 这个缩写我有点接受不了, 因为.inf文件指的是information, 更多用作信息而不是无穷.
NAN(非数)常量,用作计算后不可能得到的结果。

运算符

基本的+-*/没有问题, 还支持**乘方, %取余.
按位运算和C,python都类似, 与或非分别是&|~,完全一致. 异或是^, 位移是>> <<, 但没有循环位移>>>
逻辑比较和多数一致, 其中不等于也是!=.
逻辑运算同时支持python和js运算符, 也就是not and or! && ||同时支持.但官方更推荐not and or风格, 比较容易理解且不容易混淆. 嗯, 这个我觉得是看个人语言习惯了. 要说符号看得头晕我觉得还得是正则表达式.
三元运算符:变量=真的值 if 条件 else 假的值
自加类运算符+= -= *= /=都支持,但不支持++ --

可能和预期不符的运算

以下来自官方文档:

一些运算符的运算机制可能会与你的预期有所不符:

若运算符 / 两端的数值均为 int,则进行整数除法而非浮点数除法。例如: 5 /2 == 2 中 2 为该算式的结果而非 2.5 为结果。若希望进行浮点数运算,请将该运算符两端的其中一个数值的类型改为 float 。例如:直接使用浮点数( x / 2.0 )、转换类型( float(x) / y )、乘以 1.0 ( x * 1.0 / y )等。

运算符 % 仅适用于整型数值的取余运算。对于小数的取余运算,请使用 fmod() 方法。

对于负值,% 运算符和 fmod() 函数使用 截断算法 而非向负无穷大舍入,此时余数会带有符号(即余数可能为负)。如果你需要数学意义上的余数,请改用 posmod() 和 fposmod() 函数。

运算符 ** 是 左结合运算符 ,也就是说, 2 ** 2 ** 3 这个运算等价于 (2 ** 2) ** 3 。对此,请使用括号来处理该运算的优先级,如 2 ** (2 ** 3)

== 和 != 运算符在有些情况下允许你比较不同类型的值(例如,1 == 1.0 的结果为真),但在其他情况下可能会发生运行时错误。如果你不能确定操作数的类型,可以安全地使用 is_same() 函数(但请注意,该函数对类型和引用更加严格)。要比较浮点数,请改用 is_equal_approx() 和 is_zero_approx() 函数。

字面量

布尔值 true false 和js一样,和python不同, python是True False
十六进制和二进制还是0xabc0b10110
另外支持一个很有趣的下划线数字写法, 就是在很长的数字中间插入_,和不插的意义一样, 但是方便阅读, 如:

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.

字符串

字符串只支持双引号"hello",不支持单引号. 但支持三双引"""hello"""和三单引'''hello'''
原始字符串(不转义)r"hello"和r'hello', 也支持三引号形式.
另外还有两个特殊字符串: stringName, $"name". nodePath: ^"Node/Label"
stringName是为了提高字符串效率构造的新的类型, 我的理解是相同字符串引用的是相同地址, 这样比较起来很快速, 不需要逐字比较.
转义字符如\n \t不在罗列,跟C js类似.
支持字符串格式化. 类似C形式的如:"We're waiting for %s." % "Godot", 注意这是一个字符串表达式形式的, 中间使用%连接. 格式化占位符和C语言类似. 多个占位符怎么办呢? 后面的实际内容就得换用数组形式, 如"%s was reluctant to learn %s, but now he enjoys it." % ["Estragon", "GDScript"]
由于格式化占位符缺乏意义, 也支持更现代的名称占位符, 但这个名称却不能是变量名, 得是dict的key名, 也不能用%简写连接, 得用String.format()方法, 如: "Hi, {name} v{version}! ".format({ "name":"Godette", "version":"3.0" })
%连接格式化字符串和变量, 与用.format连接的区别是, %对数字格式有更好的控制, 如加入前导0, 四舍五入显示等, 后者可读性更强, 所以有时候还得结合起来用, 如: "Hi, {0} v{version} ".format({0:"Godette", "version":" %0.2f" % 3.114}),显示为Hi, Godette v3.11

注解 @

@开头, 有点像C语言以#开头. C语言中#开头表示与编译器交互的语句.这儿@开头表示与编辑器交互的语句. 如把有些变量值导入编辑器中.
有一个特别提出来的@onready注解, 可以将变量的赋值推迟到ready以后, 而不需要在_ready()回调里面再写一次.

代码区块

这个仅仅是为了方便将超长的一段代码用编辑器折叠的. 一般编辑器可以折叠一个函数, 而代码区块可以折叠多个函数,只要是在一个区块中.
代码区块是写在注释里的一对#region#endregion, 嗯, 这个跟我写C的风格有点类似, 我也喜欢将同类代码用一对注释括起来:

# This comment is outside the code region. It will be visible when collapsed.
#region Terrain generation
# This comment is inside the code region. It won't be visible when collapsed.
func generate_lakes():
    pass

func generate_hills():
    pass
#endregion

#region Terrain population
func place_vegetation():
    pass

func place_roads():
    pass
#endregion

内置类型

除了int float Array String等这些一般语言都有的内置类型以外, 还有很多特有的内置类型, 如向量Vector2 Color等, 有需要请看官文.

Array数组

var a = []
a.append(1) # 尾部添加
a.pop_front() #头部弹出
a.pop_back() #尾部弹出
a[0] # 第一个元素
a[-1] # 倒数第一个元素. 比较特别,index可以为负值,就是倒数.
a.size() # 取长度

高效运行

数组也支持类型化, 并且对于巨大的数组, 为提高效率还支持密集存储(密存), 密存类似于C的数组, 要求数组内部所有的数据类型相同, 且在内存中连续存放.

callable 可调用体

这个稍微有些奇怪. 别的语言把函数也当做变量可以赋值, 新的变量就是函数名. GDScript赋值是一样的, 但调用却一定要加call. 如var x = f调用需要写成x.call()而不是x()

声明函数的返回值

函数定义时候可以声明其返回值, 以对函数进行更严格的检查, 用->表示, 如:

func _init() -> void:
    print(_data)

函数形参也可以要求类型检查

func my_function(a: int, b: String):
    pass

也支持匿名函数直接赋值给变量.
还支持所谓静态函数, 其实相当于js的原型函数, 或者是class的函数, 不能访问实例(对象)的变量.

static func sum2(a, b):
    return a + b

类的构造函数

func _init(arg):
   super("some_default", arg) # Call the custom base constructor.

## getter和setter函数
这个显得比较高级了, 类似js中也有这样的函数. 并且vue.js里使用get/set函数保证界面刷新和数据更新同步.
```python
var milliseconds: int = 0
var seconds: int:
    get:
        return milliseconds / 1000
    set(value):
        milliseconds = value * 1000

写到这儿我稍微有点担心这个GPScript了, 因为这个语言里面用了很多高级的方法, 而且经常会更新和增加新的语法. 在其他语言里是有一个执行委员会会议确定, 又有很多大公司在写解释器, 又有很多第三方在写库文件, 整个一套复杂而有机的机制, 或者说形成了生态. GDScript虽然是游戏定制语言, 但是能否形成这样的完善程度, 只有拭目以待了.
刚查了gdscript在github上的仓库数, 有52.9k,和js的20.2M个和python的10.7M个, C的2.4M个真的相形见绌了.

标签: none 阅读量: 463

添加新评论