Guest User

behaviorizer snippet

a guest
Oct 13th, 2025
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.21 KB | None | 0 0
  1. public class Behaviorizer : MonoBehaviour {
  2.     public NPCController c;
  3.     Coroutine rootCo, actionCo;
  4.  
  5.     // ---------------------------------------------------------------------
  6.     // Conditions
  7.     // ---------------------------------------------------------------------
  8.  
  9.     public bool OkGo => actionCo == null;
  10.     public bool IsBusy => c.IsBusy;
  11.     public bool HasOrders => c?.orders != Orders.None;
  12.     public bool InCombat => c?.attackTargets.Count > 0;
  13.     public bool AttackReady => IsCurrentTargetValid && c.CanHitTarget;
  14.     public bool IsCurrentTargetValid => c.currentTarget && c.currentTarget != null && !c.currentTarget.IsDead;
  15.     public bool AtDestination => ((Vector2)c.transform.position - c.destination).sqrMagnitude < 0.1f;
  16.     public bool c.CanHitTargetTarget => true;
  17.  
  18.  
  19.  
  20.     // ---------------------------------------------------------------------
  21.     // Commands
  22.     // ---------------------------------------------------------------------
  23.  
  24.     public void Interrupt() { CancelAction(); }
  25.     private void StartAct(IEnumerator co) {
  26.         CancelAction();
  27.         actionCo = StartCoroutine(co);
  28.     }
  29.     private void CancelAction() {
  30.         if (actionCo != null) { StopCoroutine(actionCo); actionCo = null; }
  31.     }
  32.     private IEnumerator RunUntil(Func<bool> done) {
  33.         yield return new WaitUntil(done);
  34.         actionCo = null;
  35.     }
  36.     private IEnumerator RunWhile(Func<bool> cond, Action bodyPerFrame) {
  37.         while (cond()) { bodyPerFrame(); yield return null; }
  38.     }
  39.     private void ReturnToRoot() => actionCo = null;
  40.  
  41.     // ---------------------------------------------------------------------
  42.     // The Machine
  43.     // ---------------------------------------------------------------------
  44.  
  45.     public void Go(NPCController c) {
  46.         this.c = c;
  47.         if (rootCo != null) StopCoroutine(rootCo);
  48.         rootCo = StartCoroutine(RunRoot());
  49.     }
  50.     private IEnumerator RunRoot() {    //selectorizor
  51.         while (OkGo) {
  52.             _ = true switch {
  53.                 _ when IsBusy => StartCoroutine(DoNothing()),
  54.                 _ when InCombat => StartCoroutine(DoCombat()),
  55.                 _ => StartCoroutine(DoNonCombat()),
  56.             };
  57.  
  58.             yield return new WaitUntil(() => OkGo);
  59.         }
  60.     }
  61.  
  62.  
  63.     // ---------------------------------------------------------------------
  64.     // Branches
  65.     // ---------------------------------------------------------------------
  66.     private IEnumerator DoNothing() {
  67.         StartAct(StopMoving());
  68.         yield return RunUntil(() => !c.IsBusy);
  69.     }
  70.     private IEnumerator DoCombat() {
  71.         while (InCombat) {
  72.             if (HasOrders) {
  73.                 yield return DoOrders();
  74.                 continue;
  75.             }
  76.  
  77.             if (AttackReady) {
  78.                 StartAct(Attack(c));
  79.                 yield return RunUntil(() => !c.isAttacking || !IsCurrentTargetValid || !CanHitTarget);
  80.                 //if (!IsCurrentTargetValid) SelectClosestTarget(); // local helper below
  81.                 continue;
  82.             }
  83.  
  84.             if (IsCurrentTargetValid && !CanHitTarget) {
  85.                 StartAct(MoveToTarget(c));
  86.                 yield return new WaitUntil(() => c.CanHitTargetTarget || !IsCurrentTargetValid || HasOrders || IsBusy);
  87.                 continue;
  88.             }
  89.  
  90.             if (!IsCurrentTargetValid) {
  91.                 //SelectClosestTarget();
  92.                 if (!IsCurrentTargetValid) break;
  93.             }
  94.  
  95.             yield return null;
  96.         }
  97.         ReturnToRoot();
  98.     }
  99.     private IEnumerator DoOrders() {
  100.         while (HasOrders && !IsBusy) {
  101.             switch (c.orders) {
  102.                 case Orders.GoTo:
  103.                     StartAct(MoveToDestination(c));
  104.                     yield return new WaitUntil(() => AtDestination || !HasOrders || IsBusy);
  105.                     break;
  106.  
  107.                 case Orders.Follow:
  108.                     StartAct(Follow(c));
  109.                     // follow is continuous; bail when priorities change
  110.                     yield return new WaitUntil(() => !HasOrders || InCombat || IsBusy);
  111.                     break;
  112.  
  113.                 case Orders.Chase:
  114.                     StartAct(MoveToTarget(c));
  115.                     yield return new WaitUntil(() => c.CanHitTargetTarget || !HasOrders || !IsCurrentTargetValid || IsBusy);
  116.                     break;
  117.             }
  118.         }
  119.         ReturnToRoot();
  120.     }
  121.     private IEnumerator DoNonCombat() {
  122.         if (c.data is CivilianData) {
  123.             //wander forever until something higher-priority changes
  124.             yield return RunWhile(() => !IsBusy && !InCombat && !HasOrders, () => WanderDistrict(c));
  125.         } else if (c.data is CrewmanData) {
  126.             yield return RunWhile(() => !IsBusy && !InCombat && !HasOrders, () => WanderWorld(c));
  127.         } else { //leader
  128.             //move to next control point; when done, loop back to root
  129.             //MoveToNearestControlPoint();
  130.             WanderWorld(c);
  131.             yield return RunUntil(() => AtDestination || InCombat || HasOrders || IsBusy);
  132.         }
  133.         ReturnToRoot();
  134.     }
  135.  
  136.     // ---------------------------------------------------------------------
  137.     // Actions
  138.     // ---------------------------------------------------------------------
  139.     private IEnumerator StopMoving() { c.rb.velocity = Vector2.zero; c.animate.AnimateUpdate(Vector2.zero, 0); yield return null; }
  140.     public IEnumerator Stop(NPCController c) {
  141.         // Hold still while busy OR until velocity truly zero
  142.         while (c.IsBusy || c.rb.velocity.sqrMagnitude > 0.0001f) {
  143.             c.rb.velocity = Vector2.zero;
  144.             c.animate.AnimateUpdate(Vector2.zero, 0f);
  145.             yield return null;
  146.         }
  147.     }
  148.     public IEnumerator TurnOnCollider(NPCController c) {
  149.         if (c.physicsCollider != null) c.physicsCollider.enabled = true;
  150.         yield break;
  151.     }
  152.     public IEnumerator PurgeDeadTargets(NPCController c) {
  153.         c.attackTargets.RemoveWhere(x => x == null || !x || x.IsDead);
  154.         yield break;
  155.     }
  156.  
  157.     public IEnumerator ValidateTarget(NPCController c) {
  158.         //purge first
  159.         c.attackTargets.RemoveWhere(x => x == null || !x || x.IsDead);
  160.  
  161.         //select closest (no LINQ)
  162.         CommonController best = null;
  163.         float bestDis = float.MaxValue;
  164.         var pos = (Vector2)c.transform.position;
  165.  
  166.         foreach (var t in c.attackTargets) {
  167.             if (!t || t.IsDead) continue;
  168.             float dis = ((Vector2)t.transform.position - pos).sqrMagnitude;
  169.             if (dis < bestDis) { bestDis = dis; best = t; }
  170.         }
  171.  
  172.         c.currentTarget = best;
  173.         yield break;
  174.     }
  175.     public IEnumerator MoveToDestination(NPCController c) {
  176.         //move until we arrive or higher priority interrupts
  177.         while (!c.IsBusy) {
  178.             Vector2 diff = c.destination - (Vector2)c.transform.position;
  179.             if (diff.sqrMagnitude < 0.02f) break;
  180.  
  181.             Vector2 dir = diff.normalized;
  182.             c.rb.velocity = dir * c.moveSpeed;
  183.             c.animate.AnimateUpdate(dir, c.rb.velocity.magnitude);
  184.             yield return null;
  185.         }
  186.  
  187.         c.rb.velocity = Vector2.zero;
  188.         c.animate.AnimateUpdate(Vector2.zero, 0f);
  189.     }
Advertisement
Add Comment
Please, Sign In to add comment