一、原视频
3. Preventing Invalid Spawning
二、原代码
func get_spawn_position():var player = get_tree().get_first_node_in_group("player") as Node2Dif player == null:return Vector2.ZEROvar spawn_position = Vector2.ZEROvar random_direction = Vector2.RIGHT.rotated(randf_range(0, TAU))for i in 4:spawn_position = player.global_position + ( random_direction * SPAWN_RADIUS )var query_parameters = PhysicsRayQueryParameters2D.create(player.global_position, spawn_position, 1)var result: Dictionary = get_tree().root.world_2d.direct_space_state.intersect_ray(query_parameters)if result.is_empty():# 射线检测结果中没有碰撞breakelse:# 射线检测结果中有碰撞random_direction = random_direction.rotated(deg_to_rad(90))return spawn_position
三、修复代码
注:解决方法有很多,但是这个只会修改get_spawn_position本身,以免把项目改得乱糟糟
const ENEMY_SPAWN_WALL_CLEARANCE = 36 # 这是墙的厚度,让敌人生成时不要卡墙里面,直角边32和直角边16的斜边向上取整就是36
func get_spawn_position_v2():var player = get_tree().get_first_node_in_group("player") as Node2Dif player == null:return Vector2.ZEROvar player_pos = player.global_positionvar RayQueryParams2D = PhysicsRayQueryParameters2Dvar spawn_position = Vector2.ZEROvar original_direction = Vector2.RIGHT.rotated(randf_range(0, TAU))spawn_position = player_pos + (original_direction * SPAWN_RADIUS)var original_query_parameters = RayQueryParams2D.create(player_pos, spawn_position, 1)var original_raycast_result: Dictionary = get_tree().root.world_2d.direct_space_state.intersect_ray(original_query_parameters)var direction_to_player_pos = (player_pos - spawn_position).normalized()spawn_position = spawn_position + (direction_to_player_pos * ENEMY_SPAWN_WALL_CLEARANCE)if not original_raycast_result.is_empty():# 射线检测结果中有碰撞# 遇到这种情况,说明SPAWN_RADIUS不适配你的围墙尺寸,(偏移90°, 180°, 270°)全都算完一起做比对var wall_positions: Array[Vector2] = []wall_positions.append(original_raycast_result["position"])# 准备3个可能的方向(原始方向 + 90°, 180°, 270°)var directions = [original_direction.rotated(deg_to_rad(90)),original_direction.rotated(deg_to_rad(180)),original_direction.rotated(deg_to_rad(270))]# 对每个方向进行射线检测for dir in directions:var test_position = player_pos + (dir * SPAWN_RADIUS)var query_parameters = RayQueryParams2D.create(player_pos, test_position, 1)var raycast_result = get_tree().root.world_2d.direct_space_state.intersect_ray(query_parameters)if raycast_result.is_empty():wall_positions.append(test_position)else:wall_positions.append(raycast_result["position"])# 找到距离玩家最远的点var farthest_pos = wall_positions[0]for pos in wall_positions:if pos.distance_squared_to(player_pos) > farthest_pos.distance_squared_to(player_pos):farthest_pos = pos# 将这个点向玩家附近的重心点的方向移动36# (该重心点受地图碰撞影响,不会紧贴地图边缘)# (36是墙厚度,避免卡墙里)var centroid_pos = calculate_centroid(player_pos, 1)var direction_to_centroid_pos = (centroid_pos - farthest_pos).normalized()spawn_position = farthest_pos + (direction_to_centroid_pos * ENEMY_SPAWN_WALL_CLEARANCE)return spawn_position# 给定坐标为中心,均匀向四周发出 9 条射线,计算这 9 个碰撞点的质心
func calculate_centroid(position: Vector2, mask: int) -> Vector2:var points = []var ray_length = 300var angle_step = 2 * PI / 8 # 将 360 度(即 2π 弧度)均分为 8 份,得到每份的角度for i in range(9):var angle = i * angle_stepvar ray_direction = Vector2.RIGHT.rotated(angle) # 生成射线方向var ray_end = position + ray_direction * ray_length # 计算射线终点var query_parameters = PhysicsRayQueryParameters2D.create(position, ray_end, mask)var result = get_tree().root.world_2d.direct_space_state.intersect_ray(query_parameters)if result.is_empty():# 如果没有碰撞,添加射线终点points.append(ray_end)else:points.append(result["position"]) # 添加碰撞点# 计算质心var centroid = Vector2.ZEROfor p in points:centroid += pcentroid /= points.size()return centroid