--- id: wiki-2026-0508-state-machine-and-phase-transiti title: State Machine and Phase Transition Events category: 10_Wiki/Topics status: verified canonical_id: self aliases: [FSM, Game State Machine, Phase Events] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [game-design, fsm, simulation, architecture] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: xstate --- # State Machine and Phase Transition Events ## 매 한 줄 > **"매 game logic 의 FSM-first 의 architecture"**. 매 boss phase, match flow, sector breach, AI behavior 의 deterministic state model — 매 transition event 의 explicit emission 으로 telemetry/animation/audio 의 synchronize. 매 2026 standard: XState v5 + actor model + visualizer. ## 매 핵심 ### 매 정의 - **State**: 매 finite labeled status (idle, charging, attacking, dead). - **Event**: external trigger (input, timer, hp_threshold). - **Transition**: state × event → next_state + side_effects. - **Phase event**: cross-cutting broadcast (e.g. "phase_2_started") for multiple subsystems. ### 매 핵심 패턴 - **Hierarchical states**: parent/child (combat → charging → release). - **Parallel states**: 매 동시 sub-FSM (movement + weapon + status). - **Guards**: 매 transition 의 conditional gate. - **Actions**: entry/exit/transition side effects. ### 매 응용 1. Boss fight phase logic. 2. Multiplayer match flow (lobby → loading → playing → results). 3. Quest/dialogue branching. 4. Network connection lifecycle. ## 💻 패턴 ### XState v5 boss FSM ```typescript import { setup, createActor } from 'xstate'; const bossMachine = setup({ types: { context: {} as { hp: number }, events: {} as { type: 'damage'; amount: number } | { type: 'tick' } }, actions: { broadcastPhase: ({ context }) => emit('phase_event', { phase: context.hp > 5000 ? 1 : context.hp > 2000 ? 2 : 3 }), }, guards: { phase2: ({ context }) => context.hp <= 5000 && context.hp > 2000, phase3: ({ context }) => context.hp <= 2000 && context.hp > 0, dead: ({ context }) => context.hp <= 0, }, }).createMachine({ id: 'boss', initial: 'phase1', context: { hp: 10_000 }, states: { phase1: { on: { damage: { actions: 'broadcastPhase', guard: 'phase2', target: 'phase2' } } }, phase2: { on: { damage: { actions: 'broadcastPhase', guard: 'phase3', target: 'phase3' } } }, phase3: { on: { damage: { guard: 'dead', target: 'dead' } } }, dead: { type: 'final' }, }, }); export const bossActor = createActor(bossMachine); ``` ### Hand-rolled FSM (no deps) ```typescript type State = 'idle' | 'charging' | 'attacking' | 'cooldown'; type Event = 'see_target' | 'charge_done' | 'attack_done' | 'lose_target'; const TABLE: Record>> = { idle: { see_target: 'charging' }, charging: { charge_done: 'attacking', lose_target: 'idle' }, attacking: { attack_done: 'cooldown' }, cooldown: { charge_done: 'idle' }, }; export class FSM { state: State = 'idle'; send(e: Event) { const next = TABLE[this.state][e]; if (next) { this.exit(this.state); this.state = next; this.enter(next); } } enter(s: State) { /* hooks */ } exit(s: State) { /* hooks */ } } ``` ### Phase event bus ```typescript type PhaseEvent = | { kind: 'phase_changed'; from: number; to: number; entityId: string } | { kind: 'sector_state_changed'; sectorId: string; state: string } | { kind: 'match_state_changed'; matchId: string; state: string }; class EventBus { private listeners = new Map void>>(); emit(e: PhaseEvent) { for (const fn of this.listeners.get(e.kind) ?? []) fn(e); } on(kind: PhaseEvent['kind'], fn: (e: PhaseEvent) => void) { const arr = this.listeners.get(kind) ?? []; arr.push(fn); this.listeners.set(kind, arr); } } export const bus = new EventBus(); ``` ### Guard with cooldown ```typescript export function withCooldown(ms: number) { let last = 0; return () => { const now = Date.now(); if (now - last < ms) return false; last = now; return true; }; } ``` ### Parallel states (movement + weapon) ```typescript import { setup, createMachine } from 'xstate'; const player = createMachine({ id: 'player', type: 'parallel', states: { movement: { initial: 'idle', states: { idle: { on: { move: 'running' } }, running: { on: { stop: 'idle' } }, }, }, weapon: { initial: 'unarmed', states: { unarmed: { on: { equip: 'armed' } }, armed: { on: { fire: 'cooldown' } }, cooldown: { after: { 500: 'armed' } }, }, }, }, }); ``` ### FSM 의 telemetry ```typescript export function logTransition(from: string, to: string, event: string) { console.log(JSON.stringify({ ts: Date.now(), kind: 'fsm.transition', from, to, event })); } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 simple 3-state | Hand-rolled enum + switch. | | 매 hierarchical / parallel | XState v5 + visualizer. | | 매 networked sync | Authoritative server FSM + delta replication. | | 매 hot path AI | Behavior Tree (FSM 의 not 적합). | **기본값**: XState v5 for non-trivial; enum FSM for simple. ## 🔗 Graph - 부모: [[Fixed Time Step vs Variable Time Step]] · [[Procedural-Rhetoric|Procedural Rhetoric (In Gaming)]] - 변형: [[Stage-Director-and-World-Tension-Scaling]] · [[Staggered-Firing-Logic-and-Phase-Offset]] - 응용: [[War-Commander-Combat-Ecosystem]] · [[Sector]] - Adjacent: [[Player-Experience-Modeling]] · [[Roguelike Procedural Generation]] ## 🤖 LLM 활용 **언제**: FSM 의 design review, unreachable state 의 detect, transition coverage 의 audit. **언제 X**: 매 hot loop transition (deterministic code). ## ❌ 안티패턴 - **Boolean flag soup**: 매 implicit state — combinatorial bug surface. - **Missing exit action**: 매 leak 의 timer/listener. - **Unbounded events**: 매 event queue 의 backlog — frame stall. ## 🧪 검증 / 중복 - Verified: XState v5 docs (2025), GDC AI Summit talks, Unreal Behavior Tree docs. - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — XState v5 boss + parallel state + bus 추가 |