게임/고도엔진 문법

26. [고도 엔진] 열거형(enum)을 활용한 캐릭터 상태 기계(State Machine) 만들기

밀감감 2026. 7. 14. 12:00
반응형

 

이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.

 

이번에는 캐릭터를 조작하는 게임을 만들다 보면 is_running, is_jumping, is_attacking 같은 참/거짓(boolean) 변수가 끝도 없이 늘어나는 경험을 하게 됩니다. "공격 중에는 점프할 수 없다", "죽어있을 때는 움직일 수 없다" 같은 예외 처리를 하려다 보면 if 문이 거미줄처럼 얽힌 이른바 '스파게티 코드'가 탄생하죠.

이러한 문제를 깔끔하게 해결해 주는 디자인 패턴이 바로 상태 기계(Finite State Machine, FSM)입니다. 캐릭터가 오직 '하나의 상태'만 가질 수 있도록 열거형(enum)으로 묶어서 관리하는 방법을 알아보겠습니다.

1. enum 선언과 상태 변수 만들기

열거형(enum)은 연관된 상수들의 집합에 이름을 붙여주는 기능입니다. 0, 1, 2, 3 같은 숫자 대신 IDLE, RUN, JUMP 같은 읽기 쉬운 이름을 사용하여 상태를 정의할 수 있습니다.

스크립트 최상단에 캐릭터가 가질 수 있는 상태들을 enum으로 정의하고, 현재 상태를 저장할 변수를 하나 만들어 줍니다.

GDScript
 
# 1. 캐릭터가 가질 수 있는 모든 상태를 enum으로 정의합니다.
enum State {
    IDLE,   # 대기
    RUN,    # 달리기
    JUMP,   # 점프
    ATTACK, # 공격
    DEAD    # 사망
}

# 2. 현재 상태를 저장할 변수를 만들고, 기본값을 IDLE(대기)로 설정합니다.
var current_state: State = State.IDLE

2. match 문으로 상태별 동작 분리하기 (행동 로직)

이제 매 프레임마다 실행되는 _physics_process (또는 _process) 함수 안에서 현재 상태가 무엇인지 확인하고, 그에 맞는 행동만 하도록 코드를 분리합니다.

이때 if-elif 문 대신 고도 엔진의 match 문(다른 언어의 switch-case와 유사)을 사용하면 코드를 훨씬 직관적으로 작성할 수 있습니다.

GDScript
 
func _physics_process(delta):
    # 현재 상태에 따라 실행할 코드를 깔끔하게 나눕니다.
    match current_state:
        State.IDLE:
            # 대기 중일 때의 애니메이션 재생 및 마찰력 적용
            velocity.x = move_toward(velocity.x, 0, SPEED)
            
        State.RUN:
            # 달릴 때의 이동 로직
            var direction = Input.get_axis("ui_left", "ui_right")
            velocity.x = direction * SPEED
            
        State.JUMP:
            # 공중에 있을 때의 중력 적용 로직
            velocity.y += gravity * delta
            
        State.ATTACK:
            # 공격 중에는 이동하지 못하도록 속도를 0으로 고정
            velocity.x = 0
            
        State.DEAD:
            # 사망 시 아무 동작도 하지 않고 함수 종료
            return
            
    move_and_slide()
    check_transitions() # 상태 전환을 검사하는 함수 호출

3. 상태 전환하기 (조건 검사)

행동 로직을 만들었으니, 언제 다른 상태로 넘어갈지(Transition) 규칙을 정해줘야 합니다. 코드가 섞이지 않도록 상태 전환을 검사하는 함수를 따로 분리하는 것이 좋습니다.

GDScript
 
func check_transitions():
    # 사망 상태라면 다른 어떤 상태로도 변할 수 없습니다.
    if current_state == State.DEAD:
        return
        
    match current_state:
        State.IDLE:
            # 이동 키를 누르면 달리기 상태로 전환
            if Input.get_axis("ui_left", "ui_right") != 0:
                change_state(State.RUN)
            # 점프 키를 누르면 점프 상태로 전환
            elif Input.is_action_just_pressed("ui_accept"):
                change_state(State.JUMP)
                
        State.RUN:
            # 이동을 멈추면 다시 대기 상태로 전환
            if Input.get_axis("ui_left", "ui_right") == 0:
                change_state(State.IDLE)
            elif Input.is_action_just_pressed("ui_accept"):
                change_state(State.JUMP)

# 상태가 바뀔 때 한 번만 실행되어야 하는 코드(애니메이션 변경 등)를 처리하는 헬퍼 함수
func change_state(new_state: State):
    current_state = new_state
    
    match current_state:
        State.IDLE:
            $AnimationPlayer.play("idle")
        State.RUN:
            $AnimationPlayer.play("run")
        State.JUMP:
            velocity.y = JUMP_VELOCITY
            $AnimationPlayer.play("jump")

한눈에 보는 상태 기계(FSM) 핵심 요약

키워드 / 기능 기능 설명 상태 기계에서의 역할
enum 관련된 숫자 상수들에 이름을 붙여 그룹화합니다. IDLE, RUN 등 캐릭터가 가질 수 있는 상태의 종류를 정의합니다.
상태 변수 현재 어떤 상태인지 저장하는 하나의 변수입니다. 여러 개의 boolean 변수(is_running 등)를 단 하나의 변수로 대체합니다.
match 문 변수의 값에 따라 실행할 코드를 분기(나눔)합니다. 현재 상태에 따라 이동, 애니메이션 등의 동작을 다르게 처리합니다.
상태 전환 (Transition) 특정 조건이 만족되었을 때 상태 변수의 값을 바꿉니다. 키 입력이나 땅 착지 등의 이벤트를 감지하여 다른 상태로 넘겨줍니다.
반응형