| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace MazeGen
- {
- /// <summary>
- /// Autonomous agent decision-making system based on a prioritized decision tree.
- ///
- /// Each decision encapsulates:
- /// - A precondition (CanExecute)
- /// - An action (Execute)
- /// - Narration (Think)
- ///
- /// The decision tree evaluates decisions in priority order and returns the first
- /// one that can execute. This keeps behavior and narration synchronized and makes
- /// the agent's decisions easy to understand and extend.
- ///
- /// To extend agent behavior, simply create a new AgentDecision subclass and add
- /// it to BuildDecisionTree(). No need to modify the Update loop or narration logic.
- ///
- /// All timing runs off SimulationClock, not Time.deltaTime, so paused simulation
- /// genuinely freezes every agent.
- /// </summary>
- public class AutonomousAgentBrain : MonoBehaviour
- {
- private MazeAgentController _controller;
- private MazeGenerator _generator;
- private CombatDirector _director;
- private float _decisionInterval;
- private float _exploreChance;
- private float _retreatThreshold = 1.5f;
- private Func<bool> _isLevelEnded;
- private float _timer;
- private AgentDecisionTree _decisionTree;
- public void Initialize(MazeAgentController controller, MazeGenerator generator, CombatDirector director, float initialDelay, float decisionInterval, float exploreChance, float retreatThreshold, Func<bool> isLevelEnded)
- {
- _controller = controller;
- _generator = generator;
- _director = director;
- _decisionInterval = Mathf.Max(0.05f, decisionInterval);
- _exploreChance = Mathf.Clamp01(exploreChance);
- _retreatThreshold = Mathf.Max(0, retreatThreshold);
- _isLevelEnded = isLevelEnded;
- _timer = Mathf.Max(0f, initialDelay);
- // Build the decision tree in priority order
- BuildDecisionTree();
- }
- /// <summary>
- /// Constructs the decision tree with all available decisions in priority order.
- /// Add new decisions here to extend agent behavior without modifying Update().
- ///
- /// Example of adding a custom decision:
- /// _decisionTree.Add(new MyCustomDecision(...));
- /// </summary>
- private void BuildDecisionTree()
- {
- _decisionTree = new AgentDecisionTree();
- // Priority order:
- // 1. Heal if needed (health is more important than combat)
- _decisionTree.Add(new UseHealthPotionDecision());
- // 2. Handle existing fights (join or sneak past)
- _decisionTree.Add(new HandleExistingFightDecision(_director, _retreatThreshold));
- // 3. Declare new fights if appropriate
- _decisionTree.Add(new DeclareFightDecision(_director, _retreatThreshold));
- // 4. Sneak away if threatened
- _decisionTree.Add(new SneakAwayDecision(_director, _retreatThreshold));
- // 5. Catch-all: move to next room (always succeeds)
- _decisionTree.Add(new MoveOnDecision(_generator, _controller, _exploreChance, _retreatThreshold));
- }
- private void Update()
- {
- MazeAgent agent = _controller?.Agent;
- if (agent == null || agent.IsFinished || agent.IsDead) return;
- if (_isLevelEnded != null && _isLevelEnded()) return;
- // Committed to a fight: the CombatDirector owns this agent until it resolves.
- if (agent.IsEngaged) return;
- _timer -= SimulationClock.DeltaTime;
- if (_timer > 0f) return;
- _timer = _decisionInterval;
- // Evaluate decision tree: returns true if a decision executed and was narrated
- _decisionTree?.TryDecide(agent);
- }
- }
- }
|