--- id: wiki-2026-0508-unit-stances title: Unit Stances category: 10_Wiki/Topics status: verified canonical_id: self aliases: [RTS Stances, Behavior Modes] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [rts, game-ai, behavior, finite-state-machine, gameplay] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: cpp framework: RTS-AI --- # Unit Stances ## 매 한 줄 > **"매 unit 의 player command 부재 시 매 default reaction policy."** RTS (Age of Empires, StarCraft, WARNO) 와 매 RPG party AI 에서 매 micro burden 의 reduction 을 위한 매 핵심 mechanism. 매 4 stance (aggressive · defensive · hold position · stand ground) 가 매 universal taxonomy. ## 매 핵심 ### 매 4 standard stance - **Aggressive**: 매 enemy 발견 → 매 추적 + 매 attack — 매 hunt-and-kill. - **Defensive**: 매 enemy 발견 → 매 attack 하되 매 limited pursuit (e.g. tile 5칸). - **Hold position**: 매 attack 은 매 in-range 만, 매 movement X — 매 ranged unit 의 default. - **Stand ground / Patrol**: 매 movement X + 매 attack X (or 매 patrol path). ### 매 game 별 variant - **AoE2**: aggressive · defensive · stand ground · no attack. - **StarCraft 2**: hold position · patrol · attack-move · stop. - **WARNO**: weapon-specific (e.g. ATGM hold fire) + stance. - **Total War**: skirmish · guard · cycle charge. ### 매 behavior tree 와 의 관계 - 매 stance 는 매 root selector 의 priority 의 reorder. - 매 aggressive: pursue → attack → idle. - 매 hold: attack-in-range → idle (pursue subtree 의 prune). ### 매 modern game AI 의 LLM 통합 - 2026 trend: 매 NPC commander 의 매 LLM-driven stance switch — 매 battlefield context 에 매 dynamic. - 매 latency 제약: 매 LLM call 매 5-10초 cooldown — 매 frame-level 결정 X. ### 매 응용 1. **RTS micro reduction**: 매 player 가 매 100 units 의 manual control X — 매 stance 로 매 default behavior. 2. **Tower defense**: 매 tower 의 target priority (closest · strongest · weakest) 의 stance variant. 3. **MMO pet AI**: 매 hunter pet 의 aggressive · defensive · passive. ## 💻 패턴 ### 매 Stance enum + FSM (C++) ```cpp enum class Stance { Aggressive, Defensive, HoldPosition, StandGround, }; class Unit { Stance stance = Stance::Defensive; Vec2 anchor; // 매 hold position 의 reference point float pursuit_radius = 5.0f; void Update(float dt, const World& world) { Unit* enemy = FindNearestEnemy(world); if (!enemy) { Idle(); return; } switch (stance) { case Stance::Aggressive: ChaseAndAttack(enemy); break; case Stance::Defensive: if (Distance(pos, enemy->pos) < pursuit_radius && Distance(anchor, enemy->pos) < pursuit_radius * 2) ChaseAndAttack(enemy); else if (InAttackRange(enemy)) Attack(enemy); else ReturnTo(anchor); break; case Stance::HoldPosition: if (InAttackRange(enemy)) Attack(enemy); // 매 movement X break; case Stance::StandGround: // 매 nothing break; } } }; ``` ### 매 Behavior tree 의 stance gating ```python # py_trees 스타일 def build_unit_bt(stance: str): root = Selector("root") if stance in ("aggressive", "defensive"): pursue = Sequence([CanSeeEnemy(), InPursuitRange(stance), MoveToEnemy()]) root.add_child(pursue) attack = Sequence([CanSeeEnemy(), InAttackRange(), Attack()]) root.add_child(attack) if stance == "defensive": root.add_child(Sequence([NotAtAnchor(), ReturnToAnchor()])) root.add_child(Idle()) return root ``` ### 매 Group stance broadcast (Unity, ECS) ```csharp public struct StanceComponent : IComponentData { public Stance Value; public float3 Anchor; } [BurstCompile] public partial struct ApplyStanceJob : IJobEntity { public void Execute(ref StanceComponent stance, in SelectedTag _) { stance.Value = NewStanceFromUI; } } ``` ### 매 LLM-driven dynamic stance (2026) ```python # 매 commander NPC 가 매 5초마다 매 LLM 으로 stance update import anthropic client = anthropic.Anthropic() def commander_stance(battlefield_state: dict) -> dict[str, str]: """매 unit_id → stance mapping 의 return""" resp = client.messages.create( model="claude-opus-4-7", max_tokens=512, system="당신은 RTS commander. 매 unit 별 stance 의 결정.", messages=[{"role": "user", "content": f"State: {battlefield_state}\n\nJSON: {{unit_id: stance}}"}], ) import json return json.loads(resp.content[0].text) ``` ### 매 Stance cycling hotkey ```cpp void OnHotkey_CycleStance(Unit& u) { int next = (static_cast(u.stance) + 1) % 4; u.stance = static_cast(next); PlaySound("stance_change.ogg"); UpdateUI(u); } ``` ### 매 Stance-aware pathfinding ```python def navigate(unit, target, stance): if stance == "hold_position": return None # 매 pathfinding 의 skip path = a_star(unit.pos, target) if stance == "defensive": # 매 anchor 에서 매 멀어지는 step 의 prune path = [p for p in path if dist(p, unit.anchor) < unit.pursuit_radius * 2] return path ``` ## 매 결정 기준 | 상황 | Default stance | |---|---| | 매 melee unit, frontline | Aggressive | | 매 ranged unit (archer, sniper) | Hold Position | | 매 worker / villager | Defensive (or Flee) | | 매 garrison / siege | Stand Ground | | 매 hero / mage | Defensive | **기본값**: 매 ranged 는 매 hold, 매 melee 는 매 defensive. ## 🔗 Graph - 부모: [[Behavior Tree]] - 변형: [[Hold Position]] - 응용: [[Eugen Systems 모딩 매뉴얼|WARNO]] ## 🤖 LLM 활용 **언제**: 매 strategic-level commander NPC 의 매 stance 결정 — 매 5-10초 cadence, 매 unit 별 X 매 group 별. **언제 X**: 매 frame-level 결정, 매 individual unit 의 reactive logic — 매 latency · cost 의 prohibitive. ## ❌ 안티패턴 - **매 Stance 너무 많음**: 매 8+ stance — 매 player 의 cognitive overload, 매 같은 effective behavior. - **매 Hidden stance change**: 매 attack-move 후 매 stance auto-revert X — 매 player 의 confusion. - **매 No visual feedback**: 매 stance icon · color 의 부재 — 매 current state 의 invisible. - **매 Pursuit radius 의 unbounded**: 매 defensive 가 매 map 끝까지 chase — 매 anchor distance check 누락. ## 🧪 검증 / 중복 - Verified (AoE2 manual; StarCraft 2 Battle.net wiki; Eugen Systems WARNO wiki, 2024). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — 4 stance taxonomy + LLM commander 패턴 추가 |