0

for the past few months I've been making a Binding of Isaac like game with some inspiration from the older Zelda games. I previously made a post here ( Generating a Navigation Mesh at runtime for a 2d semi-procedural dungeon crawler(Unity) ) where I was struggling to even get a Navmesh to bake as I was using scenes to generate the rooms. I've since moved to using prefabbed rooms and instantiating them at runtime to place randomly in the dungeon. I've also gotten the Nav mesh surfaces to apply to each room throughout the dungeon and can see them all in the rooms they are supposed to be in, how they are supposed to look etc. In my hierarchy at run time, I am able to see the rooms and their contents in my RoomController gameObject which is part of my main scene. It shows as though they are being spawned by that room, but are all spawning inside of the start room. Below im going to provide some screenshots of what I'm seeing to better illustrate the issue. Before pressing playAfter pressing playShowing the spawner

I would like the enemies to spawn in their own individual rooms, see image 2 where "Basmement-RoomBasic" has 3 smallDemons and 4 MeleeEnemies. These enemies would spawn in that room on the nav mesh

I've tried rebaking the nav mesh, baking it at runtime with a navigation baker taken directly from Unity's guide on Runtime navigation baking, and removing sections of the tilemap that they shouldn't spawn on and making it only inside the room. I tried modifing my GridController, my DungeonGenerator, and my ObjectSpawner scripts to no avail. Below I will also provide these scripts.

First the 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;
    }



}

Next the DungeonGenerator

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);
        }
    }
}

And finally the 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");
            }
        }
    }


}

Any information would be greatly appreciated to point me in the right direction. I've been working on this game all semester long for my graduation project, and finishing this up would make the game practically complete. Thanks in advance!


Edit # 1

I figured it could be useful to additionally show examples of the nav mesh at runtime.

NavMesh example 1

NavMesh example 2

Gatzby01
  • 3
  • 2
  • Its not clear (or i missed it due to a lot of code) when the grid controller does its thing. But in short it has an array of every tile. No tile seems excluded. You also have a list of items all of which seem to get spawned. You never seem to block a tile potentially meaning multiple things per tile. No tile is out of bounds so its all spawn. – BugFinder Apr 24 '23 at 19:22
  • @BugFinder Hi so on Awake, it will Generate the grid based on the parameters set in the inspector for that specific room. Before switching to an AI based enemy system, I used the grid controller to spawn the enemies in a section of the room that couldn't really decide if an area was walkable or not. I'm wondering if I even need the GridController anymore since I'm just looking to spawn enemies on the nav mesh. That's why the grid controller doesn't have any blocking of the tile. I included it incase it could prove useful for debugging the issue – Gatzby01 Apr 24 '23 at 19:40

0 Answers0