Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / [Lua] Создание объектов из C++

[Lua] Создание объектов из C++

mr.DIMASПостоялецwww15 апр. 201821:47#0
Никак не могу понять как мне создавать луа-таблицы с функциями лежащими в файле скрипта.

Вот пример из Starbound:

function init()
  setPortraits()
  
  storage.complete = storage.complete or false

  self.compassUpdate = config.getParameter("compassUpdate", 0.5)
  self.wagnerUid = config.getParameter("wagnerUid")
  self.repairQuest = config.getParameter("repairQuest")
   
  storage.stage = storage.stage or 1
  self.stages = {
    findWagner,
    killShoggoth
  }

  self.state = FSM:new()
  self.state:set(self.stages[storage.stage])
end

Как мне присвоить init какой-либо луа-таблице на стороне C++? Что будет если много разных скриптов определят свою функцию init? Как мне использовать именно init из заданного скрипта?

Для чего это нужно: у меня в игре есть квесты, их логика хранится в файлах. Квест не уникален - может быть создано много квестов одного типа (например "Найди-принеси"), поэтому мне нужно инстанцировать несколько объектов-квестов (по сути луа-таблицы) с одинаковыми функциями (init, update и т.д.) но с разным состоянием.

PS. Возможно немного сумбурно описал проблему :)

Правка: 15 апр. 2018 21:48

ArochПостоялецwww15 апр. 201822:38#1
у тебя есть _G доступен отовсюду, init по умолчанию это _G.init если ты поменяешь environment (lua_setfenv) то сможешь переключить таблицу по умолчанию на любую другую созданную изначально в _G и сможешь городить в ней свой огород, для каждого скрипта просто ставь свою среду, и в ней соответственно будет свой init.
mr.DIMASПостоялецwww15 апр. 201822:58#2
Aroch
Круто, спасибо!
mr.DIMASПостоялецwww16 апр. 201821:03#3
В последней версии луа нет этих функций. Долго втыкал в мануал по новой версии, но так и не понял как мне заменить environment.

Возможно это делается как-то так, но как мне тогда вернуть старую таблицу после заполнения новой?

lua_pushglobaltable(L)
lua_pushnumber(L, LUA_RIDX_GLOBALS)
lua_newtable(L)
lua_settable(L, -3)
luaL_dofile(L, "blablah.lua")

DelfigamerПостоялецwww16 апр. 201821:05#4
У тебя какая версия?
В 5.1 вместо
> luaL_dofile
нужно раздельно загружать чанк, lua_setfenv и уже затем его выполнять.
mr.DIMASПостоялецwww16 апр. 201821:08#5
Delfigamer
Версия 5.3.4
DelfigamerПостоялецwww16 апр. 201821:23#6
When Lua loads a chunk, the default value for its _ENV upvalue is the global environment (see load). Therefore, by default, free names in Lua code refer to entries in the global environment (and, therefore, they are also called global variables). Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment. You can use load (or loadfile) to load a chunk with a different environment. (In C, you have to load the chunk and then change the value of its first upvalue.)
const char *lua_setupvalue (lua_State *L, int funcindex, int n);
Sets the value of a closure's upvalue. It assigns the value at the top of the stack to the upvalue and returns its name. It also pops the value from the stack.
Returns NULL (and pops nothing) when the index n is greater than the number of upvalues.
Parameters funcindex and n are as in function lua_getupvalue.

Если тебе нужно, чтобы глобальные имена читались через локальное окружение, то таблицу окружения нужно будет специально подготовить - соорудить метатаблицу с __index, указывающим на глобальное окружение, и присвоить её окружению локальному.
[Metamethod] __index: The indexing access table[key]. This event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.
Despite the name, the metamethod for this event can be either a function or a table. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. If it is a table, the final result is the result of indexing this table with key. (This indexing is regular, not raw, and therefore can trigger another metamethod.)
void lua_setmetatable (lua_State *L, int index);
Pops a table from the stack and sets it as the new metatable for the value at the given index.

Правка: 16 апр. 2018 21:25

mr.DIMASПостоялецwww16 апр. 201821:50#7
Мне нужно следующее: создать луа-таблицу на стороне Си и засунуть в нее содержимое файла скрипта. Чтобы например я создал таблицу-квест на стороне Си, затем загрузил в нее функции (init, update и т.д) и переменные из файла скрипта. Затем я буду дергать функции из этой таблицы со стороны Си. Таких таблиц может быть создано сколько угодно. Надеюсь понятнее объяснил чем в нульпосте :)

Правка: 16 апр. 2018 21:53

DelfigamerПостоялецwww16 апр. 201822:34#8
Есть возможность заставить скрипты явно создавать таблицу и записывать всё в неё? Например,
local behavior = {}

function behavior.init()
  -- do stuff
end

function behavior.update(object)
  -- do stuff
end

return behavior
Если нужно, чтобы было прямо как в ОП, то это делается с помощью инструментов, описанных в #6. Рекомендую прочитать документацию и изучить принципы работы языка, которым ты хочешь воспользоваться.

Правка: 16 апр. 2018 22:35

mr.DIMASПостоялецwww17 апр. 201821:21#9
Delfigamer
> Есть возможность заставить скрипты явно создавать таблицу и записывать всё в
> неё?

Сейчас у меня именно такой вариант, я что-то тупанул похоже. Можно же скопировать эту таблицу и получить новую копию квеста например.

PS. После работы голова совсем туго соображает :)

mr.DIMASПостоялецwww20 апр. 201821:02#10
Я вот что подумал. Можно сделать все вот так:
luaL_dofile(L, "script.lua");

// теперь в глобальной таблице сидят init, update и т.д.

// создаем новую квест-таблицу
lua_newtable(L);

// копируем в новую квест-таблицу функции из глобального неймспейса
lua_pushstring(L, "init");
lua_getglobal(L, "init");
lua_settable(L, -3);

lua_pushstring(L, "update");
lua_getglobal(L, "update");
lua_settable(L, -3);

lua_setglobal(L, "QuestN"); // тут нужно генерить уникальный идентификатор для квеста

Таким образом я получу то что хотел в нульпосте.

PS. Код не проверял, сил нету :). Поправьте если где ошибся

Правка: 20 апр. 2018 21:03

DelfigamerПостоялецwww20 апр. 201821:33#11
Хлипко, чуть что не так - сразу всё развалится.

Правка: 20 апр. 2018 21:34

mr.DIMASПостоялецwww20 апр. 201821:41#12
Delfigamer
Понятно дело что тут нет даже элементарных проверок. Дальнейшее улучшение выглядит так: перед вызовом скрипта, удаляем из глобальной таблицы init, update и т.д. После загрузки проверяем есть ли нужные функции в глобальной таблице, если нет - посылаем юзера править скрипт. Больше недостатков не увидел пока что.
ArochПостоялецwww20 апр. 201821:45#13
mr.DIMAS
> но как мне тогда вернуть старую таблицу после заполнения новой?
судя по этому вопросу, ты не понимаешь как работает луа. Грубо говоря это стек, который ты сам же и заполняешь, ты можешь добавлять в него что-то (lua_push...), а можешь убирать (lua_pop). Некоторые функции (lua_set...) требуют определенные аргументы которые ты также кладешь на стек, и после их выполнения эти аргументы убираются со стека. На самом деле почти все функции так или иначе влияют на стек, открой lua.h + https://www.lua.org/manual/5.3/ и читай пока не придет осознание.

Правка: 20 апр. 2018 21:47

mr.DIMASПостоялецwww20 апр. 201821:49#14
Aroch
> читай пока не придет осознание.
Уже пришло

Я ж говорю, вечером уставший пытаюсь что-то сделать в своей игре и голова совершенно отказывается соображать :(

/ Форум / Программирование игр / Общее

2001—2018 © GameDev.ru — Разработка игр