AutonomousAgentBrain.cs 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace MazeGen
  5. {
  6. /// <summary>
  7. /// Autonomous agent decision-making system based on a prioritized decision tree.
  8. ///
  9. /// Each decision encapsulates:
  10. /// - A precondition (CanExecute)
  11. /// - An action (Execute)
  12. /// - Narration (Think)
  13. ///
  14. /// The decision tree evaluates decisions in priority order and returns the first
  15. /// one that can execute. This keeps behavior and narration synchronized and makes
  16. /// the agent's decisions easy to understand and extend.
  17. ///
  18. /// To extend agent behavior, simply create a new AgentDecision subclass and add
  19. /// it to BuildDecisionTree(). No need to modify the Update loop or narration logic.
  20. ///
  21. /// All timing runs off SimulationClock, not Time.deltaTime, so paused simulation
  22. /// genuinely freezes every agent.
  23. /// </summary>
  24. public class AutonomousAgentBrain : MonoBehaviour
  25. {
  26. private MazeAgentController _controller;
  27. private MazeGenerator _generator;
  28. private CombatDirector _director;
  29. private float _decisionInterval;
  30. private float _exploreChance;
  31. private float _retreatThreshold = 1.5f;
  32. private Func<bool> _isLevelEnded;
  33. private float _timer;
  34. private AgentDecisionTree _decisionTree;
  35. public void Initialize(MazeAgentController controller, MazeGenerator generator, CombatDirector director, float initialDelay, float decisionInterval, float exploreChance, float retreatThreshold, Func<bool> isLevelEnded)
  36. {
  37. _controller = controller;
  38. _generator = generator;
  39. _director = director;
  40. _decisionInterval = Mathf.Max(0.05f, decisionInterval);
  41. _exploreChance = Mathf.Clamp01(exploreChance);
  42. _retreatThreshold = Mathf.Max(0, retreatThreshold);
  43. _isLevelEnded = isLevelEnded;
  44. _timer = Mathf.Max(0f, initialDelay);
  45. // Build the decision tree in priority order
  46. BuildDecisionTree();
  47. }
  48. /// <summary>
  49. /// Constructs the decision tree with all available decisions in priority order.
  50. /// Add new decisions here to extend agent behavior without modifying Update().
  51. ///
  52. /// Example of adding a custom decision:
  53. /// _decisionTree.Add(new MyCustomDecision(...));
  54. /// </summary>
  55. private void BuildDecisionTree()
  56. {
  57. _decisionTree = new AgentDecisionTree();
  58. // Priority order:
  59. // 1. Heal if needed (health is more important than combat)
  60. _decisionTree.Add(new UseHealthPotionDecision());
  61. // 2. Handle existing fights (join or sneak past)
  62. _decisionTree.Add(new HandleExistingFightDecision(_director, _retreatThreshold));
  63. // 3. Declare new fights if appropriate
  64. _decisionTree.Add(new DeclareFightDecision(_director, _retreatThreshold));
  65. // 4. Sneak away if threatened
  66. _decisionTree.Add(new SneakAwayDecision(_director, _retreatThreshold));
  67. // 5. Catch-all: move to next room (always succeeds)
  68. _decisionTree.Add(new MoveOnDecision(_generator, _controller, _exploreChance, _retreatThreshold));
  69. }
  70. private void Update()
  71. {
  72. MazeAgent agent = _controller?.Agent;
  73. if (agent == null || agent.IsFinished || agent.IsDead) return;
  74. if (_isLevelEnded != null && _isLevelEnded()) return;
  75. // Committed to a fight: the CombatDirector owns this agent until it resolves.
  76. if (agent.IsEngaged) return;
  77. _timer -= SimulationClock.DeltaTime;
  78. if (_timer > 0f) return;
  79. _timer = _decisionInterval;
  80. // Evaluate decision tree: returns true if a decision executed and was narrated
  81. _decisionTree?.TryDecide(agent);
  82. }
  83. }
  84. }