Unity地牢生成:如何在多GameObject上调用独立脚本的RayCheck函数?
Alright, let's break down how to implement your outer room spawning with the raycast check, plus clean up your existing dungeon generation code to make it more maintainable.
一、实现外侧房间生成(调用DungeonRaycastCheck.RayCheck)
First, we need to track all spawned rooms so we can check their edges later, then integrate the raycast check to fill in outer rooms where there's empty space.
1. 跟踪所有生成的房间
Add a list to your Dungeon script to keep references to every room you spawn—this lets us check for existing rooms when spawning outer ones:
using UnityEngine; using System.Collections.Generic; // 别忘了引入List命名空间 public class Dungeon : MonoBehaviour { // ... 你的其他变量 ... public List<GameObject> spawnedRooms = new List<GameObject>(); // 新增:存储所有生成的房间 private const float RoomSpacing = 10f; // 新增:用常量定义房间间距,方便统一修改 void startTile() { dungeonRoomsCount++; Vector2 startPos = new Vector2(RoomPos.x, RoomPos.y); var startRoom = Instantiate(Room0_4x4, startPos, Quaternion.identity); spawnedRooms.Add(startRoom); // 将初始房间加入跟踪列表 } // 其他生成房间的方法同理,每次Instantiate后都要把返回的房间添加到spawnedRooms中 }
2. 调用RayCheck生成外侧房间
在初始地牢生成循环完成后,遍历所有已生成的房间,触发射线检查来生成外侧边缘房间:
void Start() { startTile(); for (; dungeonRoomsCount < MaxDungeonRoomCount ; dungeonRoomsCount++ ) { RoomDirection randomDir = (RoomDirection)Random.Range(0, 4); switch(randomDir) { case RoomDirection.Up: dungeonUp(); break; case RoomDirection.Down: dungeonDown(); break; case RoomDirection.Left: dungeonLeft(); break; case RoomDirection.Right: dungeonRight(); break; } } print("done Generation"); // 新增:遍历所有房间,生成外侧边缘房间 foreach(var room in spawnedRooms) { RayCheck.RayCheck(room); } }
3. 实现DungeonRaycastCheck.RayCheck功能
你的DungeonRaycastCheck脚本需要检查每个房间的四个方向,如果对应位置没有房间,就生成外侧边缘房间:
using UnityEngine; using System.Linq; // 引入Linq用于Any()检查 public class DungeonRaycastCheck : MonoBehaviour { public Dungeon dungeon; // 引用主Dungeon脚本获取预制体和房间列表 private const float RoomSpacing = 10f; // 传入要检查的房间,生成外侧房间 public void RayCheck(GameObject room) { Vector2 roomPos = room.transform.position; // 检查四个方向:上、下、左、右 CheckAndSpawnOuterRoom(roomPos + Vector2.up * RoomSpacing, RoomDirection.Up); CheckAndSpawnOuterRoom(roomPos + Vector2.down * RoomSpacing, RoomDirection.Down); CheckAndSpawnOuterRoom(roomPos + Vector2.left * RoomSpacing, RoomDirection.Left); CheckAndSpawnOuterRoom(roomPos + Vector2.right * RoomSpacing, RoomDirection.Right); } // 检查目标位置是否有房间,没有则生成外侧房间 private void CheckAndSpawnOuterRoom(Vector2 targetPos, RoomDirection direction) { // 检查目标位置是否已经存在房间(用距离判断避免浮点误差) bool hasExistingRoom = dungeon.spawnedRooms.Any(r => Vector2.Distance(r.transform.position, targetPos) < 0.1f); if (!hasExistingRoom) { GameObject outerRoomPrefab = GetOuterRoomPrefab(direction); if (outerRoomPrefab != null) { var newOuterRoom = Instantiate(outerRoomPrefab, targetPos, Quaternion.identity); dungeon.spawnedRooms.Add(newOuterRoom); // 加入跟踪列表 } } } // 根据方向返回对应的外侧房间预制体(你可以自定义规则) private GameObject GetOuterRoomPrefab(RoomDirection direction) { // 示例:假设你有专门的边缘房间预制体(比如只有一侧开口的房间) switch(direction) { case RoomDirection.Up: return dungeon.RoomEdgeUp; // 需要在Dungeon脚本中添加这个预制体引用 case RoomDirection.Down: return dungeon.RoomEdgeDown; case RoomDirection.Left: return dungeon.RoomEdgeLeft; case RoomDirection.Right: return dungeon.RoomEdgeRight; default: return dungeon.Room1_4x4; // fallback到普通房间 } } }
记得在Inspector中给Dungeon脚本添加外侧房间预制体的引用哦!
二、代码优化建议
你的当前代码存在不少重复逻辑和硬编码值,这里提供几个优化方向:
1. 使用枚举替代魔法数字
定义房间方向的枚举,让代码更易读、更不容易出错:
public enum RoomDirection { Up, Down, Left, Right }
之后所有用到0/1/2/3表示方向的地方,都替换成RoomDirection.Up这类枚举值。
2. 提取公共生成方法
避免在每个方向方法中重复写实例化逻辑,创建一个统一的生成方法:
private void SpawnRoomAtPosition(Vector2 spawnPos, RoomDirection direction) { int roomId = Random.Range(0, 2); GameObject selectedPrefab = GetRoomPrefab(roomId, direction); if (selectedPrefab != null) { var newRoom = Instantiate(selectedPrefab, spawnPos, Quaternion.identity); spawnedRooms.Add(newRoom); RoomPos = spawnPos; // 更新当前生成位置 } } // 根据方向和roomId选择对应的预制体 private GameObject GetRoomPrefab(int roomId, RoomDirection direction) { switch(direction) { case RoomDirection.Up: return roomId == 0 ? Room1_4x4 : Room2_3x3Up; case RoomDirection.Down: case RoomDirection.Left: case RoomDirection.Right: return Room1_4x4; // 目前这些方向只有Room1_4x4可选 default: return Room1_4x4; } }
现在你的方向方法会变得非常简洁:
public void dungeonUp() { print("Room-Up"); Vector2 newPos = RoomPos + Vector2.up * RoomSpacing; SpawnRoomAtPosition(newPos, RoomDirection.Up); } public void dungeonDown() { print("Room-Down"); Vector2 newPos = RoomPos + Vector2.down * RoomSpacing; SpawnRoomAtPosition(newPos, RoomDirection.Down); }
3. 避免在Instantiate中修改RoomPos
你原来的代码有类似new Vector2(RoomPos.x, (RoomPos.y = RoomPos.y + 10))的写法,可读性很差。建议先计算新位置,再单独赋值给RoomPos。
4. 添加房间父物体
给所有生成的房间添加一个父物体,让场景层级更整洁:
void Start() { GameObject dungeonRoomsParent = new GameObject("DungeonRooms"); // 创建父物体 startTile(dungeonRoomsParent); // 修改startTile接受父物体参数 // ... 其他生成代码 ... } void startTile(GameObject parent) { dungeonRoomsCount++; Vector2 startPos = new Vector2(RoomPos.x, RoomPos.y); var startRoom = Instantiate(Room0_4x4, startPos, Quaternion.identity, parent.transform); spawnedRooms.Add(startRoom); } // 同时修改SpawnRoomAtPosition方法,添加父物体参数 private void SpawnRoomAtPosition(Vector2 spawnPos, RoomDirection direction, GameObject parent) { // ... 生成逻辑 ... var newRoom = Instantiate(selectedPrefab, spawnPos, Quaternion.identity, parent.transform); // ... }
5. 移除无效随机性
在dungeonDown这类方法中,你生成了随机的roomId但两个分支用的是同一个预制体——可以去掉这种无效随机,或者后续添加不同的预制体选项让随机有实际意义。
内容的提问来源于stack exchange,提问作者Dayskie

