| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- using System.Collections.Generic;
- using UnityEngine;
- namespace MazeGen
- {
- /// <summary>
- /// Spawns visual monster GameObjects when rooms with encounters are generated.
- /// Listens to MazeGenerator's RoomGenerated event and creates MonsterView children
- /// in each room that has an encounter.
- ///
- /// Also keeps the views honest after a fight: <see cref="RefreshRoom"/> removes the views of
- /// monsters that died, so a cleared room actually looks cleared.
- /// </summary>
- public class MonsterSpawner : MonoBehaviour
- {
- [SerializeField] private MazeGenerator mazeGenerator;
- // Container transform to organize spawned monsters in the hierarchy
- private Transform _monstersContainer;
- // Views per room id, so a resolved fight can clean up just that room's dead.
- private readonly Dictionary<int, List<MonsterView>> _viewsByRoom = new Dictionary<int, List<MonsterView>>();
- // Subscribing in Awake rather than Start: MazeSceneBuilder generates the start rooms in
- // its own Start, and component Start order on a shared GameObject isn't something to rely
- // on - subscribing a frame-phase earlier guarantees no room generation is missed.
- private void Awake()
- {
- if (mazeGenerator == null)
- mazeGenerator = GetComponent<MazeGenerator>();
- if (mazeGenerator == null)
- {
- Debug.LogError("[MonsterSpawner] Could not find MazeGenerator!");
- return;
- }
- // Create a container for all spawned monsters
- _monstersContainer = new GameObject("SpawnedMonsters").transform;
- _monstersContainer.SetParent(transform);
- _monstersContainer.localPosition = Vector3.zero;
- // Subscribe to room generation event
- mazeGenerator.RoomGenerated += OnRoomGenerated;
- }
- private void OnDestroy()
- {
- if (mazeGenerator != null)
- mazeGenerator.RoomGenerated -= OnRoomGenerated;
- }
- /// <summary>
- /// Called whenever a new room is generated. If it has an encounter,
- /// spawn visual monsters in that room.
- /// </summary>
- private void OnRoomGenerated(Room room)
- {
- if (room.Encounter == null || room.Encounter.Monsters.Count == 0)
- return;
- SpawnMonstersInRoom(room);
- }
- /// <summary>
- /// Drops the views of any monster in this room that is now dead. Wire this to
- /// CombatDirector.FightResolved so the map reflects the fight's outcome.
- /// </summary>
- public void RefreshRoom(Room room)
- {
- if (room == null || !_viewsByRoom.TryGetValue(room.Id, out var views)) return;
- for (int i = views.Count - 1; i >= 0; i--)
- {
- MonsterView view = views[i];
- if (view == null)
- {
- views.RemoveAt(i);
- continue;
- }
- if (view.MonsterData != null && view.MonsterData.IsDefeated)
- {
- views.RemoveAt(i);
- view.OnDefeated();
- }
- }
- if (views.Count == 0)
- _viewsByRoom.Remove(room.Id);
- }
- /// <summary>
- /// Creates visual MonsterView GameObjects scattered around the room.
- /// </summary>
- private void SpawnMonstersInRoom(Room room)
- {
- var encounter = room.Encounter;
- int monsterCount = encounter.Monsters.Count;
- // Create a parent for this room's monsters
- var roomMonstersParent = new GameObject($"Room{room.Id}_Monsters").transform;
- roomMonstersParent.SetParent(_monstersContainer);
- roomMonstersParent.localPosition = Vector3.zero;
- var views = new List<MonsterView>(monsterCount);
- _viewsByRoom[room.Id] = views;
- // Scatter monsters around the room in a circle
- float angleStep = 360f / monsterCount;
- float scatterRadius = room.Radius * 0.5f; // Spawn within the room
- for (int i = 0; i < monsterCount; i++)
- {
- var monsterData = encounter.Monsters[i];
- float angle = i * angleStep * Mathf.Deg2Rad;
- Vector2 offset = new Vector2(
- Mathf.Cos(angle) * scatterRadius,
- Mathf.Sin(angle) * scatterRadius
- );
- Vector2 spawnPos = room.Position + offset;
- views.Add(SpawnMonster(monsterData, spawnPos, roomMonstersParent));
- }
- }
- /// <summary>
- /// Instantiates a single MonsterView at the given position.
- /// </summary>
- private MonsterView SpawnMonster(MonsterEncounter.MonsterInstance monsterData, Vector2 position, Transform parent)
- {
- var monsterGO = new GameObject(monsterData.Template.MonsterName);
- monsterGO.transform.SetParent(parent);
- monsterGO.transform.position = position;
- var monsterView = monsterGO.AddComponent<MonsterView>();
- monsterView.Initialize(monsterData, position);
- // Optional: add a collider for future click-detection or physics
- var collider = monsterGO.AddComponent<CircleCollider2D>();
- collider.radius = 0.15f;
- return monsterView;
- }
- }
- }
|