生成一个用于2D半程序化地牢爬行游戏(Unity)的运行时导航网格。

huangapple go评论58阅读模式
英文:

Generating a Navigation Mesh at runtime for a 2d semi-procedural dungeon crawler(Unity)

问题

I have translated the code portions you provided. Please find the translated code below:

Room Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Room : MonoBehaviour
{
    public int Width;
    public int Height;
    public int X;
    public int Y;

    private bool updatedDoors = false;

    public Room(int x, int y)
    {
        X = x;
        Y = y;
    }

    public Door leftDoor;
    public Door rightDoor;
    public Door topDoor;
    public Door bottomDoor;
    public GameObject replaceWallTop;
    public GameObject replaceWallBottom;
    public GameObject replaceWallLeft;
    public GameObject replaceWallRight;
    public List<Door> doors = new List<Door>();

    void Start()
    {
        if (RoomController.instance == null)
        {
            Debug.Log("You pressed play in the wrong scene!");
            return;
        }

        Door[] ds = GetComponentsInChildren<Door>();
        foreach (Door d in ds)
        {
            doors.Add(d);
            switch (d.doorType)
            {
                case Door.DoorType.right:
                    rightDoor = d;
                    break;
                case Door.DoorType.left:
                    leftDoor = d;
                    break;
                case Door.DoorType.top:
                    topDoor = d;
                    break;
                case Door.DoorType.bottom:
                    bottomDoor = d;
                    break;
            }
        }

        RoomController.instance.RegisterRoom(this);
    }

    void Update()
    {
        if (name.Contains("End") && !updatedDoors)
        {
            RemoveUnconnectedDoors();
            updatedDoors = true;
        }
    }

    // Other methods and functions remain unchanged and are not translated.
}

RoomController Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Linq;

public class RoomInfo
{
    public string name;
    public int X;
    public int Y;
}

public class RoomController : MonoBehaviour
{
    // Code translation for RoomController is too lengthy to display here. Please let me know if you need it.
}

DungeonGenerator Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DungeonGenerator : MonoBehaviour
{
    public DungeonGenerationData dungeonGenerationData;
    private List<Vector2Int> dungeonRooms;
    public Room startingRoomPrefab;
    public Room bossRoomPrefab;
    public Room[] roomPrefabs;

    void Start()
    {
        dungeonRooms = DungeonCrawlerController.GenerateDungeon(dungeonGenerationData);
        SpawnRooms(dungeonRooms);
    }

    private void SpawnRooms(IEnumerable<Vector2Int> rooms)
    {
        // Translation for SpawnRooms function is too lengthy to display here. Please let me know if you need it.
    }
}

Please note that due to the length and complexity of the code, I may have omitted some details during translation. If you have specific questions or need further assistance with any part of the code, please let me know.

英文:

For the past few months, I have been creating a 2D dungeon crawler similar to the Binding of Isaac and Enter the Gungeon, with a bit of inspiration from older Zelda games. Without getting into too much detail, the main scene which houses the player, UI, and some other objects such as a Game Controller, Room Controller, etc. When pressing play, the main scene gets populated with "rooms" which are their own seperate scenes. These scenes contain the tilemap for the room, door handlers to open and close the doors based on enemy state, and an ObjectRoomSpawner which deals with spawning the enemies/spawnables such as healthpots onto the room. Up until now, I've been using a simple moveTowards function for the enemy, but it makes the gameplay feel a bit boring.

My question is, how might I bake a nav mesh at runtime for my scenario? While it's not really generated procedrually, each instance of the game will have a different dungeon layout from a set number of rooms I've created then added to the RoomController. Everything I've tried so far hasn't worked out. The farthest I've managed to get done is generating the nav mesh for the first instance of each room, but any duplicate of that room throughout the dungeon doesn't have. Some of the things I've tried are creating a NavMesh GameObject in each 'room' scene then adding the modifier component to each tilemap under the grid for the room. I've also tried creating a GameObject in the main scene, and using the tutorial page Unity has on NavMesh baking at runtime, but to no avail. Hopefully someone can help point me in the right direction. Below is my Room and RoomController scripts.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Room : MonoBehaviour
{

    public int Width;
    public int Height;
    public int X;
    public int Y;
    
    private bool updatedDoors = false;

    public Room(int x, int y)
    {
        X = x;
        Y = y;
    }

    public Door leftDoor;
    public Door rightDoor;
    public Door topDoor;
    public Door bottomDoor;
    public GameObject replaceWallTop;
    public GameObject replaceWallBottom;
    public GameObject replaceWallLeft;
    public GameObject replaceWallRight;
    public List&lt;Door&gt; doors = new List&lt;Door&gt;();

    // Start is called before the first frame update
    void Start()
    {
        if (RoomController.instance == null)
        {
            Debug.Log(&quot;You pressed play in the wrong scene!&quot;);
            return;
        }

        Door[] ds = GetComponentsInChildren&lt;Door&gt;();
        foreach (Door d in ds)
        {
            doors.Add(d);
            switch (d.doorType)
            {
                case Door.DoorType.right:
                    rightDoor = d;
                    break;
                case Door.DoorType.left:
                    leftDoor = d;
                    break;
                case Door.DoorType.top:
                    topDoor = d;
                    break;
                case Door.DoorType.bottom:
                    bottomDoor = d;
                    break;
            }
        }

        RoomController.instance.RegisterRoom(this);
    }

    void Update()
    {
        if (name.Contains(&quot;End&quot;) &amp;&amp; !updatedDoors)
        {
            RemoveUnconnectedDoors();
            updatedDoors = true;
        }
    }

    public void RemoveUnconnectedDoors()
    {
        Debug.Log(&quot;removing doors&quot;);
        foreach (Door door in doors)
        {
            switch (door.doorType)
            {
                case Door.DoorType.right:
                    if (GetRight() == null)
                    {
                        door.gameObject.SetActive(false);
                        replaceWallRight.SetActive(true);
                    }
                    break;
                case Door.DoorType.left:
                    if (GetLeft() == null)
                    {
                        door.gameObject.SetActive(false);
                        replaceWallLeft.SetActive(true);
                    }
                    break;
                case Door.DoorType.top:
                    if (GetTop() == null)
                    {
                        door.gameObject.SetActive(false);
                        replaceWallTop.SetActive(true);
                    }
                    break;
                case Door.DoorType.bottom:
                    if (GetBottom() == null)
                    {
                        door.gameObject.SetActive(false);
                        replaceWallBottom.SetActive(true);
                    }
                    break;
            }
        }
    }

    public Room GetRight()
    {
        if (RoomController.instance.DoesRoomExist(X + 1, Y))
        {
            return RoomController.instance.FindRoom(X + 1, Y);
        }
        return null;
    }
    public Room GetLeft()
    {
        if (RoomController.instance.DoesRoomExist(X - 1, Y))
        {
            return RoomController.instance.FindRoom(X - 1, Y);
        }
        return null;
    }
    public Room GetTop()
    {
        if (RoomController.instance.DoesRoomExist(X, Y + 1))
        {
            return RoomController.instance.FindRoom(X, Y + 1);
        }
        return null;
    }
    public Room GetBottom()
    {
        if (RoomController.instance.DoesRoomExist(X, Y - 1))
        {
            return RoomController.instance.FindRoom(X, Y - 1);
        }
        return null;
    }


    void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube(transform.position, new Vector3(Width, Height, 0));
    }

    public Vector3 GetRoomCentre()
    {
        return new Vector3(X * Width, Y * Height);
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag == &quot;Player&quot;)
        {
            RoomController.instance.OnPlayerEnterRoom(this);
        }
    }
}

and

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Linq;

public class RoomInfo
{
    public string name;
    public int X;
    public int Y;
}

public class RoomController : MonoBehaviour
{

    public static RoomController instance;

    string currentWorldName = &quot;Basement&quot;;

    RoomInfo currentLoadRoomData;

    Room currRoom;

    Queue&lt;RoomInfo&gt; loadRoomQueue = new Queue&lt;RoomInfo&gt;();

    public List&lt;Room&gt; loadedRooms = new List&lt;Room&gt;();

    bool isLoadingRoom = false;
    bool spawnedBossRoom = false;
    bool updatedRooms = false;

    void Awake()
    {
        instance = this;
    }

    void Start()
    {
        //LoadRoom(&quot;Start&quot;, 0, 0);
        //LoadRoom(&quot;Empty&quot;, 1, 0);
        //LoadRoom(&quot;Empty&quot;, -1, 0);
        //LoadRoom(&quot;Empty&quot;, 0, 1);
        //LoadRoom(&quot;Empty&quot;, 0, -1);
    }

    void Update()
    {
        UpdateRoomQueue();
    }

    void UpdateRoomQueue()
    {
        if (isLoadingRoom)
        {
            return;
        }

        if (loadRoomQueue.Count == 0)
        {
            if (!spawnedBossRoom)
            {
                StartCoroutine(SpawnBossRoom());
            }
            else if (spawnedBossRoom &amp;&amp; !updatedRooms)
            {
                foreach (Room room in loadedRooms)
                {
                    room.RemoveUnconnectedDoors();
                }
                UpdateRooms();
                updatedRooms = true;
            }
            return;
        }

        currentLoadRoomData = loadRoomQueue.Dequeue();
        isLoadingRoom = true;

        StartCoroutine(LoadRoomRoutine(currentLoadRoomData));
    }

    IEnumerator SpawnBossRoom()
    {
        spawnedBossRoom = true;
        yield return new WaitForSeconds(0.5f);
        if (loadRoomQueue.Count == 0)
        {
            Room bossRoom = loadedRooms[loadedRooms.Count - 1];
            Room tempRoom = new Room(bossRoom.X, bossRoom.Y);
            Destroy(bossRoom.gameObject);
            var roomToRemove = loadedRooms.Single(r =&gt; r.X == tempRoom.X &amp;&amp; r.Y == tempRoom.Y);
            loadedRooms.Remove(roomToRemove);
            LoadRoom(&quot;End&quot;, tempRoom.X, tempRoom.Y);
        }
    }

    public void LoadRoom(string name, int x, int y)
    {
        if (DoesRoomExist(x, y) == true)
        {
            return;
        }

        RoomInfo newRoomData = new RoomInfo();
        newRoomData.name = name;
        newRoomData.X = x;
        newRoomData.Y = y;

        loadRoomQueue.Enqueue(newRoomData);
    }

    IEnumerator LoadRoomRoutine(RoomInfo info)
    {
        string roomName = currentWorldName + info.name;

        AsyncOperation loadRoom = SceneManager.LoadSceneAsync(roomName, LoadSceneMode.Additive);

        while (loadRoom.isDone == false)
        {
            yield return null;
        }
    }

    public void RegisterRoom(Room room)
    {
        if (!DoesRoomExist(currentLoadRoomData.X, currentLoadRoomData.Y))
        {
            room.transform.position = new Vector3(
                currentLoadRoomData.X * room.Width,
                currentLoadRoomData.Y * room.Height,
                0
            );

            room.X = currentLoadRoomData.X;
            room.Y = currentLoadRoomData.Y;
            room.name = currentWorldName + &quot;-&quot; + currentLoadRoomData.name + &quot; &quot; + room.X + &quot;, &quot; + room.Y;
            room.transform.parent = transform;

            isLoadingRoom = false;

            if (loadedRooms.Count == 0)
            {
                CameraController.instance.currRoom = room;
            }

            loadedRooms.Add(room);
        }
        else
        {
            Destroy(room.gameObject);
            isLoadingRoom = false;
        }

    }

    public bool DoesRoomExist(int x, int y)
    {
        return loadedRooms.Find(item =&gt; item.X == x &amp;&amp; item.Y == y) != null;
    }

    public Room FindRoom(int x, int y)
    {
        return loadedRooms.Find(item =&gt; item.X == x &amp;&amp; item.Y == y);
    }

    public string GetRandomRoomName()
    {
        string[] possibleRooms = new string[] {
            &quot;Empty&quot;,
            &quot;Basic1&quot;,
            &quot;Basic2&quot;,
            &quot;Basic3&quot;
        };

        return possibleRooms[Random.Range(0, possibleRooms.Length)];
    }

    public void OnPlayerEnterRoom(Room room)
    {
        CameraController.instance.currRoom = room;
        currRoom = room;

        StartCoroutine(RoomCoroutine());
    }

    public IEnumerator RoomCoroutine()
    {
        yield return new WaitForSeconds(0.2f);
        UpdateRooms();
    }

    public void UpdateRooms()
    {
        foreach (Room room in loadedRooms)
        {
            if (currRoom != room)
            {
                EnemyController[] enemies = room.GetComponentsInChildren&lt;EnemyController&gt;();
                if (enemies != null)
                {
                    foreach (EnemyController enemy in enemies)
                    {
                        enemy.notInRoom = true;
                       
                    }

                    foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
                    {
                        door.doorCollider.SetActive(false);
                    }
                }
                else
                {
                    foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
                    {
                        door.doorCollider.SetActive(false);
                    }
                }
            }
            else
            {
                EnemyController[] enemies = room.GetComponentsInChildren&lt;EnemyController&gt;();
                if (enemies.Length &gt; 0)
                {
                    foreach (EnemyController enemy in enemies)
                    {
                        enemy.notInRoom = false;
                        Debug.Log(&quot;In room&quot;);
                    }

                    foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
                    {
                        door.doorCollider.SetActive(true);
                    }
                }
                else
                {
                    foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
                    {
                        door.doorCollider.SetActive(false);
                    }
                }
            }
        }
    }
}

This is my first time posting here so if the formatting looks off or anything else appears to be missing, please let me know!

Edit # 2

After some changes to my RoomController script and DungeonGenerator, I am now spawning the rooms as prefabs. However, they duplicate and overlap each other occasionally. Not every time will a room spawn in the start room, but at least one or two overlap throughout the dungeon. Here is the updated RoomController and my DungeonGenerator Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Linq;
public class RoomInfo
{
public int X;
public int Y;
public Room roomPrefab;
}
public class RoomController : MonoBehaviour
{
public static RoomController instance;
string currentWorldName = &quot;Basement&quot;;
RoomInfo currentLoadRoomData;
Room currRoom;
Queue&lt;RoomInfo&gt; loadRoomQueue = new Queue&lt;RoomInfo&gt;();
public List&lt;Room&gt; loadedRooms = new List&lt;Room&gt;();
bool isLoadingRoom = false;
bool spawnedBossRoom = false;
bool updatedRooms = false;
void Awake()
{
instance = this;
}
void Start()
{
//LoadRoom(&quot;Start&quot;, 0, 0);
//LoadRoom(&quot;Empty&quot;, 1, 0);
//LoadRoom(&quot;Empty&quot;, -1, 0);
//LoadRoom(&quot;Empty&quot;, 0, 1);
//LoadRoom(&quot;Empty&quot;, 0, -1);
}
void Update()
{
UpdateRoomQueue();
}
// Manages the queue of rooms to be loaded, then loads one at a time
void UpdateRoomQueue()
{
// If a room is already being loaded, don&#39;t do anything
if (isLoadingRoom)
{
return;
}
// If there are no more rooms to load, spawn boss room and update the existing rooms
if (loadRoomQueue.Count == 0)
{
// If the boss room hasn&#39;t spawned yet, spawn it
if (!spawnedBossRoom)
{
StartCoroutine(SpawnBossRoom());
}
// If the boss rom has spawned and the rooms havn&#39;t been updated yet, update them
else if (spawnedBossRoom &amp;&amp; !updatedRooms)
{
// Remove unconnected doors from each room that don&#39;t lead to another room
foreach (Room room in loadedRooms)
{
room.RemoveUnconnectedDoors();
}
// Update the rooms and mark them as updated
UpdateRooms();
updatedRooms = true;
}
return;
}
// Dequeue the next room to be loaded
currentLoadRoomData = loadRoomQueue.Dequeue();
isLoadingRoom = true;
// Start loading the room Asyncronously
StartCoroutine(LoadRoomRoutine(currentLoadRoomData));
}
IEnumerator SpawnBossRoom()
{
// Set the flag to indicate that a boss room has been spawned
spawnedBossRoom = true;
// Wait for a short duration before updating the flag
yield return new WaitForSeconds(0.5f);
// Check that there are no more rooms to load
if (loadRoomQueue.Count == 0)
{
// Get the last room in the list of loaded rooms and find the existing instance of the boss room
Room bossRoom = loadedRooms[loadedRooms.Count - 1];
Room tempRoom = bossRoom.gameObject.GetComponent&lt;Room&gt;();
// Destroy the boss room game object and remove it from the list of loaded rooms
Destroy(bossRoom.gameObject);
loadedRooms.Remove(bossRoom);
// Load the end room at the coords of the temp room
//LoadRoom(bossRoomPrefab, tempRoom.X, tempRoom.Y);
}
}
// This method loads a new room if it does not already exist at the given location
public void LoadRoom(Room roomPrefab, int x, int y)
{
// Checks if a room already exists at the given location, and returns if it does
if (DoesRoomExist(x, y))
{
return;
}
// Creates a new RoomInfo object with the given name and position on the dungeon grid
RoomInfo newRoomData = new RoomInfo();
newRoomData.roomPrefab = roomPrefab;
newRoomData.X = x;
newRoomData.Y = y;
// Adds the new RoomInfo object to the load room queue
loadRoomQueue.Enqueue(newRoomData);
}
// Couroutine that loads a room Asyncronously
IEnumerator LoadRoomRoutine(RoomInfo info)
{
// Instantiate the room prefab
Room room = Instantiate(info.roomPrefab);
// Set the room&#39;s position based on its grid coordinates and size
room.transform.position = new Vector3(
info.X * room.Width,
info.Y * room.Height,
0
);
Debug.Log(&quot;Room width = &quot; + info.X + &quot;, and Room height = &quot; + info.Y);
// Set the room&#39;s coordinates and name
room.X = info.X;
room.Y = info.Y;
room.name = currentWorldName + &quot;-&quot; + info.roomPrefab.name + &quot; &quot; + room.X + &quot;, &quot; + room.Y;
// Set the room&#39;s parent and add it to the list of loaded rooms
room.transform.parent = transform;
loadedRooms.Add(room);
// If this is the first room loaded, set it as the current Camera View.
if (loadedRooms.Count == 1)
{
CameraController.instance.currRoom = room;
}
// Yield null to wait for the end of the frame before finishing the coroutine
yield return null;
}
// This function registers a new room in the current world(Basement) and makes it part of the game. Takes the most recent room loaded
public void RegisterRoom(Room room)
{
// Set the room&#39;s loading status to false
isLoadingRoom = false;
// Set the room&#39;s parent and add it to the list of loaded rooms
room.transform.parent = transform;
loadedRooms.Add(room);
// Set the room as the current Camera View if it is the first room loaded
if (loadedRooms.Count == 1)
{
CameraController.instance.currRoom = room;
}
// Set the room&#39;s name based on the prefab name and grid coordinates
room.name = currentWorldName + &quot;-&quot; + room.name + &quot; &quot; + room.X + &quot;, &quot; + room.Y;
}
// Checks if a room with given x and y coords already exists in the loadedRooms
public bool DoesRoomExist(int x, int y)
{
// Using the Find meothd, we search for a room object with matching x and y values
// If a match is found, we return true, if not, false.
return loadedRooms.Find(item =&gt; item.X == x &amp;&amp; item.Y == y) != null;
}
// Checks if a room exists with given x and y coords.
public Room FindRoom(int x, int y)
{
// Use the Find method again to search for a room that matches the given coords
// If a match is found, return the Room Object
// Else the method returns null
return loadedRooms.Find(item =&gt; item.X == x &amp;&amp; item.Y == y);
}
public string GetRandomRoomName()
{
// Define an array of possible room names
string[] possibleRooms = new string[] {
&quot;Empty&quot;,
&quot;Basic1&quot;,
&quot;Basic2&quot;,
&quot;Basic3&quot;
};
// Then return a random room name from the possibleRooms array
return possibleRooms[Random.Range(0, possibleRooms.Length)];
}
// When the player enters the room, move the camera to that room. This is legacy code.
public void OnPlayerEnterRoom(Room room)
{
CameraController.instance.currRoom = room;
currRoom = room;
StartCoroutine(RoomCoroutine());
}
// Time waited before loading another room and unpdating the queue
public IEnumerator RoomCoroutine()
{
yield return new WaitForSeconds(0.2f);
UpdateRooms();
}
public void UpdateRooms()
{
// First loop through all the loaded rooms
foreach (Room room in loadedRooms)
{
// If the current room isn&#39;t the current room being displayed
if (currRoom != room)
{
// Deactivate all the enemies in the room and their colliders
EnemyController[] enemies = room.GetComponentsInChildren&lt;EnemyController&gt;();
if (enemies != null)
{
foreach (EnemyController enemy in enemies)
{
enemy.notInRoom = true;
}
foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
{
door.doorCollider.SetActive(false);
}
}
// Deactive all the doors inside the room
else
{
foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
{
door.doorCollider.SetActive(false);
}
}
}
// If the current room is the current room being displayed
else
{
// Activate all enemies in the room and their colliders
EnemyController[] enemies = room.GetComponentsInChildren&lt;EnemyController&gt;();
if (enemies.Length &gt; 0)
{
foreach (EnemyController enemy in enemies)
{
enemy.notInRoom = false;
Debug.Log(&quot;In room&quot;);
}
foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
{
door.doorCollider.SetActive(true);
}
}
// Deactivate all the doors in the room
else
{
foreach (Door door in room.GetComponentsInChildren&lt;Door&gt;())
{
door.doorCollider.SetActive(false);
}
}
}
}
}
}

And the DungeonGenerator

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DungeonGenerator : MonoBehaviour
{
public DungeonGenerationData dungeonGenerationData;
private List&lt;Vector2Int&gt; dungeonRooms;
public Room startingRoomPrefab;
public Room bossRoomPrefab;
public Room[] roomPrefabs;
private void Start()
{
dungeonRooms = DungeonCrawlerController.GenerateDungeon(dungeonGenerationData);
SpawnRooms(dungeonRooms);
}
private void SpawnRooms(IEnumerable&lt;Vector2Int&gt; rooms)
{
// Load the starting room
RoomController.instance.LoadRoom(startingRoomPrefab, 0, 0);
List&lt;Vector2Int&gt; dungeonRooms = new List&lt;Vector2Int&gt;();
int count = 0;
// Loop through the list of room locations and load each room
foreach (Vector2Int roomLocation in rooms)
{
if (count == 0)
{
// Get a random room prefab from the list
int randomIndex2 = Random.Range(0, roomPrefabs.Length);
Room randomRoomPrefab2 = roomPrefabs[randomIndex2];
// Load the room prefab at the given location
RoomController.instance.LoadRoom(randomRoomPrefab2, roomLocation.x, roomLocation.y);
Debug.Log(&quot;Room spawned at x = &quot; + roomLocation.x + &quot;, and at y = &quot; + roomLocation.y);
count++;
continue;
}
if (dungeonRooms.Contains(roomLocation))
{
continue;
}
// Get a random room prefab from the list
int randomIndex = Random.Range(0, roomPrefabs.Length);
Room randomRoomPrefab = roomPrefabs[randomIndex];
// Load the room prefab at the given location
RoomController.instance.LoadRoom(randomRoomPrefab, roomLocation.x, roomLocation.y);
Debug.Log(&quot;Room spawned at x = &quot; + roomLocation.x + &quot;, and at y = &quot; + roomLocation.y);
count++;
dungeonRooms.Add(roomLocation);
}
}
}

答案1

得分: 1

加载每个房间作为新场景不是一个好主意。正如你在Unity文档中所看到的,不同场景上的NavMeshes不会连接,除非你手动创建一个NavMeshLink:
https://docs.unity3d.com/Manual/nav-AdditiveLoading.html

因此,我建议简单地将每个房间制作成一个Prefab,并在正确的位置实例化它们,然后为整个场景烘焙一个单一的NavMesh。或者,如果你不需要它们连接在一起,可以为每个Prefab烘焙一个NavMesh。

英文:

Loading each room as a new scene is not a good idea. As you can see here in the Unity docs, NavMeshes on different scenes do not connect unless you manually create a NavMeshLink:
https://docs.unity3d.com/Manual/nav-AdditiveLoading.html

So I would suggest to simply make each room a Prefab and Instantiate them in the right places, then bake a single NavMesh for the whole scene. Or bake a NavMesh for each Prefab if you do not need for them to be connected together.

huangapple
  • 本文由 发表于 2023年4月20日 06:15:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76059190.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定