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
Moduły Lua pozwalają na strukturyzację projektu i tworzenie wielokrotnie używanego kodu bibliotecznego. Ogólnie warto unikać duplikacji w projektach. Defold pozwala korzystać z funkcjonalności modułu Lua, aby dołączać pliki skryptowe do innych plików skryptowych. Dzięki temu można zawierać funkcje (i dane) w zewnętrznym pliku skryptowym, aby ponownie używać ich w plikach skryptów obiektów gry i plikach skryptów GUI.
Kod Lua przechowywany w plikach z rozszerzeniem “.lua” w dowolnym miejscu w strukturze projektu gry może być wymagany (dołączony) w plikach skryptowych i plikach skryptów GUI. Aby utworzyć nowy plik modułu Lua, kliknij prawym przyciskiem myszy folder, w którym chcesz go utworzyć w widoku Assets, a następnie wybierz New... ▸ Lua Module. Nadaj plikowi unikalną nazwę i naciśnij Ok:
Załóżmy, że poniższy kod jest dodany do pliku “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
Następnie dowolny skrypt może zażądać (ang. require) tego pliku i używać funkcji:
require "main.anim"
function update(self, dt)
-- zaktualizuj pozycję, ustaw kierunek itp.
...
-- ustaw animację
local anim = direction_animation(self.dir, "player")
if anim ~= self.current_anim then
sprite.play_flipbook("#sprite", anim)
self.current_anim = anim
end
end
Funkcja require
wczytuje podany moduł. Na początek przegląda tabelę package.loaded
, aby sprawdzić, czy moduł jest już załadowany. Jeśli tak, funkcja require
zwraca wartość przechowywaną w package.loaded[module_name]
. W przeciwnym przypadku wczytuje i ewaluuje plik za pomocą ładowacza (ang. loader).
Składnia łańcucha nazwy pliku (filename string) przekazywanej do require jest nieco specjalna. Lua zamienia znaki "."
w łańcuchu nazwy pliku na znaki separatora ścieżki: '/'
na macOS i Linux oraz '\'
na systemie Windows.
Należy zauważyć, że zazwyczaj nie jest dobrym pomysłem używanie zakresu globalnego do przechowywania stanu i definiowania funkcji, tak jak w przykładzie powyżej. Istnieje ryzyko kolizji nazw, ujawnienia stanu modułu lub wprowadzenia zależności między użytkownikami modułu.
Aby inkapsulować dane i funkcje, Lua używa modułów. Moduł Lua to zwykła tabela Lua służąca do zawierania funkcji i danych. Tabela jest deklarowana jako lokalna, aby nie zanieczyszczać zakresu globalnego:
local M = {}
-- private
local message = "Hello world!"
function M.hello()
print(message)
end
return M
Moduł można następnie używać. Ponownie, preferuje się przypisanie go do zmiennej lokalnej:
local m = require "mymodule"
m.hello() --> "Hello world!"
Rozważmy prosty moduł:
-- module.lua
local M = {} -- tworzy nową tabelę w zakresie lokalnym
M.value = 4711
return M
I użytkownika modułu:
local m = require "module"
print(m.value) --> "4711" (nawet jeśli plik "module.lua" zostanie zmieniony i na nowo załadowany)
Jeśli ponownie załadujesz plik modułu, kod zostanie uruchomiony ponownie, ale nic się nie dzieje z m.value
. Dlaczego?
Po pierwsze, tabela utworzona w pliku “module.lua” jest tworzona w zakresie lokalnym, a odniesienie do tej tabeli jest zwracane użytkownikowi. Ponowne wczytanie pliku “module.lua” ocenia kod modułu ponownie, ale tworzy nową tabelę w zakresie lokalnym zamiast aktualizować tabelę m
.
Po drugie, Lua przechowuje w pamięci podręcznej załadowane pliki. Pierwszym razem, gdy plik jest wymagany, jest on dodawany do tabeli package.loaded
, aby można go było szybciej odczytywać podczas kolejnych wymagań. Aby wymusić ponowne odczytanie pliku z dysku, można ustawić wpis pliku na nil
: package.loaded["my_module"] = nil
.
Aby prawidłowo ponownie załadować moduł, musisz ponownie załadować moduł, zresetować pamięć podręczną i następnie ponownie załadować wszystkie pliki, które używają modułu. Jest to jednak dalekie od optymalnego.
Zamiast tego możesz rozważyć obejście do użycia w trakcie rozwoju programu: umieścić tabelę modułu w zakresie globalnym i spowodować, aby M
odnosiło się do globalnej tabeli, zamiast tworzyć nową tabelę za każdym razem, gdy plik jest używany ponownie. Ponowne wczytanie modułu zmienia zawartość globalnej tabeli:
--- module.lua
-- Zamiast tego użyj "local M = {}" w ostatecznej wersji
uniquevariable12345 = uniquevariable12345 or {}
local M = uniquevariable12345
M.value = 4711
return M
Moduły z zachowaniem stanu przechowują stan wewnętrzny, który jest współdzielony między wszystkimi użytkownikami modułu i można go porównać do singletonów:
local M = {}
-- wszyscy użytkownicy modułu będą współdzielić tę tabelę
local state = {}
function M.do_something(foobar)
table.insert(state, foobar)
end
return M
Z kolei moduł bezstanowy nie przechowuje wewnętrznego stanu. Zamiast tego dostarcza mechanizm do wyeksternalizowania stanu do osobnej tabeli lokalnej dla użytkownika modułu. Oto kilka różnych sposobów, aby to zaimplementować:
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
Użyj modułu w ten sposób:
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 jest dodawane jako pierwszy argument, gdy używa się notacji ":"
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" jest dodawane jako pierwszy argument przy użyciu notacji ":"
print(my_state:get_state()) --> 43
__index
, ale każde domknięcie zawiera własną kopię funkcji, co zwiększa zużycie pamięci.
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
Użyj modułu w ten sposób:
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