์นดํ
๊ณ ๋ฆฌ ์์
Lua, C++ behavior tree ์ต์ ํ
denny
2025. 4. 3. 16:25
๐ ์ฑ๋ฅ ์ต์ ํ: Behavior Tree ๊ฐ์ ํ๊ธฐ
๊ธฐ์กด ์ฝ๋์์ ์ฑ๋ฅ ์ด์๊ฐ ๋ฐ์ํ ์ ์๋ ๋ถ๋ถ์ ๊ฐ์ ํฉ๋๋ค.
๐ ์ฃผ์ ์ฑ๋ฅ ๋ณ๋ชฉ ์์ธ
- ๋ฌธ์์ด ๋น๊ต (if (name == "AttackEnemy"))
- ๋ฌธ์์ด ๋น๊ต๋ ๋น์ฉ์ด ํฌ๋ฏ๋ก, enum์ ์ฌ์ฉํ์ฌ ๋ ธ๋ ํ์ ์ ๊ตฌ๋ถํ๋ฉด ์๋๋ฅผ ํฅ์ํ ์ ์์ต๋๋ค.
- std::shared_ptr์ ๊ณผ๋ํ ์ฌ์ฉ
- std::shared_ptr๋ ๋ ํผ๋ฐ์ค ์นด์ดํ ์ ์ํด ์ฑ๋ฅ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- std::unique_ptr๋ฅผ ์ฌ์ฉํ์ฌ ๋ถํ์ํ ๊ณต์ ๋ฅผ ์ค์ผ ์ ์์.
- Lua ํ
์ด๋ธ ์ ๊ทผ ์ฑ๋ฅ ๊ฐ์
- lua_getfield()๋ฅผ ์ฌ์ฉํ๋ฉด lua_pushstring() + lua_gettable() ์กฐํฉ๋ณด๋ค ๋น ๋ฅด๊ฒ ํ๋ ์ ๊ทผ ๊ฐ๋ฅ.
โ ์ต์ ํ๋ C++ ์ฝ๋
1. NodeType enum ์ถ๊ฐ (๋ฌธ์์ด ๋น๊ต ์ ๊ฑฐ)
enum class NodeType {
Leaf,
Sequence,
Selector
};
enum class LeafType {
Unknown,
CheckEnemy,
AttackEnemy,
FallbackDefense,
PatrolArea
};
2. Leaf ๋ ธ๋์์ LeafType์ ์ฌ์ฉํ์ฌ ์ต์ ํ
class Leaf : public Node {
LeafType type;
Monster* monster;
public:
Leaf(LeafType type, Monster* monster) : type(type), monster(monster) {}
bool execute() override {
switch (type) {
case LeafType::AttackEnemy:
if (monster) {
monster->attack();
} else {
std::cout << "No monster assigned to AttackEnemy!" << std::endl;
}
break;
case LeafType::CheckEnemy:
std::cout << "Checking for enemies...\n";
break;
case LeafType::FallbackDefense:
std::cout << "Falling back to defense mode!\n";
break;
case LeafType::PatrolArea:
std::cout << "Patrolling area...\n";
break;
default:
std::cout << "Unknown action!\n";
break;
}
return true;
}
};
3. LeafType์ ํจ์จ์ ์ผ๋ก ๋งคํ
๋ฌธ์์ด์ ๋น๊ตํ์ง ์๊ณ std::unordered_map์ ์ฌ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ๋งคํํฉ๋๋ค.
std::unordered_map<std::string, LeafType> leafTypeMap = {
{"CheckEnemy", LeafType::CheckEnemy},
{"AttackEnemy", LeafType::AttackEnemy},
{"FallbackDefense", LeafType::FallbackDefense},
{"PatrolArea", LeafType::PatrolArea}
};
4. parseNode์์ ์ต์ ํ๋ ์ฒ๋ฆฌ
- Lua ํ ์ด๋ธ ์ ๊ทผ ์ต์ ํ (lua_getfield() ์ฌ์ฉ)
- ๋ฌธ์์ด ๋น๊ต ์ ๊ฑฐ (LeafType ๋งคํ)
std::unique_ptr<Node> parseNode(lua_State* L, int index, Monster* monster);
std::unique_ptr<Node> parseChildren(lua_State* L, int index, NodeType type, Monster* monster) {
auto node = (type == NodeType::Sequence) ? std::make_unique<Sequence>() : std::make_unique<Selector>();
lua_pushnil(L); // ํ
์ด๋ธ ์ํ ์์
while (lua_next(L, index) != 0) {
if (lua_istable(L, -1)) {
auto child = parseNode(L, lua_gettop(L), monster);
if (child) {
if (type == NodeType::Sequence) {
dynamic_cast<Sequence*>(node.get())->addChild(std::move(child));
} else {
dynamic_cast<Selector*>(node.get())->addChild(std::move(child));
}
}
}
lua_pop(L, 1);
}
return node;
}
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);
auto it = leafTypeMap.find(name);
LeafType leafType = (it != leafTypeMap.end()) ? it->second : LeafType::Unknown;
return std::make_unique<Leaf>(leafType, monster);
}
else if (typeStr == "Sequence" || typeStr == "Selector") {
lua_getfield(L, index, "children");
std::unique_ptr<Node> node = nullptr;
if (lua_istable(L, -1)) {
node = parseChildren(L, lua_gettop(L), (typeStr == "Sequence") ? NodeType::Sequence : NodeType::Selector, monster);
}
lua_pop(L, 1);
return node;
}
return nullptr;
}
5. 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;
}
๐ ์ต์ ํ ์์ฝ
์ต์ ํ ํญ๋ชฉ ๊ธฐ์กด ๋ณ๊ฒฝ
๋ฌธ์์ด ๋น๊ต | "AttackEnemy" == name | std::unordered_map<std::string, LeafType> ์ฌ์ฉ |
Lua ํ ์ด๋ธ ์ ๊ทผ | lua_pushstring() + lua_gettable() | lua_getfield() ์ฌ์ฉ (๋ ๋น ๋ฆ) |
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ | std::shared_ptr ์ฌ์ฉ | std::unique_ptr๋ก ๋ณ๊ฒฝ |
Node ๊ด๋ฆฌ | dynamic_pointer_cast<>() | dynamic_cast<> ์ฌ์ฉํ์ฌ ๊ฐ๋ฒผ์ด ์บ์คํ |
๐ ์คํ ๊ฒฐ๊ณผ
Checking for enemies...
Goblin attacks! Remaining health: 100
Falling back to defense mode!
Patrolling area...
๐ ์ต์ข ์ ๋ฆฌ
โ
๋ฌธ์์ด ๋น๊ต ์ ๊ฑฐ (std::unordered_map ํ์ฉ)
โ
Lua ํ
์ด๋ธ ์ ๊ทผ ์ต์ ํ (lua_getfield ์ฌ์ฉ)
โ
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ฐ์ (std::shared_ptr → std::unique_ptr)
โ
Behavior Tree์ Node ํ์
์ enum class๋ก ๋ณ๊ฒฝ
์ด์ ์คํ ์๋์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ต์ ํ๋ ๊ณ ์ฑ๋ฅ Behavior Tree ์์ง์ด ๋์์ต๋๋ค! ๐๐ฅ