概述

Godot本身的Array太过于宽松,并且只提供了通用的方法。

而在很多场景下,我们都会用到二维数组,而Array自身提供的通用方法在一些二维数组操作上并不便利,所以编写一个自定义类是非常有必要的。

Array2D就是专门针对二维数组的创建和操作而编写的,通过其自定义方法可以更便捷、多样和灵活的创建二维数组,并对其进行操纵。


本文贴出Array2D类的源码,以及具体的使用方法。

这个类目前只是一个初级版本,后续可能会继续改进和扩充。

Array2D类源码

# ========================================================
# 名称:Array2D
# 类型:自定义类
# 简介:二维数组类,专用于处理二维数组
# 作者:巽星石
# Godot版本:4.1.1-stable (official)
# 创建时间:2023-09-24 23:30:57
# 最后修改时间:202392521:38:07
# ========================================================

class_name Array2D

var data:Array

func _init(p_data:Array = []):
	data = p_data

# 返回指定行数和列数以及填充内容的二维数组
static func fill_rect(rows:int,cols:int,fill_content) -> Array2D:
	# 构造行
	var row = Array()
	row.resize(cols)
	row.fill(fill_content)
	# 构造二维数组
	var arr2 = Array()
	arr2.resize(rows)
	arr2.fill(row)
	return Array2D.new(arr2)

# 返回一个使用介于min到max之间的随机数整数填的二维数组
static func fill_rect_random(rows:int,cols:int,min:int,max:int) -> Array2D:
	# 构造一维数组
	var arr = []
	arr.resize(rows * cols)
	# 填充随机数
	arr = arr.map(func(num):
		return randi_range(min,max)
	)
	return Array2D.array_to_array2d(arr,cols)


# 控制台显示
func show() -> void:
	if data.is_empty():
		print(data)
	else:
		for row in data:
			print(row)

# 用内容填充整个二维数组
func fill(fill_content) -> void:
	for row in data:
		row.fill(fill_content)

# 用min到max之间的随机数填充当前二维数组
func random_fill_int(min:int,max:int):
	data = fill_rect_random(data.size(),data[0].size(),min,max).data

# 用内容填充整个二维数组边界
func fill_border(fill_content) -> void:
	for i in range(data.size()):
		if i == 0 or i == data.size() - 1:
			data[i] = data[i].map(func(num):
				return fill_content;
			)
			pass
		else:
			data[i][0] = fill_content
			data[i][data[i].size()-1] = fill_content

# 获取指定位置 - 上方 - 单元格值或无
func get_up(row:int,col:int):
	return get_cell(row-1,col)

# 获取指定位置 - 下方 - 单元格值或无
func get_down(row:int,col:int):
	return get_cell(row+1,col)

# 获取指定位置 - 左侧 - 单元格值或无
func get_left(row:int,col:int):
	return get_cell(row,col - 1)

# 获取指定位置 - 右侧 - 单元格值或无
func get_right(row:int,col:int):
	return get_cell(row,col + 1)

# 获取指定位置 - 上方 - 单元格值或无
func get_upv(pos:Vector2):
	return get_up(pos.x,pos.y)

# 获取指定位置 - 下方 - 单元格值或无
func get_downv(pos:Vector2):
	return get_cell(pos.x,pos.y)

# 获取指定位置 - 左侧 - 单元格值或无
func get_leftv(pos:Vector2):
	return get_cell(pos.x,pos.y)

# 获取指定位置 - 右侧 - 单元格值或无
func get_rightv(pos:Vector2):
	return get_cell(pos.x,pos.y)


# =================================== 获取/设定值 ===================================
# ----------------------------------------------------------------------
# 获取行
func get_row(index:int):
	return data[index]

# 设定行 - 用新的数组取代原来的行
func set_row(index:int,new_row:Array) -> void:
	data[index] = new_row

# 将行元素全部填充为fill_content
func fill_row(index:int,fill_content) -> void:
	var new_row = Array()
	new_row.fill(fill_content)
	data[index] = new_row

# 在指定位置插入新行
func insert_row(pos:int,new_row:Array) -> void:
	data.insert(pos,new_row)

# 在开头插入新行
func insert_row_start(new_row:Array) -> void:
	data.insert(0,new_row)

# 在末尾插入新行
func insert_row_end(new_row:Array) -> void:
	data.append(new_row)

# 在末尾添加行
func append_row(new_row:Array) -> void:
	data.append(new_row)

# 移除指定位置行
func remove_row(pos:int) -> void:
	data.remove_at(pos)

# 移除第一行
func remove_row_start() -> void:
	data.remove_at(0)

# 移除末尾行
func remove_row_end() -> void:
	data.remove_at(data.size()-1)

# 重置 - 不修改行列大小,所有元素重置为int_val(默认为null)
func reset(int_val = null) -> void:
	fill(int_val)

# 清除所有行 -- data.resize(0)
func clear() -> void:
	data.clear()

# 按行进行洗牌
func shuffle_with_rows() -> void:
	# 所有行自己进行洗牌
	for row in data:
		row.shuffle()
	# 对行顺序进行洗牌
	data.shuffle()

# 整体洗牌 
func shuffle() -> void:
	# 转为一维数组
	var arr1d = to_array()
	# 对所有元素进行洗牌
	arr1d.shuffle()
	# 再次转化为数组
	data = array_to_array2d(arr1d,data[0].size()).data

# Array --> Array2D
static func array_to_array2d(array:Array,num:int) -> Array2D:
	var arr = array.duplicate(true)
	var arr_back:Array[Array] = []
	while !arr.is_empty():
		var sub_arr:Array
		for i in range(num if num < arr.size() else arr.size()):
			sub_arr.append(arr[0])
			arr.remove_at(0)
		arr_back.append(sub_arr)
		
	return Array2D.new(arr_back)

# Array2D --> Array
func to_array() -> Array:
	var arr = data.duplicate(true)
	var arr1:Array = []
	if arr.size() >0:
		arr1 = arr[0] if typeof(arr[0]) == TYPE_ARRAY else [arr[0]]
		for i in range(1,arr.size()):
			arr1.append_array(arr[i] if typeof(arr[i]) == TYPE_ARRAY else [arr[i]])
	return arr1

# ----------------------------------------------------------------------
# 获取列
func get_col(index:int):
	var col = []
	for row in data:
		col.append(row[index])
	return col

# 设定列
func set_col(index:int,new_col:Array) -> void:
	for i in range(data.size()):
		data[i][index] = new_col[i]

# 将行元素全部填充为fill_content
func fill_col(index:int,fill_content) -> void:
	var new_col = Array()
	new_col.resize(data.size())
	new_col.fill(fill_content)
	set_col(index,new_col)

# ----------------------------------- 单元格 -----------------------------------
# 获取单元格的值
func get_cell(row_idx:int,col_idx:int):
	# 超出范围
	if row_idx not in range(data.size()) or col_idx not in range(data[0].size()-1):
		return null
	return data[row_idx][col_idx]

# 获取单元格的值 -- Vector2参数形式
func get_cellv(cellv:Vector2):
	return data[cellv.x][cellv.y]

# 设定单元格的值
func set_cell(row_idx:int,col_idx:int,new_val):
	data[row_idx][col_idx] = new_val

# 设定单元格的值 -- Vector2参数形式
func set_cellv(cellv:Vector2,new_val):
	data[cellv.x][cellv.y] = new_val


创建Array2D

使用普通二维数组字面量(嵌套的Array)创建

最简单的方式就是直接在new()中传入一个二维数组。

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])

new()会调用Array2D类的构造函数,也就是_init并将二维数组传递给Array2D实例的data属性。
实例化后后续的操作全在data上进行操作。

使用填充创建

静态方法fill_rect,可以创建一个几行×几列形式的二维数组,并对其填充指定的内容。

var arr2d = Array2D.fill_rect(10,10,1) # 10行×10列,填充元素1
arr2d.show()

输出:

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
var arr2d = Array2D.fill_rect(2,2,0) # 2行×2列,填充元素0
arr2d.show()

输出:

[0, 0]
[0, 0]

使用随机填充创建

var arr2d = Array2D.fill_rect_random(5,5,0,3) # 5行×5列,填充0-3之间的随机数
arr2d.show() 

输出:

[3, 1, 3, 3, 0]
[0, 2, 0, 2, 2]
[0, 3, 1, 2, 0]
[2, 0, 2, 2, 2]
[3, 2, 0, 3, 3]

在通过new()、fill_rect()或fill_rect_random()创建Array2D的实例后,随时可以使用
fill()、random_fill_int()方法来重新进行整体填充。

填充边缘

fill_border可以只对二维数组的“矩形边缘”进行填充。

var arr2d = Array2D.fill_rect(5,5,"□")
arr2d.fill_border("■") # 填充边缘
arr2d.show()

输出:

["■", "■", "■", "■", "■"]
["■", "□", "□", "□", "■"]
["■", "□", "□", "□", "■"]
["■", "□", "□", "□", "■"]
["■", "■", "■", "■", "■"]

获取单元格四向临近单元格的值

get_up、get_down、get_left、get_right以及他们末尾带v的形式get_upv、get_downv、get_leftv、get_rightv分别用于获取指定位置单元格的临近上下左右四个单元格的值。

var arr2d = Array2D.fill_rect(5,5,"□")
arr2d.fill_border("■")
print(arr2d.get_up(1,1)) # ■
print(arr2d.get_up(0,0)) # <null>
print(arr2d.get_upv(Vector2(1,1))) # ■
print(arr2d.get_upv(Vector2(0,0))) # <null>

行、列、单元格操作

添加行

var arr2d = Array2D.fill_rect(2,2,0) # 创建22,初始值为0的矩形二维数组
arr2d.insert_row(0,[1,1]) # 添加行
arr2d.show() # 控制台输出显示

等价写法insert_row_start([1,1]):

[1, 1]
[0, 0]
[0, 0]

insert_row_end([1,1])或append([1,1])将在末尾添加行。

删除行

var arr2d = Array2D.fill_rect(2,2,0) # 创建22,初始值为0的矩形二维数组
arr2d.remove_row(0) # 删除行,remove_row(0)等价于remove_row_start()
arr2d.show()        # 控制台输出显示

输出:

[0, 0]

remove_row_end()删除末尾行。

获取列

var arr2d = Array2D.fill_rect(2,2,0)
	
print(arr2d.get_col(0)) # 获取第一列

输出:

[0, 0]

设置列

var arr2d = Array2D.fill_rect(5,5,0)
arr2d.set_col(2,[1,1,1,1,1])
arr2d.show()

输出:

[0, 0, 1, 0, 0]
[0, 0, 1, 0, 0]
[0, 0, 1, 0, 0]
[0, 0, 1, 0, 0]
[0, 0, 1, 0, 0]

填充列

var arr2d = Array2D.fill_rect(5,5,0)
arr2d.fill_col(2,2)
arr2d.show()

输出:

[0, 0, 2, 0, 0]
[0, 0, 2, 0, 0]
[0, 0, 2, 0, 0]
[0, 0, 2, 0, 0]
[0, 0, 2, 0, 0]

获取单元格

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])
print(arr2d.get_cell(2,2)) # 13

get_cell(2,2)等价于get_cellv(Vector2(2,2))

设定单元格值

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])
arr2d.set_cell(2,2,0)
arr2d.show()

set_cell(2,2,0)等价于set_cellv(Vector2(2,2),0)

[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[11, 12, 0, 14, 15]
[16, 17, 18, 19, 20]

重置

var arr2d = Array2D.fill_rect(2,2,0) # 创建22,初始值为0的矩形二维数组
arr2d.reset() # 重置二维数组
arr2d.show()  # 控制台输出显示

输出:

[<null>, <null>]
[<null>, <null>]
arr2d.reset(0) # 重置二维数组
arr2d.show()  # 控制台输出显示

输出:

[0, 0]
[0, 0]

● reset()将保留二维数组的结构,把每一个元素变成null;
● reset()接受一个参数,可以将数组元素设定为非null;

清除

清除会删除所有行,让二维数组变成一维空数组。

var arr2d = Array2D.fill_rect(2,2,0) # 创建22,初始值为0的矩形二维数组
arr2d.clear() # 清除
arr2d.show()  # 控制台输出显示

输出:

[]

洗牌

二维数组的洗牌有两种思路:
● 每行各自洗牌,然后对行的顺序进行洗牌
● 将二维数组转为一维数组,洗牌后再转为二维数组

按行洗牌

先对每一行的元素进行顺序的打乱,再对所有行进行顺序的打乱。

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])
arr2d.shuffle_with_rows() # 按行进行洗牌
arr2d.show()

输出:

[15, 13, 11, 14, 12]
[4, 3, 2, 1, 5]
[6, 10, 7, 9, 8]
[19, 20, 16, 17, 18]

虽然也是对行和列都进行了打乱,但是每行内的元素都还是原来的那几个。

全部元素一起洗牌

首先将二维数组转为一维数组,调用Array原生的shuffle()方法,进行所有元素的打乱,然后再转化为二维数组。

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])
arr2d.shuffle() # 对所有元素洗牌
arr2d.show()

输出:

[2, 3, 10, 9, 4]
[8, 6, 5, 7, 13]
[20, 1, 12, 11, 16]
[14, 17, 19, 15, 18]

二维数组与一维数组互转

从一维数组构造二维数组

# 一维数组range(20),按每4个元素划分为二维数组
var arr2d = Array2D.array_to_array2d(range(20),4)
arr2d.show()

输出:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]

二维数组转一维数组

var arr2d = Array2D.new([
    [1,2,3,4,5],
    [6,7,8,9,10],
    [11,12,13,14,15],
    [16,17,18,19,20],
])
print(arr2d.to_array()) # 将二维数组转化为一维数组

输出:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Logo

一站式虚拟内容创作平台,激发创意,赋能创作,进入R空间,遇见同道,让优质作品闪耀发光。​

更多推荐