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
Komponent typu skrypt (ang. script) pozwala na tworzenie logiki gry przy użyciu języka programowania Lua. Skrypty dodawane są do obiektów gry dokładnie tak samo jak każdy inny komponent, a Defold wykona kod Lua w ramach funkcji cyklu życia silnika.
W Defoldzie występują trzy rodzaje skryptów Lua, z dostępem do różnych bibliotek Defolda.
Defold wykonuje skrypty Lua jako część cyklu życia silnika i ujawnia cykl życia przez zestaw predefiniowanych funkcji wywołania zwrotnego. Gdy dodasz komponent skryptu do obiektu gry, skrypt staje się częścią cyklu życia obiektu gry i jego komponentu (lub komponentów). Skrypt jest oceniany w kontekście Lua, gdy jest wczytywany, a następnie silnik wykonuje następujące funkcje i przekazuje odniesienie do bieżącej instancji komponentu skryptu. Możesz użyć tego odniesienia “self” do przechowywania stanu w instancji komponentu.
::: ważne “Self” to obiekt typu userdata, który działa jak tabela Lua, ale nie można go przeglądać za pomocą pairs() ani ipairs(), ani drukować za pomocą pprint(). :::
function init(self)
-- These variables are available through the lifetime of the component instance
self.my_var = "something"
self.age = 0
end
```lua function final(self) if self.my_var == “something” then – wykonaj jakiś kod end end
function update(self, dt)
self.age = self.age + dt -- increase age with the timestep
end
function fixed_update(self, dt)
msg.post("#co", "apply_force", {force = vmath.vector3(1, 0, 0), position = go.get_world_position()})
end
function on_message(self, message_id, message, sender)
if message_id == hash("increase_score") then
self.total_score = self.total_score + message.score
end
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
print("Touch", action.x, action.y)
end
end
function on_reload(self)
print(self.age) -- wyświetl wartość zmiennej self.age
end
Obiekt gry z komponentem skryptu implementuje pewną logikę. Często zależy ona od pewnego czynnika zewnętrznego. Przykładowo sztuczna inteligencja przeciwnika może reagować na gracza znajdującego się w określonym promieniu od przeciwnika, drzwi mogą odblokować się i otworzyć w wyniku interakcji gracza itp.
Funkcja update()
pozwala na implementację złożonych zachowań zdefiniowanych jako automat stanów, który działa w każdej klatce - czasami jest to odpowiednie podejście. Jednak z każdym wywołaniem update()
wiąże się pewien koszt. Jeśli naprawdę nie potrzebujesz tej funkcji, powinieneś ją usunąć i spróbować budować swoją logikę w sposób reaktywny. Oczekiwanie biernie na jakąś wiadomość, która wyzwoli reakcję, jest “tańsze” niż aktywne przeszukiwanie świata gry w poszukiwaniu danych do analizy. Ponadto rozwiązanie danego problemu w sposób reaktywny zazwyczaj prowadzi do czystszego i bardziej stabilnego projektu i jego implementacji.
Przyjrzyjmy się konkretnemu przykładowi. Załóżmy, że chcesz, aby komponent skryptu wysłał wiadomość 2 sekundy po inicjalizacji. Następnie powinien oczekiwać na określoną wiadomość odpowiedzi i po jej otrzymaniu, wysłać inną wiadomość 5 sekund później. Niereaktywny kod dla tego wyglądałby mniej więcej tak:
function init(self)
-- Licznik czasu.
self.counter = 0
-- Potrzebujemy tego do śledzenia naszego stanu.
self.state = "first"
end
function update(self, dt)
self.counter = self.counter + dt
if self.counter >= 2.0 and self.state == "first" then
-- wysłanie wiadomości po 2 sekundach
msg.post("some_object", "some_message")
self.state = "waiting"
end
if self.counter >= 5.0 and self.state == "second" then
-- wysłanie wiadomości 5 sekund po otrzymaniu "response"
msg.post("another_object", "another_message")
-- Wyczyszczenie stanu, aby nie wykonać tego bloku stanu ponownie.
self.state = nil
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("response") then
-- Stan "pierwszy" zakończony, wchodzi kolejny
self.state = "second"
-- Wyzerowanie licznika
self.counter = 0
end
end
Even in this quite simple case we get fairly tangled up logic. It’s possible to make this look better with the help of coroutines in a module (see below), but let’s instead try to make this reactive and use a built-in timing mechanism.
local function send_first()
msg.post("some_object", "some_message")
end
function init(self)
-- Poczekaj 2 s, a następnie wywołaj send_first()
timer.delay(2, false, send_first)
end
local function send_second()
msg.post("another_object", "another_message")
end
function on_message(self, message_id, message, sender)
if message_id == hash("response") then
-- Poczekaj 5 s, a następnie wywołaj send_second()
timer.delay(5, false, send_second)
end
end
To jest bardziej przejrzyste i łatwiejsze do śledzenia. Pozbywamy się wewnętrznych zmiennych stanu, które często są trudne do śledzenia przez logikę - i które mogą prowadzić do subtelnych błędów. Dodatkowo całkowicie rezygnujemy z funkcji update()
. Zwalnia to silnik z wywoływania naszego skryptu 60 razy na sekundę.
Przetwarzanie wstępne, czyli preprocessing wykrozystuje preprocesor Lua i specjalne znaczniki, aby warunkowo dołączać kod w zależności od wariantu budowy. Przykład:
-- Użyj jednego z tych słów kluczowych: RELEASE, DEBUG lub HEADLESS
--#IF DEBUG
local lives_num = 999
--#ELSE
local lives_num = 3
--#ENDIF
Preprocesor jest dostępny jako rozszerzenie budowania. Dowiedz się więcej na temat sposobu instalacji i użycia na stronie rozszerzenia na GitHubie.
Edytor Defold obsługuje edycję skryptów Lua z kolorowaniem składni i autouzupełnianiem. Aby wyświetlić nazwy funkcji Defold, naciśnij Ctrl+Spacja, aby wyświetlić listę funkcji pasujących do tego, co wpisujesz.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB