Godot4.3 多线程动态TileMapLayer地图加载

实现原理

将加载的区域按特定范围(本教程以3X3为例)作为一个区块,一个区块创建一个TileMapLayer,并以一条线程负责生成地图块

将多个(本教程以9x9为例)区块(即多个TileMapLayer)叠放后做为一张完整区域地图,最终实现27X27范围的地图块加载

在玩家移动的过程中,计算玩家坐标所处的格子(玩家坐标 / 格子大小),在通过格子的坐标,计算玩家所处的区块(格子坐标 / 区块大小)是否发生变化

实现过程

创建一个资源,选择TileSet,并设置好地图块

创建一个脚本,名为Chunk.dg,所有区块都将使用此脚本,脚本继承自TileMapLayer

完善负责更新图块的方法update_chunk,按噪声生成随机的地形,线程结束后将缓存的数据设置到当前的TileMapLayer

在_exit_tree中等待线程完成,到这一步,一个3*3区块的线程加载就完成了,现在需要在父节点生成9*9共81个此类节点,并动态添加

将携带此脚本的节点拖拽到文件系统,生成一个单独的场景

在父节点添加脚本

在每一帧,检测玩家的坐标,计算出图标的坐标,在计算出区块的坐标,循环9*9的区块,将81个区块坐标保存,在判断多出来的区块,和缺少的区块,多出来的释放掉,缺少的创建出来

最终效果:

附上完整代码:

class_name Chunk extends TileMapLayer

# 区块加载的图块大小
var chunk_size : Vector2 = Vector2(3,3)
# 区块的坐标
var chunk_coord : Vector2
# 线程ID
var task_id : int
# TileMapLayer缓存
var buffer_layer : TileMapLayer = TileMapLayer.new()
# TileSet
var tileset = preload("res://new_tile_set.tres")
# 生成随机地形的噪声
var noise : FastNoiseLite

func _ready() -> void:
	# 设置图块集
	buffer_layer.tile_set = tileset
	# 启动线程加载数据
	task_id = WorkerThreadPool.add_task(update_chunk)

# 更新区块
func update_chunk():
	# 计算结束图块的位置
	var start_coord = chunk_coord * chunk_size
	var end_coord = start_coord + chunk_size
	# 遍历3*3的范围创建图块
	for x in range(start_coord.x,end_coord.x):
		for y in range(start_coord.y,end_coord.y):
			# 通过噪声获取海拔
			var h = noise.get_noise_2d(x,y)
			if h < 0:
				# 创建水面
				buffer_layer.set_cell(Vector2i(x,y),0,Vector2i(2,0))
			elif h < 0.1:
				# 创建沙滩
				buffer_layer.set_cell(Vector2i(x,y),0,Vector2i(0,1))
			else:
				# 创建草地
				buffer_layer.set_cell(Vector2i(x,y),0,Vector2i(1,0))
	# 从buffer获取数据
	var data = buffer_layer.tile_map_data
	# 空闲时更新数据
	call_deferred("update_finished",data)

# 线程处理完毕后
func update_finished(data):
	tile_map_data = data

# 在退出事件中,等待线程结束
func _exit_tree() -> void:
	WorkerThreadPool.wait_for_task_completion(task_id)
	
extends Node2D

# 图块大小
var cell_size = Vector2(16,16)
# 加载的区块大小
var size : Vector2 = Vector2(9,9)
# 一个区块的大小
var once_chunk_size : Vector2 = Vector2(3,3)
# 玩家
@onready var player : CharacterBody2D = $"../CharacterBody2D"
# 玩家当前所处区块
var current_chunk : Vector2
# 所有区块的字典
var chunks = {}
# 预加载场景
var chunk_scene:PackedScene = preload("res://Chunk.tscn")
# 海拔噪声
var noise = FastNoiseLite.new()
# 强制更新
var update = true

func _ready():
	noise.seed = randi()

func _process(delta: float) -> void:
	# 取地图快坐标
	var cell_coord = (player.global_position /  cell_size).floor()
	# 取区块坐标
	var chunk_coord = (cell_coord  / once_chunk_size).floor()
	# 区块坐标发生变化,或者强制更新
	if chunk_coord != current_chunk or update:
		update = false
		# 更新当前区块
		current_chunk = chunk_coord
		# 开始的区块,中心点坐标需要偏移一半
		var start_chunk =  current_chunk - (size / 2).floor()
		var end_chunk = start_chunk + size
		# 加载的区块
		var load = []
		# 把所有的图块坐标加入到数组
		for x in range(start_chunk.x,end_chunk.x):
			for y in range(start_chunk.y,end_chunk.y):
				load.append(Vector2(x,y))
		# 卸载的区域,遍历已经加载的区块
		for coord in chunks.keys():
			if !load.has(coord):
				# 卸载
				(chunks[coord] as TileMapLayer).queue_free()
				chunks.erase(coord)
		# 加载的区域
		for coord in load:
			if !chunks.has(coord):
				#加载,创建节点,设置坐标,大小,噪声,保存到字典,添加到节点
				var layer:Chunk = chunk_scene.instantiate()
				layer.chunk_coord = coord
				layer.chunk_size = once_chunk_size
				layer.noise = noise
				chunks[coord] = layer
				add_child(layer)
				

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值