Создание игр » Featured, Scripting » Использование LuaBind. Часть 1.
Использование LuaBind. Часть 1.
Lua и LuaBind мы уже установили и настроили в предыдущих уроках: “Установка Lua” и “LuaBind”. Как я уже говорил в предыдущих уроках, LuaBind предназначен для упрощения работы с Lua-скриптами в С++, потому многие операции по созданию связи движок+Lua, через LuaBind сделать намного проще и быстрее. Давайте попробуем на практике что же такое LuaBind и как использовать его возможности для интеграции Lua в программы на C++. Для начала я расскажу об основных функциях и объектах LuaBind, о том, как шарить классы и переменные между C++ и Lua через LuaBind, а потом уже приведу практические примеры его использования.
Основы LuaBind
Luabind поддерживает:
- Перегруженные свободные функции
- C++ классы в Lua
- Перегруженные функции члены
- Операторы
- Свойства
- Перечисления
- Lua функции в C++
- Lua классы в C++
- Lua классы
- Наследование от Lua или C++ классов
- Переопределение виртуальных функций из классов C++
- Неявные преобразования между зарегистрированными типами
- Политики (policies) для возвращаемых значений и параметров
Для использования LuaBind надо подключить заголовочные файлы Lua и Luabind:
extern "C" { #include "lua.h" } #include <luabind/luabind.hpp> |
Все классы и функции, которые мы будем шарить с помощью LuaBind, он регистрирует в пространствах имён (а точнее таблицах Lua), либо в глобальной таблице. LuaBind называет это модулями – все объявления Вы должны делать в том или ином модуле, вот так:
module(L) // глобальные объявления // module(L, "my_library") //а вот так можно задать имя модуля [ // объявления ]; |
Можно делать и вложенные пространства имён, только для этого есть отдельный оператор:
module(L) [ namespace_("my_library") [ // объявления ] ]; |
Для биндинга функций в LuaBind есть оператор def:
template<class F, class policies> void def(const char* name, F f, const Policies&); |
Используется он, например, вот так:
module(L) [ def("myFuncNameInLua", &MyFuncNameInCPP) ]; |
Естественно, можно объявлять несколько функций подряд:
module(L) [ def("myFuncLua1", &MyFuncCPP1), def("myFuncLua2", &MyFuncCPP2), def("myFuncLua3", &MyFuncCPP3) ]; |
Для вызова Lua-функций из C++ есть call_function:
template<class Ret> Ret call_function(lua_State* L, const char* name, ...) template<class Ret> Ret call_function(object const& obj, ...) |
Применяется, например, вот так:
int ret = call_function<int>(L, "LuaFunctionName", param); |
Биндинг классов не намного сложнее, чем биндинг функций:
module(L) [ class_<testclass>("testclass") .def(constructor<int>()) // конструктор класса принимает int .def("print", &testclass::print) // функция-член класса ]; |
После чего этот класс становится доступен в Lua и его можно использовать:
a = testclass(10) a:print() |
Конечно же, можно шарить не только функции-члены классов, но и данные-члены. Вот как это делается:
module(L) [ class_<MyClass>("LuaClass") // можно читать и писать .def_readwrite("data", &MyClass::data_read_write), // а это только читать .def_readonly("data2", &MyClass::data_read_only) ]; |
Использование LuaBind в игре
Посмотрим, как на практике можно использовать LuaBind в игре. Для начала я решил сделать что попроще, а именно: возможность конфигурации врагов через скрипты, а так же создание новых типов врагов через Lua-скрипты, без правки кода на C++. Вот конфиг (lua-скрипт), который я хочу что бы грузился нашей игрой:
Enemy01 = { sprite = "Data/enemy-01.png", health = 30, speed = 150.0, } Enemy03 = { sprite = "Data/enemy-03.png", health = 150, speed = 50.0, } Enemy04 = { sprite = "Data/enemy-04.png", health = 100, speed = 100.0, } |
В нём я указал три типа врагов, каждому из них задал его собственный спрайт, скорость и количество жизней. Далее написал функцию, которая выполняет этот скрипт и достаёт из него данные:
bool InitScripts() { myLuaState = lua_open(); luabind::open(myLuaState); // Define a lua function that we can call luaL_dostring(myLuaState, GetFileAsString("ai.lua").c_str()); // перебираем все глобальные переменные (и функции там же лежат) const luabind::object& objects = luabind::globals(myLuaState); for (luabind::iterator itr(objects); itr!=luabind::iterator(); itr++) { const luabind::object& obj = (*itr); if ( type( obj ) == LUA_TTABLE ) // это метатаблица { // перебираем данные в этой таблице for (luabind::iterator itr2(obj); itr2!=luabind::iterator(); itr2++) { // это переменная, хранящая имя файла со спрайтом? if (luabind::object_cast<std::string>(itr2.key()).compare("sprite")==0) { luabind::object key = itr.key(); // берём ключ std::string name = luabind::object_cast<std::string>(key); vecEnemyTypes.push_back(name); } } } } return true; } |
Что конкретно и как конкретно делает эта функция – понятно из комментариев. А вот так я использую результаты работы Lua-скрипта, для создания новых врагов в игре:
// рандомно выбираем какой тип врага будет в этот раз std::string strTypeName = vecEnemyTypes[rand()%vecEnemyTypes.size()]; // берём спрайт, жизни и скорость выбранного врага string strSprite = luabind::object_cast<string>( luabind::globals(myLuaState)[strTypeName]["sprite"]); int fSpeed = luabind::object_cast<float>( luabind::globals(myLuaState)[strTypeName]["speed"]); int iHealth = luabind::object_cast<int>( luabind::globals(myLuaState)[strTypeName]["health"]); // переводим в wstring и создаём врага std::wstring wName(strSprite.begin(), strSprite.end()); vecEnemies.push_back(CEnemy(wName, iHealth, fSpeed)); |
Как обычно, Вы можете скачать проект с исходными кодами и всем прочими данными. Либо скачать только дему игры.
Если есть какие-то вопросы – пишите, обязательно отвечу!
Раздел: Featured, Scripting · Теги: Lua, Создание движка
у меня вопрос: как заставить это работать?
using namespace std;
#define sharedptr boost::shared_ptr
lua_State* lvm;
class base
{
public:
base() {};
~base() {};
virtual void set(int i)
{
m_i = i;
}
int m_i;
static void reg()
{
luabind::module(lvm)
[
luabind::class_<base, sharedptr>("base")
.def(luabind::constructor())
.def("set", &base::set)
];
}
};
class derived: public base
{
public:
derived() {};
~derived() {};
virtual void set(int i)
{
m_i = i + 100500;
}
static void reg()
{
luabind::module(lvm)
[
luabind::class_<derived, base, sharedptr>("derived")
.def(luabind::constructor())
];
}
};
sharedptr factory()
{
sharedptr b(new derived);
b->set(10);
return b;
}
int main()
{
try
{
lvm = luaL_newstate();
luaL_openlibs(lvm);
luabind::open(lvm);
base::reg();
derived::reg();
luabind::module(lvm)
[
luabind::def("factory", &factory)
];
luaL_dostring(lvm,
“function foo()\n”
” return factory()\n”
“end\n”
);
sharedptr b = luabind::call_function<sharedptr>(lvm, “foo”);
cout <m_i << endl;
lua_close(lvm);
}
catch(exception& err)
{
cout << "exception: " << err.what() << endl;
}
cin.get();
}