概述
GDScript的数组是一种很常用的数据类型。本文主要阐述一下GDScript数组分类,以及官方文档和大多数视频或教程较少提及的类型化数组和紧缩数组。
GDScript数组分类
通过反复查阅GDScript内置文档并进行细节比较,发现GDScript的数组,可以划分为三类:
- 普通数组:
Array
,不限定元素的类型,同一个数组可以存储不同类型的数据元素。 - 类型化数组:
Array[type]
,在Array
的基础上限定只能存储一种类型的数据元素。 - 紧缩数组:
Packed*Array
,数据紧密存放,在数组比较大时可以节省内存。包括:PackedInt32Array
、PackedInt64Array
、PackedFloat32Array
、PackedFloat64Array
、PackedStringArray
、PackedVector2Array
、PackedVector3Array
、PackedColorArray
、PackedByteArray
。其中PackedByteArray
代表二进制的字节数组,与其他紧缩数组在代表的意义和所提供的方法方面是完全不同的。
关于数组分类总结如下表:
分组 | 类 | 中文名称 | 类型(typeof) | 备注 |
---|---|---|---|---|
普通数组 | Array | 普通数组 | TYPE_ARRAY | 元素类型可不一致 |
类型化数组 | Array[type] | 类型化数组 | TYPE_ARRAY | 元素类型必须一致 |
紧缩数组 | PackedByteArray | 字节紧缩数组 | TYPE_PACKED_BYTE_ARRAY | 音频、图片等可能会以此形式存储 |
↑ | PackedInt32Array | 32位整数紧缩数组 | TYPE_PACKED_INT32_ARRAY | 紧缩数组中比较特殊的一位 |
↑ | PackedInt64Array | 64位整数紧缩数组 | TYPE_PACKED_INT64_ARRAY | 方法完全一样,但参数不一样 |
↑ | PackedFloat32Array | 32位浮点数紧缩数组 | TYPE_PACKED_FLOAT32_ARRAY | |
↑ | PackedFloat64Array | 64位浮点数紧缩数组 | TYPE_PACKED_FLOAT64_ARRAY | |
↑ | PackedStringArray | 字符串紧缩数组 | TYPE_PACKED_STRING_ARRAY | |
↑ | PackedVector2Array | Vector2紧缩数组 | TYPE_PACKED_VECTOR2_ARRAY | |
↑ | PackedVector3Array | Vector3紧缩数组 | TYPE_PACKED_VECTOR3_ARRAY | |
↑ | PackedColorArray | Color紧缩数组 | TYPE_PACKED_COLOR_ARRAY |
普通数组
GDScript普通数组的使用方法,请查阅:
【Godot4.2】普通数组Array使用全解析 一文。
类型化数组
- 类型化数组是在普通数组之上添加了一层限定,即限定了所存储的元素类型。
- 形式为
Array[type]
,其中type
可以是基础数据类型或者Object
及其子类型(包括各种Node
、Resource
等)。
申明与创建
可以用如下形式创建类型化数组:
var arr1:Array[int] # 申明一个元素都是int类型的数组
var arr2:Array[String] # 申明一个元素都是String类型的数组
var arr3:Array[Button] # 申明一个元素都是Button控件的数组
var arr4:Array[PackedScene] # 申明一个元素都是PackedScene的数组
可以看到,只需要对申明的数组变量用Array[type]
形式显式申明其类型即可。
其他的操作与普通Array
没有区别。
同时Array
提供了一个构造函数,可以用构造函数形式创建类型化数组。其形式如下:
Array(base:Array,type:int,class_name:StringName,script:Variant)
其中:
base
:是要被类型化的Array
数组,可以传入一个空数组。type
:指定元素的类型,基础数据类型int
、float
、String
等,以及内置或自定义的Node
、Resource
等class_name
:是具体的类名,比如Button
、PackedScene
等。script
:自定义节点类型或资源类型的脚本?(尚有疑问,谨待确认)
比如,申明一个PackedScene
类型作为元素的类型化数组,我们可以用常用形式:
var arr:Array[PackedScene] # 形式1
也可以用构造函数形式:
var arr = Array([],TYPE_OBJECT,"PackedScene",null) # 形式2
两者申明的类型化数组没有本质上的区别,只是后者提供了一种字符串参数形式创建的类型化数组的方法,在某些场景下可能用得到。
数组的类型化判断以及元素类型信息获取
Array
类型提供了几个方法用于判断数组是否是类型化数组,以及获取元素类信息等。
is_typed()
:判断一个数组是否是类型化的。
注意:Packed*Array
不算在Array
或者类型化数组范畴,也就没有is_typed()
等方法,所以不能用is_typed()
判断其是否类型化。
var arr1:Array[PackedScene]
var arr2 = []
print(arr1.is_typed()) # true
print(arr2.is_typed()) # flase
is_same_typed()
:判断两个类型化数组的类型是否相同。
var arr1:Array[PackedScene]
var arr2:Array[PackedScene]
var arr3:Array[int]
print(arr1.is_same_typed(arr2)) # true
print(arr1.is_same_typed(arr3)) # flase
get_typed_builtin()
:返回类型化的数组的内置类型(Variant.Type),如果是内置数据类型,返回相应的类型常量,如果是节点或资源类型,统一返回TYPE_OBJECT
(数值24
)
var arr1:Array[int]
var arr2:Array[Button]
var arr3:Array[PackedScene]
print(arr1.get_typed_builtin()) # 2 = TYPE_INT
print(arr2.get_typed_builtin()) # 24 = TYPE_OBJECT
print(arr3.get_typed_builtin()) # 24 = TYPE_OBJECT
get_typed_class_name()
返回类型化数组具体的元素类型名称,基础数据类型返回空字符串,节点或资源类型返回具体类名。
var arr1:Array[int]
var arr2:Array[Button]
var arr3:Array[PackedScene]
print(arr1.get_typed_class_name()) # ""
print(arr2.get_typed_class_name()) # "Button"
print(arr3.get_typed_class_name()) # "PackedScene"
get_typed_script()
:如果是自定义节点或资源类型,可以返回其脚本对象,内置数据类型或内置节点、资源类型,则返回<Object#null>
。
var arr:Array[PackedScene]
print(arr.get_typed_builtin()) # 24 = TYPE_OBJECT
print(arr.get_typed_class_name()) # "PackedScene"
print(arr.get_typed_script()) # <Object#null>
紧缩数组
Packed*Array
形式的数组类型被称为“紧缩数组”类型。包括:
类 | 中文名称 | 类型(typeof) |
---|---|---|
PackedByteArray | 字节紧缩数组 | TYPE_PACKED_BYTE_ARRAY |
PackedInt32Array | 32位整数紧缩数组 | TYPE_PACKED_INT32_ARRAY |
PackedInt64Array | 64位整数紧缩数组 | TYPE_PACKED_INT64_ARRAY |
PackedFloat32Array | 32位浮点数紧缩数组 | TYPE_PACKED_FLOAT32_ARRAY |
PackedFloat64Array | 64位浮点数紧缩数组 | TYPE_PACKED_FLOAT64_ARRAY |
PackedStringArray | 字符串紧缩数组 | TYPE_PACKED_STRING_ARRAY |
PackedVector2Array | Vector2紧缩数组 | TYPE_PACKED_VECTOR2_ARRAY |
PackedVector3Array | Vector3紧缩数组 | TYPE_PACKED_VECTOR3_ARRAY |
PackedColorArray | Color紧缩数组 | TYPE_PACKED_COLOR_ARRAY |
紧缩数组类型与普通数组、类型化数组的关系
Array
和Packed*Array
在内存中都是连续存放的,但是前者存放的是Variant
类型,Variant
类型固定占20字节,并且可以在其中存储几乎任何引擎数据类型。- 类型化数组是限定Array只能存储一种类型的数据元素,也就是将
Variant
限定为了具体的数据类型(比如说int
),但其Array的本质和用Variant
类型存储数据元素的本质没有变化。 - 而具体的
Packed*Array
,则使用相应的具体类型,而不是Variant
类型存储元素数据,所以相比类型化数组更加紧凑,所以相对普通Array
和类型化数组Array[type]
被称为紧缩数组。
字节紧缩数组(PackedByteArray)
字节紧缩数组PackedByteArray
算是比较特殊的一种紧缩数组,它的API和其他几种紧缩数组的不同,用途也不同
数组和紧缩数组间的转化关系
- 普通数组
Array
(包括类型化数组Array[type]
)可以通过Packed*Array(Array)
形式也就是相应的紧缩数组的构造函数形式转化为紧缩数组,其过程会对元素类型进行强制转换;
var arr1:Array = [1,"张三",Button.new()]
var pk_arr1 = PackedInt32Array(arr1)
var pk_arr2 = PackedFloat32Array(arr1)
var pk_arr3 = PackedStringArray(arr1)
var pk_arr4 = PackedByteArray(arr1)
print(arr1) # [1, "张三", <Button#7394641913158>]
print(pk_arr1) # [1, 0, 0]
print(pk_arr2) # [1, 0, 0]
print(pk_arr3) # ["1", "张三", "<Button#6977593879327>"]
print(pk_arr4) # [1, 0, 0]
var arr1:Array[Button] = [Button.new(),Button.new()]
var pk_arr1 = PackedInt32Array(arr1)
var pk_arr2 = PackedFloat32Array(arr1)
var pk_arr3 = PackedStringArray(arr1)
var pk_arr4 = PackedByteArray(arr1)
print(arr1) # [<Button#8468215966358>, <Button#8468266298058>]
print(pk_arr1) # [0, 0]
print(pk_arr2) # [0, 0]
print(pk_arr3) # ["<Button#8468215966358>", "<Button#8468266298058>"]
print(pk_arr4) # [0, 0]
Packed*Array(Array)
可以通过to_byte_array()
方法转换为PackedByteArray
;
var arr1:PackedInt32Array = [1,2,4,5,6,7,8]
var bt_arr1 = arr1.to_byte_array()
print(bt_arr1 is PackedByteArray) # true
PackedByteArray
则可以使用to_float32_array()
、to_float64_array()
、to_int32_array()
、to_int64_array()
转化为PackedFloat32Array
,PackedFloat64Array
,PackedInt32Array
和PackedInt64Array
;
普通数组转字节紧缩数组
var arr = ["张三","李四","王五"]
print(PackedByteArray(arr)) # [0, 0, 0]
var arr:PackedStringArray = ["张三","李四","王五"]
print(arr.to_byte_array()) # [208, 234, 206, 112, 47, 1, 0, 0, 192, 232, 206, 112, 47, 1, 0, 0, 128, 233, 206, 112, 47, 1, 0, 0]
可以看到普通的字符串数组用PackedByteArray()
构造函数直接转换是无效的。
而是应该将数组显示申明为PackedStringArray
类型,再调用to_byte_array()
方法转换为PackedByteArray
。
var arr = [1,2,3]
print(PackedByteArray(arr)) # [1,2,3]
var arr = [1.5,23.45,36]
print(PackedByteArray(arr)) # [1, 23, 36]
普通的纯数字数组通过PackedByteArray()
构造函数直接转换有效,但浮点数会被自动转化为整数(向下取整)。
提示:
直接用print()
方式打印PackedByteArray
其每个字节都显示为10进制数字,有时候不是太利于理解。
在《对数组的函数扩充》一文中,编写了相应的函数可以将10进制显示为2进制。下文中显示的二进制形式就是基于相应函数而来。
整形、浮点型紧缩数组转PackedByteArray
var arr1:PackedInt32Array = [1]
var arr2:PackedInt64Array = [1]
var arr3:PackedFloat32Array = [1.5]
var arr4:PackedFloat64Array = [1.5]
print(arr1.to_byte_array()) # [1, 0, 0, 0]
# [00000001,00000000,00000000,00000000]
print(arr2.to_byte_array()) # [1, 0, 0, 0, 0, 0, 0, 0]
# [00000001,00000000,00000000,00000000,00000000,00000000,00000000,00000000]
print(arr3.to_byte_array()) # [0, 0, 192, 63]
# [00000000,00000000,11000000,00111111]
print(arr4.to_byte_array()) # [0, 0, 0, 0, 0, 0, 248, 63]
# [00000000,00000000,00000000,00000000,00000000,00000000,11000000,00111111]
可以看到:
Int32
、Float32
占4字节,Int64
、Float64
占8字节。- 因此
PackedInt32Array
、PackedFloat32Array
的一个元素转为PackedByteArray
的4个元素,分别对应4个字节 - 反过来
PackedByteArray
转PackedInt32Array
、PackedFloat32Array
时,是将4个元素转化为1个整数
下图表示一个包含2个元素的PackedInt32Array
,在内存中的实际表示。而其每个字节组成的数组就可以看为其PackedByteArray
形式。
PackedStringArray转PackedByteArray
var arr1:PackedStringArray = ["a"]
var arr2:PackedStringArray = ["啊"]
var arr3:PackedStringArray = ["a","b","c"]
var arr4:PackedStringArray = ["啊","你好","张三"]
print(arr1.to_byte_array()) # [16, 34, 29, 73, 196, 2, 0, 0]
print(arr2.to_byte_array()) # [80, 27, 29, 73, 196, 2, 0, 0]
print(arr3.to_byte_array())
# [16, 34, 29, 73, 196, 2, 0, 0, 144, 90, 29, 73, 196, 2, 0, 0, 208, 35, 29, 73, 196, 2, 0, 0]
print(arr4.to_byte_array())
# [80, 27, 29, 73, 196, 2, 0, 0, 128, 119, 41, 72, 196, 2, 0, 0, 0, 121, 41, 72, 196, 2, 0, 0]
print("a".to_ascii_buffer()) # [97]
print("a".to_utf8_buffer()) # [97]
print("a".to_utf16_buffer()) # [97, 0]
print("a".to_utf32_buffer()) # [97, 0, 0, 0]
print("a".to_wchar_buffer()) # [97, 0]
var string = "张三"
var byte = string.to_utf8_buffer()
print(byte) # [229, 188, 160, 228, 184, 137]
print(slices_arr(byte,3)) # [[229, 188, 160], [228, 184, 137]]
print(show_byte_array_string(byte,3))
# ["11100101", "10111100", "10100000"],
# ["11100100", "10111000", "10001001"]
可以看到PackedStringArray
转为PackedByteArray
后,存储的并不是字符串本身的二进制编码形式,而是指向字符转在内存中的地址(地址占8个字节)。
PackedColorArray --> PackedByteArray
Color以RGBA形式构造,每一个分量为一个Float32,则在PackedByteArray中一个颜色是由四组四个字节的形式表示。
var arr:PackedColorArray = [Color.YELLOW]
var byte = arr.to_byte_array()
print(byte) # [0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 128, 63]
print(slices_arr(byte,4))
# [[0, 0, 128, 63], [0, 0, 128, 63], [0, 0, 0, 0], [0, 0, 128, 63]]
# [r,g,b,a]
print(show_byte_array_string(byte,4))
# ["00000000", "00000000", "10000000", "00111111"], # R
# ["00000000", "00000000", "10000000", "00111111"], # G
# ["00000000", "00000000", "00000000", "00000000"], # B
# ["00000000", "00000000", "10000000", "00111111"] # A
PackedVector2Array/PackedVector3Array --> PackedByteArray
var arr:PackedVector2Array = [Vector2(100,100)]
var byte = arr.to_byte_array()
print(byte) # [0, 0, 200, 66, 0, 0, 200, 66]
print(slices_arr(byte,4)) # [[0, 0, 200, 66], [0, 0, 200, 66]]
print(show_byte_array_string(byte,4))
# ["00000000", "00000000", "11001000", "01000010"], # X
# ["00000000", "00000000", "11001000", "01000010"] # Y
var arr:PackedVector3Array = [Vector3(100,100,100)]
var byte = arr.to_byte_array()
print(byte) # [0, 0, 200, 66, 0, 0, 200, 66, 0, 0, 200, 66]
print(slices_arr(byte,4)) # [[0, 0, 200, 66], [0, 0, 200, 66], [0, 0, 200, 66]]
print(show_byte_array_string(byte,4))
# ["00000000", "00000000", "11001000", "01000010"], # X
# ["00000000", "00000000", "11001000", "01000010"], # Y
# ["00000000", "00000000", "11001000", "01000010"] # Z
基础数据类型与二进制形式转换
字符串转二进制
print("a".to_ascii_buffer()) # [97]
print("a".to_utf8_buffer()) # [97]
print("a".to_utf16_buffer()) # [97, 0]
print("a".to_utf32_buffer()) # [97, 0, 0, 0]
print("a".to_wchar_buffer()) # [97, 0]