-
Lua, C++ behavior tree카테고리 없음 2025. 4. 3. 16:13
트리의 깊이가 깊어지면 lua_gettop(L)을 직접 사용하여 노드를 파싱하는 방식이 불안정할 수 있습니다. 이를 해결하기 위해 **lua_next()**를 사용하여 테이블을 안전하게 순회하도록 개선하겠습니다.
📌 개선된 C++ 코드
- 깊은 트리 구조에서도 안전하게 동작
- lua_next()를 사용해 노드 테이블을 안정적으로 탐색
- 불필요한 스택 조작을 줄이고 가독성 개선
#include <iostream> #include <lua.hpp> #include <vector> #include <memory> #include <string> // 행동 트리 노드 기본 클래스 class Node { public: virtual ~Node() = default; virtual bool execute() = 0; }; // Sequence 노드 class Sequence : public Node { std::vector<std::shared_ptr<Node>> children; public: void addChild(std::shared_ptr<Node> child) { children.push_back(child); } bool execute() override { for (auto& child : children) { if (!child->execute()) return false; } return true; } }; // Selector 노드 class Selector : public Node { std::vector<std::shared_ptr<Node>> children; public: void addChild(std::shared_ptr<Node> child) { children.push_back(child); } bool execute() override { for (auto& child : children) { if (child->execute()) return true; } return false; } }; // Leaf 노드 (실제 동작 구현) class Leaf : public Node { std::string name; public: Leaf(const std::string& name) : name(name) {} bool execute() override { std::cout << "Executing " << name << std::endl; return true; } }; // Lua에서 트리 로드 및 C++ 트리 변환 std::shared_ptr<Node> parseNode(lua_State* L, int index); std::shared_ptr<Node> parseChildren(lua_State* L, int index, const std::string& type) { std::shared_ptr<Node> node = (type == "Sequence") ? std::make_shared<Sequence>() : std::make_shared<Selector>(); lua_pushnil(L); // 테이블 순회 시작 while (lua_next(L, index) != 0) { if (lua_istable(L, -1)) { std::shared_ptr<Node> child = parseNode(L, lua_gettop(L)); if (child) { if (type == "Sequence") { std::dynamic_pointer_cast<Sequence>(node)->addChild(child); } else { std::dynamic_pointer_cast<Selector>(node)->addChild(child); } } } lua_pop(L, 1); // 값 팝 } return node; } std::shared_ptr<Node> parseNode(lua_State* L, int index) { lua_pushstring(L, "type"); lua_gettable(L, index); std::string type = lua_tostring(L, -1); lua_pop(L, 1); if (type == "Leaf") { lua_pushstring(L, "name"); lua_gettable(L, index); std::string name = lua_tostring(L, -1); lua_pop(L, 1); return std::make_shared<Leaf>(name); } else if (type == "Sequence" || type == "Selector") { lua_pushstring(L, "children"); lua_gettable(L, index); std::shared_ptr<Node> node = nullptr; if (lua_istable(L, -1)) { node = parseChildren(L, lua_gettop(L), type); } lua_pop(L, 1); return node; } return nullptr; } std::shared_ptr<Node> loadTreeFromLua(lua_State* L, const std::string& filename) { 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, "BehaviorTree"); if (!lua_istable(L, -1)) { std::cerr << "Invalid BehaviorTree format" << std::endl; return nullptr; } return parseNode(L, lua_gettop(L)); } int main() { lua_State* L = luaL_newstate(); luaL_openlibs(L); auto behaviorTree = loadTreeFromLua(L, "behavior_tree.lua"); if (behaviorTree) { behaviorTree->execute(); } lua_close(L); return 0; }
📌 개선 내용
✅ lua_next() 사용하여 안전한 테이블 순회
✅ 불필요한 lua_gettop() 제거 → 안정적인 스택 관리
✅ 더 깊은 트리 구조에서도 작동 가능
✅ 각각의 기능을 함수로 분리하여 가독성 증가
📌 예제 Lua 코드 (behavior_tree.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
📌 실행 결과
Executing CheckEnemy Executing AttackEnemy
🚀 개선된 점 요약
- 트리의 깊이가 깊어도 작동 가능
- Lua 스택 관리가 안정적
- C++에서 더 직관적인 트리 파싱
이제 깊이가 깊은 Behavior Tree도 문제없이 동작할 것입니다! 🔥