(原) Godot 免费跨平台游戏引擎 (四、脚本GDScript)

原创文章,请后转载,并注明出处。

Godot 免费跨平台游戏引擎 (一、初见)

Godot 免费跨平台游戏引擎(二、第一个游戏)

Godot 免费跨平台游戏引擎(三、理论是实践的基础)

Godot 免费跨平台游戏引擎 (四、脚本GDScript)

Godot 免费跨平台游戏引擎(五、常用英文)

Godot 免费跨平台游戏引擎(六、一些收集)

Godot 免费跨平台游戏引擎(七、2D)

Godot 免费跨平台游戏引擎(八、网络)

Godot 免费跨平台游戏引擎(九、GUI外观)

Godot 免费跨平台游戏引擎(十、相关资源)

Godot 免费跨平台游戏引擎(十一、源码编译)

Godot 免费跨平台游戏引擎(十二、软件GUI)

Godot简单制作残影效果

Godot ParallaxBackground 视差背景

Godot 使用Light2D实现遮罩效果

码农家的孩子:学字母(Godot改版中)


GDScript是Godot默认的编程语言,使用了类似Python的语法。

这里有英语的脚本介绍:关键字

这里有英语的脚本介绍:函数及语法

extends BaseClass    #继承
class_name MyClass, "res://path/to/optional/icon.svg"    #带有图标的类定义(可选)

# 变量定义
var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var typed_var: int
var inferred_type := "String"

# 常量
const ANSWER = 42
const THE_NAME = "Charly"

# 枚举
enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# 内置的向量类型
var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)

# 函数
func some_function(param1, param2):
    var local_var = 5

    if param1 < local_var:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2

# 基于父类/基类的函数重写.
# 如果你仍要调用父类的函数,使用.号。
func something(p1, p2):
    .something(p1, p2)

# 内部类
class Something:
    var a = 10

# 构造器
func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

语言

标识符

标识符区分大小写。

关键词

常见的就列出来,不常见的做一点解释

if、elif、else、for、while、match、break、continue、pass、return、func、const、enum、var
class:定义一个类
extends:定义用当前类扩展什么类
is:测试变量是否扩展给定的类,或者是否是给定的内置类型
as:如果可能,将值转换为给定类型
self:引用当前类实例
tool:在编辑器中执行脚本
signal:定义一个信号民
static:定义一个静态函数
onready:一旦脚本附加到的节点及其子级成为场景树的一部份,就初始化变量。
export:保存一个变量及其附加资源,使其在编辑器中可见和可修改。
setget:为变量定义setter和getter函数.
breakpoint:调试器断点的编辑器助手。
preload:预加载一个类或变量。
yield:协和支持
assert:断言一个条件,如果失败则记录错误。
remote:
master:
puppet:
remotesync:
mastersync:
puppetsync:
PI:圆周率常量
TAU:TAU常量 ?
INF:无穷大常数。
NAN:NAN(不是一个数字)常数

上面没有解释的几个,都是关于网络RPC方面的。

运算符

x[index] 数组索引
x.attribute 属性引用
foo() 函数调用
is 实例类型检查
~ 按位取反
-x 负/一元否定
* / % 乘/除/余数
+ -
<< >> 位移
& ^ |  按位与、异或、或
< > == != >= <=
in 内容测试
! not 布尔非
and && 布尔与
or || 布尔或
if x else  三元if/else
= += -= *= /= %= &= |=

变量

45 基数为10的整数
0x8F51 基数为16整数
0b101100 基数2整数
3.14, 58.1e-10 浮点数(实数)
"Hello" 字符串
"""Hello""" 多行字符串
@"Nodel/Label"  ??
$NodePath get_node("NodePath")的快捷方式,类似于获取节点名柄

注释

任何以#开始到行尾的内容视为注释

内置类型

null 空数据类型 bool true/false int 正/负整数 float 浮点实数 String Unicode字符序列

Vector2 二维向类(包括x和y) Rect2 二维矩形(包括postion、size和end字段) Vector3 三维向量(x、y、z) Transform2D 2D变换在3x2矩阵 Plane 3D平面类型的标准形式包含一个 normal 向量字段以及一个 d 标量距离 Quat 四元数是一种用于表示3D旋转的数据类型。 AABB 轴对齐边界框(或3D框)(包含position、size和end) Basis 3x3矩阵被用于3D旋转与绽放。 Transform 3D变换(包含basis(Basis)、origin(Vector3))

Color 颜色(r g b a) NodePath 编译路径,到一个主要用在场景系统中的节点。 RID 资源ID Object 任何非内置类型的基类

Array 任意对象类型的泛型序列,包括其他数组或字典。索引从0开始。负索引表示从尾部开始计数。

var arr = []
arr = [1, 2, 3]
var b = arr[1] # 2.
var c = arr[arr.size() - 1] # 3.
var d = arr[-1] # 与上相同
arr[0] = "Hi!" # 替换 1 为 "Hi!".
arr.append(4) # 序列现在是 ["Hi!", 2, 3, 4].

字典

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

数据

变量

var 定义

在声明中初始化变量

var my_vector2 := Vector2()
var my_node := Sprite.new()

有效的类型:

内置类型(Array、Vector2、int、String等)
引擎类(Node、Resource、Reference等)
常量名(const MyScript = preload("res://my_script.gd") )
脚本类使用 class_name 关键字声明

转换

如果值是相同类型或转换类型的子类型,则在对象类型之间进行转换会导致相同的对象。如果该值不是子类型,则强制转换操作将产生 null 值。

var my_node2D: Node2D
my_node2D = $Sprite as Node2D

对于内置类型,如果可能,将对其进行强制转换,否则引擎将引发错误。

var my_int: int
my_int = "123" as int
my_int = Vector2() as int

在与场景树进行交互时,强制转换对于获得更好的类型安全也很有用:

var my_sprite := $Character as Sprite
($AnimPlayer as AnimationPlayer).play("walk")

枚举

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

与下相同:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT =3

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT = 6 }

与下相同:
const State = {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT = 6}

函数

变量查找的作用域优先级是:局部->类成员->全局。 self 变量总是可用的,并作为访问类成员的选项提供。

函数可以任何时候 return,默认返回值是 null。

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

# 函数有默认值,可以自动推断类型
func my_func1(int_arg := 42, String_arg := "string"):
     pass     

# 指定返回类型
func my_int_fun() -> int:
     return 0     

# void 不返回任何内容
func void_func() -> void:
     return

引用函数

与Python不同,若要在过行时按名称引用一个函数,必须使用call或funcref。

my_node.call("my_function", args)

var my_func = funcref(my_node, "my_function")
my_func.call_func(args)

静态函数

函数可以声明为静态,它不能访问实例成员变量或 self。

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

语句和控制流程

# 允许将语句放在条件相同的行上
if 1 + 1 == 2: return 2 + 2
else:
   var x = 3 + 3
   return x

# 三元表达式
# var x = [值] if [条件] else [值]
y += 3 if y < 10 else -1   
for x in [5,7,11]:
    pass

var dict = {"a":0, "b":1, "c":2}
for i in dict:
    print(dict[i])

for i in range(3):
    print(i)

for i in range(1,3):
    print(i)             #1,2

for i in range(2,8,2):
    print(i)             #2,4,6    

for i in 3:   #等同于range(3)
    print(i)

for i in 2.2: #等同于range(ceil(2.2))
    print(i) 

match

类似于switch,用于分支程序运行。

match [变量]:
	[模式](s):
		[程序块]
	[模式](s):
		[程序块]
match x:
   1:
      print("...")
   2: 
      print("....")
   "test":
      print("????")

match typeof(x):
   TYPE_REAL:
      print("float")
   TYPE_STRING:
      print("text")
   TYPE_ARRAY:
      print("array")

match x:
   1:
      print("...")
   2:
      print(".....")
   _:
      print("下划线匹配所有其它情况,类型default")            

#绑定模式
match x:
   1:
      print("...")
   2:
      print("....")
   var new_var:
       print("...", new_var)

#数组模式
match x:
   []:
      print("空数组")
   [1,3,"test",null]:
      print("...")
   [var stat,_,"test"]:
      print ("stat开头,test结尾的数组")
   [42,...]:
      print("包含以42开头的数组?")

#字典模式
match x:
    {}:
       print("空字典")
    {"name": "Dennis"}:
       print("name为Dennis")
    {"name": "Dennis", age, var age}:
       print("Dennis is ", age, " years old.") 
    {"name", "age"}:
       print("包含name和age")
    {"key": "godo", ...}
       print("检测key,其它没管")

默认情况下,所有脚本都是未命名的类。只能使用路径引用

extends "res://path/character.gd"

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

也要以为类命名,并附图标。

extends Node
class_name Item, "res://interface/icons/item.png"

继承

不允许多重继承。使用 extends 关键字。

extends SomeClass
extends "somefile.gd"
extends "soefile.gd".SomeInnerClass
# 检查给定的实例是否从给定的类继承
const Enemy = preload("enemy.gd")
if (entify is Enemy):
   entify.apply_damage()
# 调用基类,在函数面前加.,象其它语言的super关键字。
func some_func(x):
    .base_func(args)

默认函数象_init和大多数通知象_enter_tree、_exit_tree、_process、_physics_processt等,将自动调用在所有父类中的函数。无需显示调用它们。

类构造器

# 在类实例化时调用的类构造函数名为_init。
# 如果被继承的类的构造函数接受参数,则将它们传递为:
func _init(args).(parent_args):
    pass

#示例
#State.gd
var entify = null
var message = null

func _init(e=null):
    entity = e

func enter(m):
    message = m

#Idle.gd
extends "State.gd"

#此构造函数将e传入基类
func _init(e=null, m=null).(e):
    message = m

内部类

类文件可以包含内部类。内部类使用 class 关键字定义。ClassName.new() 实例化。

class SomeInnerClass:
    var a = 5
    func print_value_of_a():
        print(a)

func _init():
     var c = SomeInnerClass.new()
     c.print_value_of_a()

类作为资源

存储为文件的类被视为 Resource。必须从磁盘加载它们才能在其他类中访问。

var my_class = load("myclass.gd")

const MyClass = preload("myclass.gd") #预载

func _init():
   var a = MyClass.new()
   a.some_function()

Setters/getters

函数用于获取类的成员在何时出于何原因被调用。

var 变量 = 值 setget setterfunc, getterfunc

每当变量被外部的源修改时,seeterfunc函数将被调用。这发生在值改变之前,函数将决定如何处理。

var my_var setget my_var_set, my_var_set

func my_var_set(new_value):
    my_var = new_value
func my_var_get():
    return my_var

也可以只要一部份

var my_var = 5 setget my_var_set
var my_var = 5 setget ,my_var_get

注意,本地访问是不触发函数的。

工具模式

我猜,这是用于改良编辑界面,提供一些适合中于自己的功能。

用tool关键字放在文件顶部

tool
extends Button

func _ready():
    print("Hello")

信号

func _ready():
   var character_node = get_node("Character")
   character_node.connect("health_depleted", self, "_on_Character_health_depleted")

func _on_Character_health_depleted():
   get_tree().reload_current_scene()

# 例

signal health_changed

func take_damage(amount):
   emit_signal("health_changed",1,2)


func _on_Character_health_changed(old_value,new_value):
   ...

协程使用yield

调用 yield() 将立即从当前函数返回,并且相同函数的当前冻结状态作为返回值。调用 resume() 将继续执行并返回函数。

#将输出Hello、my dear、 world
func my_func():
   print("Hello")
   yield()
   print("world")

func _ready():
    var y = my_func()
    print("my dear")
    y.resume()

还可以在 yield() 和 resume() 之间传递值


#将打印Hello、world、cheers
func my_func():
    print("Hello")
    print(yield())
    return "cheers"

func _ready():
    var y = my_func()
    print(y.resume("world"))

协程&信号

使用 yield 的真正优势在于信号结合使用。 yield 可以接受两个参数,对象和信号。收到信号后,将重新开始执行。

yield(get_tree(),"idle_frame")  #下一帧继续执行
yield(get_node("AnimationPlayer"),"animation_finished") #播放完动画后继续执行
yield(get_tree().create_timer(5.0),"timeout") #等待5秒然后继续执行

协程自身转换为无效状态时会使用 completed 信号

#my_func 仅在按下两个按钮后继续执行
func my_func():
   yield(button_func(), "completed")
   print("所有键都按下了")

func button_func():
   yield($Button0, "pressed")
   yield($Button1, "pressed")

Onready 关键字

使用节点时,通常希望将对场景部份的引用保留在变量中。由于仅在进入活动场景树时才保证要配置场景。因此只有在调用 Node._ready() 时才能获得子节点

var my_label

func _ready():
    my_label = get_node("MyLabel")

#将成员变量的初始化推迟到调用 _ready()。以上就可以用一行来替换:
onready var my_label = get_node("MyLabel")

Assert 关键字

可用于检查调试版本中的条件。??暂跳过。

GDScript 动态语言简介

变量与赋值

int a
a = "Hi"  #出错

var a
a = "Hi"  #没问题
a = 5  #没问题

类似的,在函数定义时

func print_value(int value)

func print_value(value)

指针和引用

func use_class(instance):  #不关心类的类型
   instance.use()    #任何有use方法的类


func do_something():
   var instance = SomeClass.new()  #引用创建
   use_class(instance)  #作为引用传递

数组

var array = [10, "hello", 40, 60]
array.resize(3)
use_array(array) #用为引用传递

var array = []
array.append(4)
array.pop_front()

字典

字典是动态的,键可以在任何一点添加或删除

d["monther"] = "Rebecca"
d.erase("name")

var d = {
	name = "John",
	age = 22
}

print("Name:",d.name)

d.mother = "Caroline"

For & while

for i in range(10,0,-1)

for key in dict

鸭子类型 ?

GDScript 导出

可导出类成员,它们的值会与它们所附加的资源一起保存。也可以在属性编辑器中进行编辑。

extends Button
export var number = 5  #对于赋值的情况,编辑器会推断类似

export(int) var number

export(Texture) var character_face  
export(Resource) var resource

export(int, "Warrior", "Magician", "Thief") var character_class
export(String, "Rebecca", "Mary") var character_name
export(String, "中国", "美国") var GuoJia   #变量不能为中文,选项可以为中文

enum NameEnum{THING_1, THING_2, ANOTHE = -1}
export(NameEnum) var x

export(String, FILE) var f   #一个带路径文件
export(String, DIR) var f   #一个目录
export(String, FILE, "*.txt") var f  #带类型过滤的文件
export(String, FILE, GLOBAL, "*.png") var tool_image  #这只能用于工具中
export(String, DIR, GLOBAL) var tool_dir

export(String, MULTILINE) var text   #多行文本
export(int, 20) var i   #从0到20
export(int, -10, 20) var j #从-10到20
export(float, -10, 20, 0.2) var k  #浮点数,从-10到20,间隔0.2
export(float, EXP, 100, 1000, 20) var l  #y=exp(x)

export(float, EASE) var transition_speed  #编辑时显示ease函数的可视化表示形式
export(Color, RGB) var col
export(Color, RGBA) var col

export(NodePath) var node  #节点选择

导出位标志

用作位标志的整数可以在一个属性中存储多个布尔值,有点类似于枚举。但是用8421码的方式进行存储。即Fire=1,Water=2,Earth=4,Win=8

export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0

导出数组

导出的数组在所有实例之间都是 共享的。

export var a = [1, 2, 3]
export(Array, int) var ints = [1,2,3]
export(Array, int, "Red", "Green", "Blue") var enums = [2, 1, 0]
export(Array, Array, float) var two_dimensional = [[1.0, 2.0], [3.0, 4.0]]

GDScript 格式字符串

var format_string = "We're waiting for %s."
var actual_string = format_string % "Godot"

var format_string = "We're waiting for {str}"
var actual_string = format_string.format({"str":"Godot"})

var format_string = "%s was reluctant to learn %s, but now he enjoys it."
var actual_string = format_string % ["Estragon", "GDScript"]

占位符类型

s 	通过隐式 String 转换的相同方法 简单 转换为 String。
c 	单个 Unicode 字符。对于代码点或单个字符, 需要一个无符号的8位整数(0-255)。
d 	一个 十进制 整数。需要一个整数或实数(向下取整)。
o 	一个 八进制 整数。需要一个整数或实数(向下取整)。
x 	一个 小写 字母的 十六进制 整数。需要一个整数或实数(向下取整)。
X 	一个 大写 字母的 十六进制 整数。需要一个整数或实数(向下取整)。
f 	一个 十进制 实数。需要一个整数或实数。

占位符的修饰符

+ 	用在数字说明符中,如果为正 显示 + 号 。
Integer 	设置 填充 。用空格填充,或在一个整数占位符中如果整数以 0 开头,则用0填充。当使用在 . 后时,参见 . 。
. 	在 f 之前,设置 精度 到0小数位。可以跟数字来改变。用零填充。
- 	向右填充 而不是向左。
* 	动态填充,在 . 后期望额外的整数参数来设置填充或精度。参见 动态填充。

填充

print("%10d" % 12345)
print("%010d" % 12345)
print("%10.3f" % 10000.5555)
print("%-10d" % 12345678)

#动态填充
var format_string = "%*.*f"
print(format_string % [7, 3, 8.8888])

  1. 通过函数名调用函数,即通过字符串调用

my_node.call(“my_function”, args)

  1. 将函数保存到变量

var my_func = funcref(my_node, “my_function”) #保存

my_func.call_func(args) #调用

继承

extends SomeClass #继承/扩展于一个全局类

extends “some file.gd.” #继承/扩展于一个类文件

extends “comefile.gd.“SomeInnerClass #继承文件的内部类

#判断是否继承

const Enemy = preload("enemy.gd") 

if (entity is Enemy):  
    entity.apply_damage()  

var my_class = load(“myclass.gd”)

const MyClass = preload(“myclass.gd”) #仅在编译时加载

可导出的类成员

export var number = 5 #可以导出的类成员

export(int) var number #数据类型

export(int, “Warrior”, “Magician”, “Thief”) var character_class #编辑器将枚举三个字符串为0.1.2

export(String, “Rebeca”, “Mary”, “Leah”) var character_name #枚举为字符串

以下也为枚举:

enum NamedEnum {THING_1, THING_2, ANOTHER_THING = -1}  
export (NamedEnum) var x  

export(String, FILE) var f #字符串是文件路径

export(String, DIR) var f #字符串是目录

export(String, FILE, “*.txt”) var f #指定文件类型

export(String, FILE, GLOBAL, “*.png”) var tool_image #字符串是全局文件系统中PNG文件的路径

export(String, DIR, GLOBAL) var tool_dir

export(String, MULTILINE) var text #多行字符串

export(int, 20) var i #值范围0-20

export(int, -10, 20) var j #值范围-10-20

export(float, -10, 20, 0.2) var k #值范围-10-20,步长0.2

export(float, EXP, 100, 1000, 20) var l #值在100-1000间按步长20变化,将显示滚动条

export(float, EASE) var transition_speed #显示ease()函数的可视化表示形式

export(Color, RGB) var col #RGB值

export(Color, RGBA) var col #RGBA值

export(NodePath) var node #场景中节点

Export(int,FLAGS) var spell/elements=ELEMENT/WIND|ELEMENT/WATER #单独编辑一个整数位

export(int, FLAGS, “Fire”, “Water”, “Earth”, “Wind”) var spell_elements = 0

export var a = [1, 2, 3] #数组

export(Array, int) var ints = [1,2,3]

set/get

var my_var setget my_var_set,my_var_get

func my_var_set(new_value):
   my_var = new_value

func my_var_get():
   return my_var

工具模式

tool
extends Button

func _ready():
    print("Hello")

信号

signal your_signal_name

signal your_signal_name_with_args(a,b)

func _callback_no_args():
    print("Got callback")

func _callback_args(a,b):
    print("a:",a,"b:",b)

func _at_some_func():
    instance.connect("your_signal_name",self,"_callback_no_args")

    instance.connect("your_signal_name_with_args",self,"_callback_args")

    instance.connect("your_signal_name", self, "_callback_args", [22, "hello"])  

识别调用对象

    func _button_pressed(which):  
        print("Button was pressed: ", which.get_name())  
      
    func _ready():  
        for b in get_node("buttons").get_children():  
            b.connect("pressed", self, "_button_pressed",[b])  

协程

func my_func():
    print("Hello")
    yield()
    print("world")

func _ready():
    var y = my_func()
    print("my dear")
    y.resume()

依次输入:Hello my dear world

func my_func():
     print("Hello")
     print(yield())
     return "cheers"

func _ready():
    var y = my_func()
    print(y.resume("world))

依次输出:Hello world cheers

协程和信号

yield(get_tree(),“idle_frame”) #继续执行下一帧

yield(get_node(“AnimationPlayer”),“finished”) #动画播放结束后继续执行

yield(get_tree().create_timer(5.0),“timeout”) #等待5秒后继续执行

func my_func():
    yield(button_func(),"completed")
    print("All buttons")

func button_func():
    yield($Button0,"pressed")
    yield($Button1,"pressed")

onready

onready var my_label = get_node(“MyLabel”) #延迟成员变量的初始化,直到调用ready

与此相同:

var my_label
func _ready():
    my_label = get_node("MyLabel")

assert

assert关键字可用于检查调试构建中的条件。这些断言在非调试构建中被忽略。

assert(i==0)

相关文章