This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Lua-модули позволяют структурировать проект и создавать многократно используемый библиотечный код. Избежание дублирования в проектах является хорошей идеей. Defold позволяет использовать функциональность Lua-модулей, предоставляя возможность включать файлы скриптов в другие файлы скриптов. Это позволяет инкапсулировать функциональность (и данные) во внешний файл скрипта для повторного использования в скриптах игровых объектов и GUI-скриптах.
Lua-код, хранящийся в файлах с окончанием “.lua” где-либо в структуре игрового проекта, может быть затребован в файлах скриптов и GUI-скриптов. Чтобы создать новый файл Lua-модуля, кликните ПКМ по папке, в которой хотите его создать, в представлении Assets, затем выберите New... ▸ Lua Module. Дайте файлу уникальное имя и кликните Ok:

Предположим, в файл “main/anim.lua” добавлен следующий код:
function direction_animation(direction, char)
    local d = ""
    if direction.x > 0 then
        d = "right"
    elseif direction.x < 0 then
        d = "left"
    elseif direction.y > 0 then
        d = "up"
    elseif direction.y < 0 then
        d = "down"
    end
    return hash(char .. "-" .. d)
end
В таком случае любой скрипт может потребовать этот файл и использовать функцию:
require "main.anim"
function update(self, dt)
    -- обновить позицию, задать направление и т.д.
    ...
    -- установить анимацию
    local anim = direction_animation(self.dir, "player")
    if anim ~= self.current_anim then
        sprite.play_flipbook("#sprite", anim)
        self.current_anim = anim
    end
end
Функция require загружает конкретный модуль. Она начинает с просмотра таблицы package.loaded, чтобы определить, загружен ли уже модуль. Если да, то require возвращает значение, хранящееся в package.loaded[module_name]. В противном случае он загружает и оценивает файл с помощью загрузчика.
Синтаксис строки имени файла, передаваемой в require, немного своеобразен. Lua заменяет символы ‘.’ в строке имени файла на разделители путей: ‘/’ в macOS и Linux и ‘\’ в Windows.
Стоит отметить, что использование глобальной области видимости для хранения состояния и определения функций, как мы делали выше, обычно является плохой идеей. В этом случае существует риск конфликта имен, раскрытия состояния модуля или введения связи между пользователями модуля.
Для инкапсуляции данных и функций Lua использует модули. Lua-модуль — это обычная Lua-таблица, используемая для хранения функций и данных. Таблица объявляется локальной, чтобы не засорять глобальную область видимости:
local M = {}
-- private
local message = "Hello world!"
function M.hello()
    print(message)
end
return M
Теперь модуль можно использовать. Опять же, предпочтительнее присвоить его локальной переменной:
local m = require "mymodule"
m.hello() --> "Hello world!"
Рассмотрим простой модуль:
-- module.lua
local M = {} -- создает новую таблицу в локальной области видимости
M.value = 4711
return M
И пользователь модуля:
local m = require "module"
print(m.value) --> "4711" (даже если "module.lua" изменен и перезагружен на горячую)
При горячей перезагрузке файла модуля код снова выполняется, но с m.value ничего не происходит. Почему?
Во-первых, таблица, созданная в “module.lua”, создается в локальной области видимости, и пользователю возвращается ссылка на эту таблицу. Перезагрузка “module.lua” снова оценивает код модуля, но при этом создается новая таблица в локальной области видимости вместо обновления таблицы, на которую ссылается m.
Во-вторых, Lua кэширует требуемые файлы. Когда файл требуется в первый раз, он помещается в таблицу package.loaded, чтобы его можно было быстрее прочитать при последующих запросах. Можно заставить файл быть повторно считанным с диска, установив запись файла в значение nil: package.loaded["my_module"] = nil.
Чтобы правильно выполнить горячую перезагрузку модуля, необходимо перезагрузить модуль, сбросить кэш, а затем перезагрузить все файлы, которые использует модуль. Это далеко не оптимальный вариант.
Вместо этого можно рассмотреть обходной путь, который можно использовать при разработке: поместить таблицу модуля в глобальную область видимости и заставить M ссылаться на глобальную таблицу вместо создания новой таблицы при каждой оценке файла. Перезагрузка модуля изменит содержимое глобальной таблицы:
--- module.lua
-- Заменить на локальное M = {} после завершения
uniquevariable12345 = uniquevariable12345 or {}
local M = uniquevariable12345
M.value = 4711
return M
Модули с состоянием хранят внутреннее состояние, которое является общим для всех пользователей модуля, и их можно сравнить с синглтонами:
local M = {}
-- Все пользователи модуля будут совместно использовать эту таблицу
local state = {}
function M.do_something(foobar)
    table.insert(state, foobar)
end
return M
С другой стороны, модуль без состояния не хранит никакого внутреннего состояния. Вместо этого он предоставляет механизм для экстернализации состояния в отдельную таблицу, локальную для пользователя модуля. Вот несколько различных способов реализовать это:
local M = {}
  
function M.alter_state(the_state, v)
    the_state.value = the_state.value + v
end
  
function M.get_state(the_state)
    return the_state.value
end
  
function M.new(v)
    local state = {
        value = v
    }
    return state
end
  
return M
Use the module like this:
local m = require "main.mymodule"
local my_state = m.new(42)
m.alter_state(my_state, 1)
print(m.get_state(my_state)) --> 43
local M = {}
  
function M:alter_state(v)
    -- self is added as first argument when using : notation
    self.value = self.value + v
end
  
function M:get_state()
    return self.value
end
  
function M.new(v)
    local state = {
        value = v
    }
    return setmetatable(state, { __index = M })
end
  
return M
Use the module like this:
local m = require "main.mymodule"
local my_state = m.new(42)
my_state:alter_state(1) -- "my_state" is added as first argument when using : notation
print(my_state:get_state()) --> 43
__index, но при этом каждое замыкание содержит свою собственную копию методов, вследствие чего потребление памяти выше.  local M = {}
  
  function M.new(v)
      local state = {
          value = v
      }
  
      state.alter_state = function(v)
          state.value = state.value + v
      end
  
      state.get_state = function()
          return state.value
      end
  
      return state
  end
  
  return M
Используйте модуль следующим образом:
  local m = require "main.mymodule"
  local my_state = m.new(42)
  my_state.alter_state(1)
  print(my_state.get_state()) 
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB