본문 바로가기
-----ETC-----/C++ 게임 개발 시리즈

[C++ 게임 개발 시리즈] Day 18: 간단한 AI 기초 (상태 머신)

by cogito21_cpp 2024. 8. 1.
반응형

간단한 AI 기초 (상태 머신)

게임 개발에서 인공지능(AI)은 NPC(Non-Player Character)나 적 캐릭터의 행동을 제어하는 중요한 요소입니다. 상태 머신(State Machine)은 AI를 구현하는 데 사용되는 일반적인 방법입니다. 상태 머신은 객체가 여러 상태 중 하나에 있을 수 있고, 특정 이벤트나 조건에 따라 상태가 전환되는 모델입니다.

상태 머신 기초

상태 머신은 다음과 같은 구성 요소로 이루어집니다:

  1. 상태(State): 객체가 가질 수 있는 다양한 상태를 나타냅니다.
  2. 전환(Transition): 한 상태에서 다른 상태로의 변화입니다.
  3. 이벤트(Event): 상태 전환을 트리거하는 조건이나 사건입니다.

간단한 AI 상태 머신 구현

다음 예제에서는 NPC가 정지, 이동, 공격의 세 가지 상태를 가질 수 있는 간단한 AI 상태 머신을 구현합니다.

 

상태 인터페이스 정의

class AIState {
public:
    virtual ~AIState() {}
    virtual void enter() = 0;
    virtual void handleEvent(sf::Event& event) = 0;
    virtual void update(float deltaTime) = 0;
    virtual void render(sf::RenderWindow& window) = 0;
};

 

NPC 클래스 정의

NPC 클래스는 현재 상태를 보관하고, 상태 전환을 처리합니다.

class NPC {
public:
    NPC() : currentState(nullptr) {}

    void changeState(AIState* state) {
        if (currentState != nullptr) {
            currentState->exit();
        }
        currentState = state;
        currentState->enter();
    }

    void handleEvent(sf::Event& event) {
        if (currentState != nullptr) {
            currentState->handleEvent(event);
        }
    }

    void update(float deltaTime) {
        if (currentState != nullptr) {
            currentState->update(deltaTime);
        }
    }

    void render(sf::RenderWindow& window) {
        if (currentState != nullptr) {
            currentState->render(window);
        }
    }

private:
    AIState* currentState;
};

 

구체적인 상태 클래스 정의

다음으로, NPC의 구체적인 상태 클래스를 정의합니다. 여기서는 정지 상태, 이동 상태, 공격 상태를 구현합니다.

class IdleState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Idle State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 정지 상태에서는 아무 것도 하지 않음
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Idle State" << std::endl;
    }
};

class MoveState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Move State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 이동 로직 구현
        std::cout << "Moving..." << std::endl;
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Move State" << std::endl;
    }
};

class AttackState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Attack State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 공격 로직 구현
        std::cout << "Attacking..." << std::endl;
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Attack State" << std::endl;
    }
};

 

전체 코드

다음은 전체 코드입니다. 이 코드는 NPC가 상태 머신을 사용하여 상태 전환을 처리하는 예제입니다.

#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <iostream>

class AIState {
public:
    virtual ~AIState() {}
    virtual void enter() = 0;
    virtual void handleEvent(sf::Event& event) = 0;
    virtual void update(float deltaTime) = 0;
    virtual void render(sf::RenderWindow& window) = 0;
    virtual void exit() = 0;
};

class IdleState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Idle State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 정지 상태에서는 아무 것도 하지 않음
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Idle State" << std::endl;
    }
};

class MoveState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Move State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 이동 로직 구현
        std::cout << "Moving..." << std::endl;
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Move State" << std::endl;
    }
};

class AttackState : public AIState {
public:
    void enter() override {
        std::cout << "Entering Attack State" << std::endl;
    }

    void handleEvent(sf::Event& event) override {}

    void update(float deltaTime) override {
        // 공격 로직 구현
        std::cout << "Attacking..." << std::endl;
    }

    void render(sf::RenderWindow& window) override {}

    void exit() override {
        std::cout << "Exiting Attack State" << std::endl;
    }
};

class NPC {
public:
    NPC() : currentState(nullptr) {}

    void changeState(AIState* state) {
        if (currentState != nullptr) {
            currentState->exit();
        }
        currentState = state;
        currentState->enter();
    }

    void handleEvent(sf::Event& event) {
        if (currentState != nullptr) {
            currentState->handleEvent(event);
        }
    }

    void update(float deltaTime) {
        if (currentState != nullptr) {
            currentState->update(deltaTime);
        }
    }

    void render(sf::RenderWindow& window) {
        if (currentState != nullptr) {
            currentState->render(window);
        }
    }

private:
    AIState* currentState;
};

int main() {
    // SFML 창 생성
    sf::RenderWindow window(sf::VideoMode(800, 600), "AI State Machine Example");

    NPC npc;
    IdleState idleState;
    MoveState moveState;
    AttackState attackState;

    npc.changeState(&idleState);

    // 게임 루프
    sf::Clock clock;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();

            npc.handleEvent(event);

            // 상태 전환을 위한 임시 로직
            if (event.type == sf::Event::KeyPressed) {
                if (event.key.code == sf::Keyboard::I) {
                    npc.changeState(&idleState);
                } else if (event.key.code == sf::Keyboard::M) {
                    npc.changeState(&moveState);
                } else if (event.key.code == sf::Keyboard::A) {
                    npc.changeState(&attackState);
                }
            }
        }

        float deltaTime = clock.restart().asSeconds();
        npc.update(deltaTime);

        // 화면 지우기
        window.clear();

        // NPC 렌더링
        npc.render(window);

        // 화면에 그리기
        window.display();
    }

    return 0;
}

 

결론

오늘은 상태 머신을 사용하여 간단한 AI를 구현하는 방법을 학습했습니다. 상태 머신은 AI의 상태 전환을 관리하는 데 유용한 패턴입니다. 이를 통해 게임에서 NPC나 적 캐릭터의 다양한 행동을 제어할 수 있습니다. 질문이나 추가적인 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "Day 19: 경로 찾기 알고리즘 (A* 알고리즘)"에 대해 학습하겠습니다.

반응형