在过去的几个月里,我从旧的塞尔达游戏中汲取了一些灵感,制作了一款类似《以撒的结合》的游戏。我之前在这里发过一篇文章(在运行时为二维半程序地牢爬虫(Unity)生成导航网格),我在使用场景生成房间时甚至都在努力烘焙 Navmesh。从那以后,我开始使用预制房间并在运行时实例化它们以随机放置在地牢中。我还获得了导航网格表面以应用于整个地牢中的每个房间,并且可以在它们应该在的房间中看到它们,它们应该看起来如何等等。在运行时的层次结构中,我能够在我的主要场景的一部分 RoomController 游戏对象中查看房间及其内容。它显示它们好像是由那个房间生成的,但都是在起始房间内生成的。下面我将提供一些我所看到的屏幕截图,以更好地说明问题。
我希望敌人在他们自己的房间中生成,参见图 2,其中“Basmement-RoomBasic”有 3 个小恶魔和 4 个近战敌人。这些敌人会在导航网格上的那个房间里生成
我已经尝试重新烘焙导航网格,在运行时使用直接从 Unity 的运行时导航烘焙指南中获取的导航烘焙器对其进行烘焙,并删除它们不应在其上生成的 tilemap 部分并使其仅在房间内。我尝试修改我的 GridController、我的 DungeonGenerator 和我的 ObjectSpawner 脚本,但都无济于事。下面我也会提供这些脚本
首先是 GridController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class GridController : MonoBehaviour
{
public Room room;
[System.Serializable]
public struct Grid
{
public int columns, rows;
public float verticalOffset, horizontalOffset;
}
public Grid grid;
public GameObject gridTile;
public List<Vector2> availablePoints = new List<Vector2>();
void Awake()
{
room = GetComponentInParent<Room>();
GenerateGrid();
}
public void GenerateGrid()
{
grid.verticalOffset += room.transform.localPosition.y;
grid.horizontalOffset += room.transform.localPosition.x;
for (int y = 0; y < grid.rows; y++)
{
for (int x = 0; x < grid.columns; x++)
{
GameObject go = Instantiate(gridTile, transform);
go.GetComponent<Transform>().position = new Vector2(x - (grid.columns - grid.horizontalOffset), y - (grid.rows - grid.verticalOffset));
go.name = "X: " + x + ", Y: " + y;
availablePoints.Add(go.transform.position);
go.SetActive(false);
}
}
GetComponentInParent<ObjectRoomSpawner>().InitialiseObjectSpawning();
}
public List<Vector3> GetSources()
{
List<Vector3> sources = new List<Vector3>();
foreach (Transform child in transform)
{
if (child.gameObject.activeSelf)
{
NavMeshHit hit;
if (NavMesh.SamplePosition(child.position, out hit, 0.1f, NavMesh.AllAreas))
{
sources.Add(hit.position);
}
else
{
Debug.LogWarning("Could not find a valid position on the NavMesh for source spawn");
}
}
}
return sources;
}
}
接下来是地牢生成器
using NavMeshPlus.Components;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class DungeonGenerator : MonoBehaviour
{
public DungeonGenerationData dungeonGenerationData;
private List<Vector2Int> dungeonRooms;
public Room startingRoomPrefab;
public Room bossRoomPrefab;
public Room[] roomPrefabs;
private void Start()
{
dungeonRooms = DungeonCrawlerController.GenerateDungeon(dungeonGenerationData);
SpawnRooms(dungeonRooms);
Debug.Log("Nav mesh should be rebuilt now");
}
private void SpawnRooms(IEnumerable<Vector2Int> rooms)
{
// Load the starting room
RoomController.instance.LoadRoom(startingRoomPrefab, 0, 0);
List<Room> loadedRooms = new List<Room>();
loadedRooms.Add(startingRoomPrefab);
foreach (Vector2Int roomLocation in rooms)
{
// Check if a room already exists at the specified location
Room room = RoomController.instance.GetRoomAtPosition(roomLocation.x, roomLocation.y);
if (room != null || loadedRooms.Any(loadedRoom => loadedRoom.X == roomLocation.x && loadedRoom.Y == roomLocation.y))
{
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);
loadedRooms.Add(randomRoomPrefab);
Debug.Log("Room spawned at x = " + roomLocation.x + ", and at y = " + roomLocation.y);
}
}
}
最后是 ObjectSpawner
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class ObjectRoomSpawner : MonoBehaviour
{
[System.Serializable]
public struct RandomSpawner
{
public string name;
public SpawnerData spawnerData;
}
public GridController grid;
public RandomSpawner[] spawnerData;
void Start()
{
//grid = GetComponentInChildren<GridController>();
}
public void InitialiseObjectSpawning()
{
foreach (RandomSpawner rs in spawnerData)
{
SpawnObjects(rs);
}
}
void SpawnObjects(RandomSpawner data)
{
int randomIteration = Random.Range(data.spawnerData.minSpawn, data.spawnerData.maxSpawn + 1);
for (int i = 0; i < randomIteration; i++)
{
int randomPos = Random.Range(0, grid.availablePoints.Count - 1);
// Get a random point on the NavMesh
NavMeshHit hit;
if (NavMesh.SamplePosition(grid.availablePoints[randomPos], out hit, 10f, NavMesh.AllAreas))
{
// Instantiate the enemy prefab at the NavMesh position
GameObject go = Instantiate(data.spawnerData.itemToSpawn, hit.position, Quaternion.identity, transform) as GameObject;
grid.availablePoints.RemoveAt(randomPos);
Debug.Log("Spawned Object at position " + hit.position);
}
else
{
Debug.LogWarning("Could not find a valid position on the NavMesh for enemy spawn");
}
}
}
}
任何信息都将不胜感激,以指出我正确的方向。为了我的毕业设计,我整个学期都在研究这款游戏,完成这项工作将使游戏几乎完成。提前致谢!
编辑#1
我认为在运行时额外显示导航网格的示例可能很有用。