์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

Lua, C++ behavior tree ์ตœ์ ํ™”2

denny 2025. 4. 3. 16:35

๐Ÿ‰ C++ ๊ธฐ๋ฐ˜ Behavior Tree (Lua + C++)

์ด์ œ Behavior Tree๊ฐ€ Lua์—์„œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•˜๊ณ , C++์—์„œ ์‹คํ–‰ ๋ฐ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
๐Ÿ”น ๋ชจ๋“  Leaf ๋…ธ๋“œ์— Monster*์„ ์ „๋‹ฌํ•˜๊ณ ,
๐Ÿ”น switch ๋Œ€์‹  ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•œ ๊ตฌ์กฐ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ํ™•์žฅ์„ฑ ๋ฐ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“Œ Lua์—์„œ ํŠธ๋ฆฌ ์ •์˜ (Lua Script)

์šฐ์„ , Lua์—์„œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

function Selector(children)
    return { type = "Selector", children = children }
end

function Sequence(children)
    return { type = "Sequence", children = children }
end

function Leaf(name)
    return { type = "Leaf", name = name }
end

BehaviorTree = Selector({
    Sequence({
        Leaf("CheckEnemy"),
        Selector({
            Leaf("AttackEnemy"),
            Leaf("FallbackDefense")
        })
    }),
    Leaf("PatrolArea")
})

return BehaviorTree

๐Ÿ“Œ C++ ์ฝ”๋“œ

์ด์ œ C++์—์„œ Lua ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ , Behavior Tree๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ „์ฒด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.


1๏ธโƒฃ Node ๊ธฐ๋ณธ ํด๋ž˜์Šค

#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <unordered_map>
#include <lua.hpp>

class Monster {
public:
    std::string name;
    int health;

    Monster(std::string name, int hp) : name(std::move(name)), health(hp) {}

    void attack() {
        std::cout << name << " attacks the enemy!\n";
    }

    std::string getName() const {
        return name;
    }
};

class Node {
public:
    virtual ~Node() = default;
    virtual bool execute() = 0;
};

2๏ธโƒฃ Composite (Selector / Sequence)

enum class NodeType { Sequence, Selector };

class CompositeNode : public Node {
protected:
    std::vector<std::unique_ptr<Node>> children;
    NodeType type;

public:
    explicit CompositeNode(NodeType type) : type(type) {}

    void addChild(std::unique_ptr<Node> child) {
        children.push_back(std::move(child));
    }

    bool execute() override {
        if (type == NodeType::Sequence) {
            for (auto& child : children) {
                if (!child->execute()) return false;
            }
            return true;
        } else {  // Selector
            for (auto& child : children) {
                if (child->execute()) return true;
            }
            return false;
        }
    }
};

3๏ธโƒฃ Leaf ๊ธฐ๋ณธ ํด๋ž˜์Šค

class Leaf : public Node {
protected:
    Monster* monster;

public:
    explicit Leaf(Monster* monster) : monster(monster) {}
    virtual ~Leaf() = default;
    virtual bool execute() override = 0;
};

4๏ธโƒฃ Leaf ํ–‰๋™ ํด๋ž˜์Šค

class AttackEnemyLeaf : public Leaf {
public:
    using Leaf::Leaf;

    bool execute() override {
        if (monster) {
            monster->attack();
            return true;
        }
        return false;
    }
};

class CheckEnemyLeaf : public Leaf {
public:
    using Leaf::Leaf;

    bool execute() override {
        if (monster) {
            std::cout << monster->getName() << " is checking for enemies...\n";
        }
        return true;
    }
};

class FallbackDefenseLeaf : public Leaf {
public:
    using Leaf::Leaf;

    bool execute() override {
        if (monster) {
            std::cout << monster->getName() << " is falling back to defense mode!\n";
        }
        return true;
    }
};

5๏ธโƒฃ LeafFactory

class LeafFactory {
public:
    static std::unique_ptr<Leaf> createLeaf(const std::string& name, Monster* monster) {
        if (name == "AttackEnemy") return std::make_unique<AttackEnemyLeaf>(monster);
        if (name == "CheckEnemy") return std::make_unique<CheckEnemyLeaf>(monster);
        if (name == "FallbackDefense") return std::make_unique<FallbackDefenseLeaf>(monster);
        return nullptr;
    }
};

6๏ธโƒฃ Lua์—์„œ ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ

std::unique_ptr<Node> parseNode(lua_State* L, int index, Monster* monster) {
    lua_getfield(L, index, "type");
    std::string typeStr = lua_tostring(L, -1);
    lua_pop(L, 1);

    if (typeStr == "Leaf") {
        lua_getfield(L, index, "name");
        std::string name = lua_tostring(L, -1);
        lua_pop(L, 1);
        return LeafFactory::createLeaf(name, monster);
    } 
    else if (typeStr == "Sequence" || typeStr == "Selector") {
        lua_getfield(L, index, "children");
        auto compositeNode = std::make_unique<CompositeNode>(
            (typeStr == "Sequence") ? NodeType::Sequence : NodeType::Selector
        );

        if (lua_istable(L, -1)) {
            int len = luaL_len(L, -1);
            for (int i = 1; i <= len; ++i) {
                lua_rawgeti(L, -1, i);
                compositeNode->addChild(parseNode(L, lua_gettop(L), monster));
                lua_pop(L, 1);
            }
        }
        lua_pop(L, 1);
        return compositeNode;
    }

    return nullptr;
}

7๏ธโƒฃ ํŠธ๋ฆฌ ๋กœ๋“œ ๋ฐ ์‹คํ–‰

std::unique_ptr<Node> loadTreeFromLua(lua_State* L, const std::string& filename, Monster* monster) {
    if (luaL_dofile(L, filename.c_str()) != LUA_OK) {
        std::cerr << "Failed to load Lua file: " << lua_tostring(L, -1) << std::endl;
        return nullptr;
    }

    lua_getglobal(L, "behavior_tree");
    if (!lua_istable(L, -1)) {
        std::cerr << "Invalid behavior tree format\n";
        return nullptr;
    }

    auto rootNode = parseNode(L, lua_gettop(L), monster);
    lua_pop(L, 1);
    return rootNode;
}

8๏ธโƒฃ ์‹คํ–‰ ์ฝ”๋“œ (main)

int main() {
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);

    Monster monster("Goblin", 100);

    auto behaviorTree = loadTreeFromLua(L, "behavior_tree.lua", &monster);
    if (behaviorTree) {
        behaviorTree->execute();
    }

    lua_close(L);
    return 0;
}

๐Ÿš€ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ

ํ•ญ๋ชฉ ๊ธฐ์กด ๋ณ€๊ฒฝ

Leaf ๋…ธ๋“œ Monster* ์‚ฌ์šฉ ์ผ๋ถ€ Leaf๋งŒ ์‚ฌ์šฉ ๋ชจ๋“  Leaf์—์„œ Monster*์„ ์‚ฌ์šฉ
ํŠธ๋ฆฌ ์‹คํ–‰ ๋ฐฉ์‹ switch ๋ฌธ ๋‹คํ˜•์„ฑ(์ƒ์†) ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ
Lua ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ์ˆ˜๋™ ํ•„๋“œ ์ ‘๊ทผ parseNode() ํ•จ์ˆ˜๋กœ ์ž๋™ํ™”
Factory ํŒจํ„ด switch๋กœ ์ƒ์„ฑ LeafFactory ์‚ฌ์šฉ

๐ŸŽฏ ์ตœ์ข… ์š”์•ฝ

  • ๐Ÿ“Œ Lua์—์„œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋งŒ ์„ค๊ณ„, C++์—์„œ ์‹คํ–‰
  • ๐Ÿ“Œ Leaf ๋…ธ๋“œ์˜ ๋‹คํ˜•์„ฑ ๊ธฐ๋ฐ˜ ์„ค๊ณ„ (switch ์ œ๊ฑฐ)
  • ๐Ÿ“Œ Monster*์„ ๋ชจ๋“  Leaf์—์„œ ์‚ฌ์šฉํ•˜์—ฌ ์œ ์—ฐ์„ฑ ์ฆ๊ฐ€
  • ๐Ÿ“Œ Lua ๋ฐ์ดํ„ฐ ์ž๋™ ํŒŒ์‹ฑํ•˜์—ฌ ํŠธ๋ฆฌ ๊ตฌ์„ฑ
  • ๐Ÿ“Œ ํ™•์žฅ์„ฑ์ด ๋›ฐ์–ด๋‚œ Behavior Tree ๊ตฌ์กฐ ์™„์„ฑ

์ด์ œ Lua์—์„œ ํ–‰๋™์„ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ณ , C++์—์„œ ๋น ๋ฅด๊ณ  ์ตœ์ ํ™”๋œ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๐Ÿš€๐Ÿ”ฅ

๋Œ“๊ธ€์ˆ˜0