Разбиение одной lua-функции на небольшие файлы

Я работаю над игрой, используя Lua и LÖVE.

Я предпочитаю ООП подход, основанный на замыкании, и каждый уровень — это новый экземпляр функции, содержащей все локальные переменные и функции, необходимые для запуска игры.

Я хотел бы разбить эту единственную функцию на несколько файлов. Мое текущее решение похоже на то, что я сделал здесь: объединение нескольких файлов в один - но я действительно не думаю, что это пока идеально.

Например, у меня был бы файл load.lua с функцией self.load из фрагмента ниже.

Фрагмент кода гигантской функции для справки ниже:

levelSetup = function()
local time = 60
local timer = time
local timerIsRunning = true
local danger = 10
local handSize = 2
local itemsLeft = handSize
local curHand = 0
local lastHand = 10
local multiplier = 1
local self = {}

------------------------------------------------------
-- Initialize Values based on level Creation
------------------------------------------------------
self.load = function()
  if curLevel.time == 1 then
    time = 60
  elseif curLevel.time == 2 then
    time = 40
    multiplier = multiplier*1.5
  else
    time = 20
    multiplier = multiplier*2
  end

  if curLevel.danger == 1 then
    danger = 10 --low catastrophe chance
  elseif curLevel.danger == 2 then
    danger = 30 --medium chance
    multiplier = multiplier*1.5
  else
    danger = 50--high chance!
    multiplier = multiplier*2
  end

  if curLevel.handSize == 1 then
    handSize = 2
  elseif curLevel.handSize == 2 then
    handSize = 3
    multiplier = multiplier*1.5
  else
    handSize = 4
    multiplier = multiplier*2
  end
  itemsLeft = handSize
  timer = time
  self.nextHand()
end

  return self
end

Какое лучшее решение для этого?


person OttoRobba    schedule 27.05.2014    source источник


Ответы (2)


Функция должна быть определена в одном фрагменте. Однако, используя функцию load, вы можете собрать данные фрагмента, используя множественные источники. Например.:

function closuredef( ... )
  local modules, n = { ... }, select( "#", ... )
  local index = -1
  local function reader()
    index = index + 1
    if index == 0 then -- chunk prefix
      return "local self = {};"
    elseif index == n+1 then -- chunk suffix
      return "\nreturn self"
    else -- read specified Lua files and add them to the chunk data
      local modname = modules[ index ]
      if modname ~= nil then
        local fname = assert( package.searchpath( modname, package.path ) )
        local file = assert( io.open( fname, "r" ) )
        local data = assert( file:read( "*a" ), "could not read '"..fname.."'" )
        file:close()
        return data
      end
    end
  end
  return assert( load( reader, "=closuredef" ) )
end

levelSetup = closuredef( "level.variables", "level.load" )

В этом примере реализации используется package.searchpath (что является новым в Lua). 5.2), чтобы сделать задание Lua-файлов более удобным. Если вы все еще используете Lua 5.1, вы можете использовать абсолютные имена файлов или реализовать свою собственную функцию package.searchpath. См. здесь, здесь или здесь для образцов.

Я не уверен, что это лучшее решение для вашей проблемы, например. вам будет трудно сопоставить номера строк в сообщениях об ошибках с реальными местоположениями ошибок...

person siffiejoe    schedule 28.05.2014
comment
На этот конкретный вопрос, безусловно, лучший ответ, несмотря на сложность отладки. Эта сложная для очистки ситуация, вероятно, говорит о том, что мне нужно переосмыслить свой код. - person OttoRobba; 29.05.2014

Всегда хорошо разбивать код на более мелкие файлы. Если вы собираетесь это сделать, элегантным решением (по моему скромному мнению) будет использование оператора return. Это очень распространенная и предпочтительная практика в экосистеме Lua.

Допустим, ваш проект состоит из нескольких подмодулей, соответственно submoduleA.lua, submoduleB.lua и submoduleC.lua. . Каждый из этих подмодулей содержит некоторый специализированный код (это может быть одна функция или набор функций, но допустим, что у нас есть одна функция в каждом подмодуле).

Помимо этих подмодулей у вас также есть основной файл (с именем main.lua), из которого вы хотите вызывать и использовать функции, определенные в подмодулях.

submoduleA.lua будет содержать определение некоторой функции с именем funcA. Эта функция может иметь свои собственные локальные переменные и использовать повышающие значения, с этим проблем нет. В идеале этот funcA должен быть объявлен как локальный внутри файла submoduleA.lua из-за проблем с областью действия. А затем, в конце файла, вы используете оператор return для возврата самой функции.

-- declaring upvalues, if any
local upvalue1 = ... -- placeholder code
local upvalue2 = ... -- placeholder code

-- function definition
local function funcA(arg1, arg2, ...)
  -- some code
end

return funcA -- at the end of the file

То же самое касается submoduleB.lua и submoduleC.lua.

Затем в файле main.lua вы можете легко вызывать функции, определенные в подмодулях, используя require заявление. Остерегайтесь одной детали с require, вам не нужно объявлять имя расширения ".lua", поскольку оно делает это автоматически (в документации об этом довольно подробно).

local funcA = require ('submoduleA') 
local funcB = require ('submoduleB')
local funcC = require ('submoduleC')

И это все. Опять же, это очень распространенный шаблон для Lua. Я использую подобную технику при написании своих собственных проектов/библиотек, особенно когда код охватывает несколько файлов. См. Jumper или FloodFill для справки.

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

Надеюсь это поможет.

person Roland Y.    schedule 28.05.2014