| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- using UnityEngine;
- using System.Collections.Generic;
- using System.Linq;
- /// <summary>
- /// Manages spawning and tracking of AI agents in the maze
- /// Supports configurable spawn counts and runtime spawning
- /// </summary>
- public class AIAgentManager : MonoBehaviour
- {
- [Header("Agent Spawning")]
- [SerializeField] private int initialAgentCount = 10;
- [SerializeField] private string agentCharacterType = "Default";
- [SerializeField] private float spawnDelay = 0.1f;
- [Header("Agent Prefab")]
- [SerializeField] private GameObject agentPrefab;
- [Header("Visual Settings")]
- [SerializeField] private bool showAgentPaths = true;
- [SerializeField] private Color agentPathColor = Color.yellow;
- [SerializeField] private Material agentMaterial;
- [Header("Grouping")]
- [SerializeField] private float groupCheckInterval = 2f;
- [SerializeField] private float groupingRadius = 1.5f;
- private MazeController mazeController;
- private List<AIAgent> activeAgents = new();
- private readonly List<AgentGroup> activeGroups = new();
- private int nextAgentId = 0;
- private bool isInitialized = false;
- private float lastGroupCheck = 0f;
- void Awake()
- {
- mazeController = GetComponent<MazeController>();
- if (mazeController == null)
- {
- mazeController = FindAnyObjectByType<MazeController>();
- }
- }
- void Update()
- {
- if (!isInitialized) return;
- if (Time.time - lastGroupCheck < groupCheckInterval) return;
- lastGroupCheck = Time.time;
- EvaluateGrouping();
- }
- /// <summary>
- /// Checks all solo/leader agents for nearby agents and tries to form or join groups.
- /// </summary>
- private void EvaluateGrouping()
- {
- var candidates = activeAgents
- .Where(a => a != null && !a.IsGroupFollower && !a.HasReachedGoal)
- .ToList();
- for (int i = 0; i < candidates.Count; i++)
- {
- AIAgent a = candidates[i];
- if (Random.value > a.GroupUpAffinity) continue;
- for (int j = i + 1; j < candidates.Count; j++)
- {
- AIAgent b = candidates[j];
- if (Vector3.Distance(a.transform.position, b.transform.position) > groupingRadius) continue;
- if (Random.value > b.GroupUpAffinity) continue;
- if (a.Group != null && !a.IsGroupFollower && b.Group == null)
- a.Group.TryAddMember(b);
- else if (b.Group != null && !b.IsGroupFollower && a.Group == null)
- b.Group.TryAddMember(a);
- else if (a.Group == null && b.Group == null)
- {
- var newGroup = new AgentGroup(a);
- newGroup.TryAddMember(b);
- activeGroups.Add(newGroup);
- }
- }
- }
- activeGroups.RemoveAll(g => g.Size <= 1);
- }
- void OnValidate()
- {
- // Called when inspector values change (both in play mode and edit mode)
- // Update all active agents' path visibility
- if (isInitialized && activeAgents.Count > 0)
- {
- SetPathVisibilityForAllAgents(showAgentPaths);
- }
- }
- void Start()
- {
- if (mazeController == null)
- {
- Debug.LogError("AIAgentManager: MazeController not found!");
- enabled = false;
- return;
- }
- // Create agent prefab if not assigned
- if (agentPrefab == null)
- {
- CreateDefaultAgentPrefab();
- }
- // Spawn initial agents
- StartCoroutine(SpawnAgentsCoroutine(initialAgentCount));
- isInitialized = true;
- }
- /// <summary>
- /// Creates a default agent prefab if one isn't assigned
- /// </summary>
- private void CreateDefaultAgentPrefab()
- {
- GameObject prefab = new GameObject("AIAgent");
- prefab.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
- // Use 3D collider instead of 2D for 3D environment
- var collider = prefab.AddComponent<SphereCollider>();
- collider.radius = 1.0f;
- // Add mesh renderer with a simple sphere
- var meshFilter = prefab.AddComponent<MeshFilter>();
- var meshRenderer = prefab.AddComponent<MeshRenderer>();
- // Use Unity's built-in sphere primitive mesh
- meshFilter.mesh = Resources.GetBuiltinResource<Mesh>("Sphere.fbx");
- // Create a cyan material using URP shader (Universal Render Pipeline)
- var mat = new Material(Shader.Find("Universal Render Pipeline/Lit"));
- if (mat.shader == null)
- {
- // Fallback to simpler URP shader if Lit is not available
- mat = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
- }
- if (mat.shader == null)
- {
- // Last resort - use Standard shader
- mat = new Material(Shader.Find("Standard"));
- }
- mat.color = Color.cyan;
- meshRenderer.material = mat;
- prefab.AddComponent<AIAgent>();
- agentPrefab = prefab;
- }
- /// <summary>
- /// Spawns agents with delay
- /// </summary>
- private System.Collections.IEnumerator SpawnAgentsCoroutine(int count)
- {
- for (int i = 0; i < count; i++)
- {
- SpawnAgent();
- yield return new WaitForSeconds(spawnDelay);
- }
- }
- /// <summary>
- /// Spawns a single AI agent
- /// </summary>
- public AIAgent SpawnAgent()
- {
- if (agentPrefab == null)
- {
- Debug.LogError("AIAgentManager: Agent prefab not set!");
- return null;
- }
- GameObject agentGO = Instantiate(agentPrefab);
- agentGO.name = $"AIAgent_{agentCharacterType}_{nextAgentId}";
- var agent = agentGO.GetComponent<AIAgent>();
- agent.SetShowPath(showAgentPaths);
- if (agent == null)
- {
- agent = agentGO.AddComponent<AIAgent>();
- }
- // Set agent properties via reflection or public properties
- var idField = typeof(AIAgent).GetField("agentId",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
- var typeField = typeof(AIAgent).GetField("agentCharacterType",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
- if (idField != null) idField.SetValue(agent, nextAgentId);
- if (typeField != null) typeField.SetValue(agent, agentCharacterType);
- activeAgents.Add(agent);
- nextAgentId++;
- Debug.Log($"Spawned agent {nextAgentId - 1} ({agentCharacterType}). Total agents: {activeAgents.Count}");
- return agent;
- }
- /// <summary>
- /// Spawns multiple agents at once
- /// </summary>
- public void SpawnAgents(int count)
- {
- StartCoroutine(SpawnAgentsCoroutine(count));
- }
- /// <summary>
- /// Removes and destroys an agent
- /// </summary>
- public void RemoveAgent(AIAgent agent)
- {
- if (activeAgents.Remove(agent))
- {
- Destroy(agent.gameObject);
- Debug.Log($"Removed agent {agent.AgentId}. Remaining: {activeAgents.Count}");
- }
- }
- /// <summary>
- /// Gets agents of a specific character type
- /// </summary>
- public List<AIAgent> GetAgentsByType(string characterType)
- {
- var agents = new List<AIAgent>();
- foreach (var agent in activeAgents)
- {
- if (agent.CharacterType == characterType)
- {
- agents.Add(agent);
- }
- }
- return agents;
- }
- /// <summary>
- /// Gets the count of active agents
- /// </summary>
- public int GetAgentCount()
- {
- return activeAgents.Count;
- }
- /// <summary>
- /// Gets agent count by character type
- /// </summary>
- public int GetAgentCountByType(string characterType)
- {
- return GetAgentsByType(characterType).Count;
- }
- /// <summary>
- /// Clears all agents and resets
- /// </summary>
- public void ClearAllAgents()
- {
- foreach (var agent in activeAgents)
- {
- Destroy(agent.gameObject);
- }
- activeAgents.Clear();
- nextAgentId = 0;
- AIRoomMemoryManager.ClearAllMemories();
- }
- /// <summary>
- /// Resets agents for a new maze
- /// </summary>
- public void ResetForNewMaze()
- {
- ClearAllAgents();
- if (isInitialized)
- {
- StartCoroutine(SpawnAgentsCoroutine(initialAgentCount));
- }
- }
- /// <summary>
- /// Gets UI info about agents
- /// </summary>
- public string GetAgentStats()
- {
- int total = activeAgents.Count;
- string stats = $"Total Agents: {total}\n";
- var typeGroups = new Dictionary<string, int>();
- foreach (var agent in activeAgents)
- {
- if (!typeGroups.ContainsKey(agent.CharacterType))
- {
- typeGroups[agent.CharacterType] = 0;
- }
- typeGroups[agent.CharacterType]++;
- }
- foreach (var kvp in typeGroups)
- {
- var memory = AIRoomMemoryManager.GetMemory(kvp.Key);
- stats += $"{kvp.Key}: {kvp.Value} agents, {memory.VisitedCount} rooms explored\n";
- }
- return stats;
- }
- /// <summary>
- /// Gets the initial agent count setting
- /// </summary>
- public int InitialAgentCount => initialAgentCount;
- /// <summary>
- /// Sets the initial agent count (affects next reset)
- /// </summary>
- public void SetInitialAgentCount(int count)
- {
- initialAgentCount = count;
- }
- /// <summary>
- /// Gets the count of active agents
- /// </summary>
- public int GetActiveAgentCount()
- {
- return activeAgents.Count;
- }
- /// <summary>
- /// Gets the list of active agents
- /// </summary>
- public List<AIAgent> GetActiveAgents()
- {
- return activeAgents;
- }
- public IReadOnlyList<AgentGroup> GetActiveGroups() => activeGroups;
- /// <summary>
- /// Updates path visibility for all active agents
- /// </summary>
- public void SetPathVisibilityForAllAgents(bool show)
- {
- showAgentPaths = show;
- foreach (var agent in activeAgents)
- {
- if (agent != null)
- {
- agent.SetShowPath(show);
- }
- }
- Debug.Log($"Path visibility set to: {show} for {activeAgents.Count} agents");
- }
- }
|