AIAgentManager.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. /// <summary>
  4. /// Manages spawning and tracking of AI agents in the maze
  5. /// Supports configurable spawn counts and runtime spawning
  6. /// </summary>
  7. public class AIAgentManager : MonoBehaviour
  8. {
  9. [Header("Agent Spawning")]
  10. [SerializeField] private int initialAgentCount = 10;
  11. [SerializeField] private string agentCharacterType = "Default";
  12. [SerializeField] private float spawnDelay = 0.1f;
  13. [Header("Agent Prefab")]
  14. [SerializeField] private GameObject agentPrefab;
  15. [Header("Visual Settings")]
  16. [SerializeField] private bool showAgentPaths = true;
  17. [SerializeField] private Color agentPathColor = Color.yellow;
  18. [SerializeField] private Material agentMaterial;
  19. private MazeController mazeController;
  20. private List<AIAgent> activeAgents = new();
  21. private int nextAgentId = 0;
  22. private bool isInitialized = false;
  23. void Awake()
  24. {
  25. mazeController = GetComponent<MazeController>();
  26. if (mazeController == null)
  27. {
  28. mazeController = FindAnyObjectByType<MazeController>();
  29. }
  30. }
  31. void OnValidate()
  32. {
  33. // Called when inspector values change (both in play mode and edit mode)
  34. // Update all active agents' path visibility
  35. if (isInitialized && activeAgents.Count > 0)
  36. {
  37. SetPathVisibilityForAllAgents(showAgentPaths);
  38. }
  39. }
  40. void Start()
  41. {
  42. if (mazeController == null)
  43. {
  44. Debug.LogError("AIAgentManager: MazeController not found!");
  45. enabled = false;
  46. return;
  47. }
  48. // Create agent prefab if not assigned
  49. if (agentPrefab == null)
  50. {
  51. CreateDefaultAgentPrefab();
  52. }
  53. // Spawn initial agents
  54. StartCoroutine(SpawnAgentsCoroutine(initialAgentCount));
  55. isInitialized = true;
  56. }
  57. /// <summary>
  58. /// Creates a default agent prefab if one isn't assigned
  59. /// </summary>
  60. private void CreateDefaultAgentPrefab()
  61. {
  62. GameObject prefab = new GameObject("AIAgent");
  63. prefab.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
  64. // Use 3D collider instead of 2D for 3D environment
  65. var collider = prefab.AddComponent<SphereCollider>();
  66. collider.radius = 1.0f;
  67. // Add mesh renderer with a simple sphere
  68. var meshFilter = prefab.AddComponent<MeshFilter>();
  69. var meshRenderer = prefab.AddComponent<MeshRenderer>();
  70. // Use Unity's built-in sphere primitive mesh
  71. meshFilter.mesh = Resources.GetBuiltinResource<Mesh>("Sphere.fbx");
  72. // Create a cyan material using URP shader (Universal Render Pipeline)
  73. var mat = new Material(Shader.Find("Universal Render Pipeline/Lit"));
  74. if (mat.shader == null)
  75. {
  76. // Fallback to simpler URP shader if Lit is not available
  77. mat = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
  78. }
  79. if (mat.shader == null)
  80. {
  81. // Last resort - use Standard shader
  82. mat = new Material(Shader.Find("Standard"));
  83. }
  84. mat.color = Color.cyan;
  85. meshRenderer.material = mat;
  86. prefab.AddComponent<AIAgent>();
  87. agentPrefab = prefab;
  88. }
  89. /// <summary>
  90. /// Spawns agents with delay
  91. /// </summary>
  92. private System.Collections.IEnumerator SpawnAgentsCoroutine(int count)
  93. {
  94. for (int i = 0; i < count; i++)
  95. {
  96. SpawnAgent();
  97. yield return new WaitForSeconds(spawnDelay);
  98. }
  99. }
  100. /// <summary>
  101. /// Spawns a single AI agent
  102. /// </summary>
  103. public AIAgent SpawnAgent()
  104. {
  105. if (agentPrefab == null)
  106. {
  107. Debug.LogError("AIAgentManager: Agent prefab not set!");
  108. return null;
  109. }
  110. GameObject agentGO = Instantiate(agentPrefab);
  111. agentGO.name = $"AIAgent_{agentCharacterType}_{nextAgentId}";
  112. var agent = agentGO.GetComponent<AIAgent>();
  113. agent.SetShowPath(showAgentPaths);
  114. if (agent == null)
  115. {
  116. agent = agentGO.AddComponent<AIAgent>();
  117. }
  118. // Set agent properties via reflection or public properties
  119. var idField = typeof(AIAgent).GetField("agentId",
  120. System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  121. var typeField = typeof(AIAgent).GetField("agentCharacterType",
  122. System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  123. if (idField != null) idField.SetValue(agent, nextAgentId);
  124. if (typeField != null) typeField.SetValue(agent, agentCharacterType);
  125. activeAgents.Add(agent);
  126. nextAgentId++;
  127. Debug.Log($"Spawned agent {nextAgentId - 1} ({agentCharacterType}). Total agents: {activeAgents.Count}");
  128. return agent;
  129. }
  130. /// <summary>
  131. /// Spawns multiple agents at once
  132. /// </summary>
  133. public void SpawnAgents(int count)
  134. {
  135. StartCoroutine(SpawnAgentsCoroutine(count));
  136. }
  137. /// <summary>
  138. /// Removes and destroys an agent
  139. /// </summary>
  140. public void RemoveAgent(AIAgent agent)
  141. {
  142. if (activeAgents.Remove(agent))
  143. {
  144. Destroy(agent.gameObject);
  145. Debug.Log($"Removed agent {agent.AgentId}. Remaining: {activeAgents.Count}");
  146. }
  147. }
  148. /// <summary>
  149. /// Gets agents of a specific character type
  150. /// </summary>
  151. public List<AIAgent> GetAgentsByType(string characterType)
  152. {
  153. var agents = new List<AIAgent>();
  154. foreach (var agent in activeAgents)
  155. {
  156. if (agent.CharacterType == characterType)
  157. {
  158. agents.Add(agent);
  159. }
  160. }
  161. return agents;
  162. }
  163. /// <summary>
  164. /// Gets the count of active agents
  165. /// </summary>
  166. public int GetAgentCount()
  167. {
  168. return activeAgents.Count;
  169. }
  170. /// <summary>
  171. /// Gets agent count by character type
  172. /// </summary>
  173. public int GetAgentCountByType(string characterType)
  174. {
  175. return GetAgentsByType(characterType).Count;
  176. }
  177. /// <summary>
  178. /// Clears all agents and resets
  179. /// </summary>
  180. public void ClearAllAgents()
  181. {
  182. foreach (var agent in activeAgents)
  183. {
  184. Destroy(agent.gameObject);
  185. }
  186. activeAgents.Clear();
  187. nextAgentId = 0;
  188. AIRoomMemoryManager.ClearAllMemories();
  189. }
  190. /// <summary>
  191. /// Resets agents for a new maze
  192. /// </summary>
  193. public void ResetForNewMaze()
  194. {
  195. ClearAllAgents();
  196. if (isInitialized)
  197. {
  198. StartCoroutine(SpawnAgentsCoroutine(initialAgentCount));
  199. }
  200. }
  201. /// <summary>
  202. /// Gets UI info about agents
  203. /// </summary>
  204. public string GetAgentStats()
  205. {
  206. int total = activeAgents.Count;
  207. string stats = $"Total Agents: {total}\n";
  208. var typeGroups = new Dictionary<string, int>();
  209. foreach (var agent in activeAgents)
  210. {
  211. if (!typeGroups.ContainsKey(agent.CharacterType))
  212. {
  213. typeGroups[agent.CharacterType] = 0;
  214. }
  215. typeGroups[agent.CharacterType]++;
  216. }
  217. foreach (var kvp in typeGroups)
  218. {
  219. var memory = AIRoomMemoryManager.GetMemory(kvp.Key);
  220. stats += $"{kvp.Key}: {kvp.Value} agents, {memory.VisitedCount} rooms explored\n";
  221. }
  222. return stats;
  223. }
  224. /// <summary>
  225. /// Gets the initial agent count setting
  226. /// </summary>
  227. public int InitialAgentCount => initialAgentCount;
  228. /// <summary>
  229. /// Sets the initial agent count (affects next reset)
  230. /// </summary>
  231. public void SetInitialAgentCount(int count)
  232. {
  233. initialAgentCount = count;
  234. }
  235. /// <summary>
  236. /// Gets the count of active agents
  237. /// </summary>
  238. public int GetActiveAgentCount()
  239. {
  240. return activeAgents.Count;
  241. }
  242. /// <summary>
  243. /// Gets the list of active agents
  244. /// </summary>
  245. public List<AIAgent> GetActiveAgents()
  246. {
  247. return activeAgents;
  248. }
  249. /// <summary>
  250. /// Updates path visibility for all active agents
  251. /// </summary>
  252. public void SetPathVisibilityForAllAgents(bool show)
  253. {
  254. showAgentPaths = show;
  255. foreach (var agent in activeAgents)
  256. {
  257. if (agent != null)
  258. {
  259. agent.SetShowPath(show);
  260. }
  261. }
  262. Debug.Log($"Path visibility set to: {show} for {activeAgents.Count} agents");
  263. }
  264. }