Уроки Коммент.

Создание игр » Новости » Использование LuaBind. Часть 1.

Использование LuaBind. Часть 1.

lua, luabindLua и 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));

Как обычно, Вы можете скачать проект с исходными кодами и всем прочими данными. Либо скачать только дему игры.

Если есть какие-то вопросы – пишите, обязательно отвечу!

Ещё по этой теме:




Раздел: Новости

Один комментарий на "Использование LuaBind. Часть 1."
  1. Даниил пишет:

    у меня вопрос: как заставить это работать?
    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();
    }

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>