【Godot】给不规则的 TileMap 划分子区域块部分代码

4.1.beta2

给不规则的 TileMap 划分子区域块部分代码

## 生成房间内部数据
func generate_room_inside_data():
	GLog.info("生成房间内部数据")
	# 划分房间的各个矩形块
	for room_coords in room_coords_to_data:
		var room_data = room_coords_to_data[room_coords] as Dictionary
		var edge_coords_set = room_data["edge_coords_set"] as HashSet
		# 获取外部的空白点,用于排除筛选出内部空白点
		var outer_empty_coords_set = room_data["outer_empty_coords_set"] as HashSet
		
		# 找到内部第一个空白起始点
		var start_coords = null 
		var edge_coors_list = edge_coords_set.to_array() 
		edge_coors_list.shuffle()
		for edge_coords in edge_coors_list:
			for direction in FOUR_DIRECTIONS:
				var tmp_coords = edge_coords + direction
				if (not edge_coords_set.has(tmp_coords)
					and not outer_empty_coords_set.has(tmp_coords)
					and tile_map.get_cell_source_id(TILE_LAYER.WALL, tmp_coords) == SOURCE_ID.NONE
				):
					start_coords = tmp_coords
					break
			if typeof(start_coords) == TYPE_VECTOR2I:
				break
		assert(typeof(start_coords) == TYPE_VECTOR2I, "没有找到可用的点,代码逻辑有误!")
		
		# 广度搜索所有内部通路
		var inside_empty_coords_set = HashSet.new()
		var room_rect = room_data["rect"] as Rect2i
		var visited = {}
		var tmp_coords
		var last_coords_list = [start_coords]
		while not last_coords_list.is_empty():
			var next_coords_list = []
			for coords in last_coords_list:
				for direction in FOUR_DIRECTIONS:
					tmp_coords = coords + direction
					if (
						not visited.has(tmp_coords)
						and room_rect.has_point(tmp_coords)
						and tile_map.get_cell_source_id(TILE_LAYER.WALL, tmp_coords) == SOURCE_ID.NONE
					):
						visited[tmp_coords] = null
						next_coords_list.append(tmp_coords)
						inside_empty_coords_set.append(tmp_coords)
			last_coords_list = next_coords_list
			next_coords_list = []
		# 记录内部空白瓦片数据
		room_data["inside_empty_coords_set"] = inside_empty_coords_set
		
		# 根据内部空白点,获取每个拐角处的整个矩形
		var inside_empty_corner_coords_list = Array()
		for coords in inside_empty_coords_set:
			if is_empty_corners(coords, tile_map):
				inside_empty_corner_coords_list.append(coords)
		
		# 对房间进行局部矩形细分
		var added_coords_set = HashSet.new()
		var block_rect_list = []
		for empty_coords in inside_empty_corner_coords_list:
			# TEST 测试显示角落点
			#tile_map.set_cell(TILE_LAYER.TEST, coords, 0, Vector2i(0,0), ALTERNATIVE_TILE_ID.ASTAR_PATH)
			
			# 添加过这个小块矩形四个点包含这个坐标时则跳过
			if added_coords_set.has(empty_coords):
				 # FIXME 会有两边都有形,但中间有个障碍物,导致左右坐标全部被占
				# 进而导致另外两个矩形无法生成出来
				continue
			
			# 拐角只有两个方向
			var empty_coords_directions = get_around_empty_coords_directions(empty_coords, tile_map)
			
			for dir_idx in empty_coords_directions.size():
				# 朝这个拐角的另一方向移动,直到对面墙角,作为矩形一边的线
				var direction = empty_coords_directions[dir_idx] 
				var moved_line_coords_list = [empty_coords]
				var current_coords = empty_coords
				
				while true:
					current_coords = current_coords + direction
					if (tile_map.get_cell_source_id(TILE_LAYER.WALL, current_coords) == SOURCE_ID.NONE
						and tile_map.get_cell_source_id(TILE_LAYER.ENV, current_coords) == SOURCE_ID.NONE
					):
						moved_line_coords_list.append(current_coords)
					else:
						break
				
				# 超过这个移动步数长度才认为是矩形
				const MAX_STEP = 2
				if moved_line_coords_list.size() >= MAX_STEP:
					# 矩形左上角的坐标点
					var move_start_coords = moved_line_coords_list[0]
					var arrive_coords
					
					# 使用移动的路径线的点,进行平移推进,找到这个局部的矩形
					var move_direction = empty_coords_directions[1 if dir_idx == 0 else 0]
					while true:
						MathUtil.offset_origin_array(moved_line_coords_list, move_direction)
						for line_coords in moved_line_coords_list:
							# 如果移动到的位置有点不是空白区域点,则到达一个墙壁
							if not inside_empty_coords_set.has(line_coords):
								arrive_coords = moved_line_coords_list[moved_line_coords_list.size() - 1]
								# 回退一步
								arrive_coords -= move_direction
								break
						if arrive_coords is Vector2i:
							break
					
					# 这个小块区域大小
					var size = (arrive_coords - move_start_coords).abs()
					# 获取左上角位置
					var left_top_coords = move_start_coords
					if left_top_coords.x > arrive_coords.x:
						left_top_coords.x = arrive_coords.x
					if left_top_coords.y > arrive_coords.y:
						left_top_coords.y = arrive_coords.y 
					var block_rect = Rect2i(left_top_coords, size)
					# 必须超过步数大小(大小为 0 代表只有一行或一列)
					if size.x >= MAX_STEP-1 and size.y >= MAX_STEP-1:
						block_rect_list.append(block_rect)
						
						# 记录小块区域的四周坐标点,后续不再对其进行这个操作
						added_coords_set.append(block_rect.position) # 左上角
						added_coords_set.append(block_rect.end) # 右下角
						added_coords_set.append(Vector2i(block_rect.position.x, block_rect.end.y)) # 左下角 
						added_coords_set.append(Vector2i(block_rect.end.x, block_rect.position.y)) # 右上角
	
		room_data["block_rect_list"] = block_rect_list
		
		# TEST 测试显示小块划分区域的矩形
		#for block_rect in block_rect_list:
			#FuncUtil.for_rect_around(block_rect, func(coords: Vector2i):
				#tile_map.set_cell(TILE_LAYER.TEST, coords, 0, Vector2i(0,0), ALTERNATIVE_TILE_ID.ASTAR_PATH)
			#)


## 获取周围白点坐标方向。如果是拐角( is_empty_corners 返回 true )时,则只有个方向
func get_around_empty_coords_directions(coords: Vector2i, tile_map: TileMap) -> Array:
	var dirs = []
	for direction in FOUR_DIRECTIONS:
		if tile_map.get_cell_source_id(TILE_LAYER.WALL, coords + direction) == SOURCE_ID.NONE:
			dirs.append(direction)
	return dirs


你可能感兴趣的:(Godot,godot,gdscript,游戏引擎,游戏)