This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this tutorial in English
Jeśli dopiero zaczynasz pracę z Defold, ten przewodnik pomoże Ci odnaleźć się w edytorze. Wyjaśnia też podstawowe idee i najczęściej używane elementy składowe w Defold: obiekty gry, kolekcje, skrypty i sprite’y.
Zaczniemy od pustego projektu i krok po kroku dojdziemy do bardzo małej, grywalnej aplikacji. Na końcu powinieneś mieć już wyczucie tego, jak działa Defold, i będziesz gotowy, by sięgnąć po bardziej rozbudowany samouczek albo od razu zajrzeć do instrukcji.
W całym samouczku szczegółowe opisy pojęć i sposobu wykonywania niektórych czynności są oznaczone właśnie tak jak ten akapit. Jeśli uznasz, że te sekcje wchodzą w zbyt duży detal, możesz je pominąć.

Zacznij od utworzenia nowego projektu i otwarcia go w edytorze. Jeśli dwukrotnie klikniesz plik main/main.collection, zostanie on otwarty w edytorze:

Edytor składa się z następujących głównych obszarów:
print() i pprint() ze skryptów. Jeśli aplikacja albo gra nie chce się uruchomić, Console to pierwsze miejsce, które warto sprawdzić. Za Console znajduje się zestaw kart pokazujących informacje o błędach, a także edytor krzywych używany przy tworzeniu efektów cząsteczkowych.Szablon projektu “Empty” jest w rzeczywistości całkowicie pusty. Mimo to wybierz Project ▸ Build, aby zbudować projekt i uruchomić grę.

Czarny ekran może nie jest szczególnie ekscytujący, ale to działająca aplikacja gry w Defold i łatwo możemy ją przerobić na coś ciekawszego. Zróbmy to.
Edytor Defold pracuje na plikach. Klikając dwukrotnie plik w Assets pane, otwierasz go w odpowiednim edytorze. Następnie możesz pracować z zawartością pliku.
Kiedy skończysz edytować plik, musisz go zapisać. Wybierz File ▸ Save w głównym menu. Edytor podpowiada to, dodając gwiazdkę * do nazwy pliku na karcie każdego pliku zawierającego niezapisane zmiany.

Pierwszą rzeczą, którą zrobimy, będzie utworzenie nowej kolekcji. Kolekcja to kontener na obiekty gry, które zostały rozmieszczone i ustawione w odpowiednich pozycjach. Kolekcje najczęściej służą do budowania poziomów gry, ale są bardzo przydatne wszędzie tam, gdzie trzeba ponownie używać grup i/lub hierarchii obiektów gry, które należą do siebie. Pomocne może być myślenie o kolekcjach jak o pewnym rodzaju prefabów.
Kliknij folder main w Assets pane, potem kliknij prawym przyciskiem myszy i wybierz New ▸ Collection File. Możesz też wybrać File ▸ New ▸ Collection File z głównego menu.

Nazwij nowy plik kolekcji car.collection i otwórz go. Użyjemy tej nowej, pustej kolekcji, by zbudować mały samochód z kilku obiektów gry. Obiekt gry to kontener na komponenty, takie jak sprite’y, dźwięki, skrypty logiki itp., których używasz do budowania gry. Każdy obiekt gry jest jednoznacznie identyfikowany w grze przez swoje id. Obiekty gry mogą komunikować się ze sobą przez przekazywanie wiadomości, ale o tym za chwilę.
Możliwe jest też tworzenie obiektu gry bezpośrednio w kolekcji, tak jak zrobiliśmy tutaj. W rezultacie powstaje obiekt jedyny w swoim rodzaju. Możesz go kopiować, ale każda kopia jest oddzielna - zmiana jednej nie wpływa na pozostałe. To oznacza, że jeśli utworzysz 10 kopii obiektu gry i później uznasz, że chcesz zmienić je wszystkie, będziesz musiał edytować wszystkie 10 instancji. Dlatego obiektów gry tworzonych bezpośrednio w miejscu należy używać dla obiektów, których nie planujesz powielać wiele razy.
Natomiast obiekt gry zapisany w pliku działa jak prototyp (w innych silnikach znany też jako “prefab” albo “blueprint”). Gdy umieszczasz w kolekcji instancje obiektu gry zapisanego w pliku, każdy obiekt jest umieszczany przez referencję - jest to klon oparty na prototypie. Jeśli uznasz, że prototyp wymaga zmiany, każda umieszczona instancja obiektu gry oparta na tym prototypie zostanie natychmiast zaktualizowana.

Zaznacz główny węzeł “Collection” w widoku Outline, kliknij prawym przyciskiem myszy i wybierz Add Game Object. W kolekcji pojawi się nowy obiekt gry o id “go”. Zaznacz go i ustaw jego id na “car” w widoku Properties. Na razie “car” jest bardzo nieciekawy. Jest pusty, nie ma ani reprezentacji wizualnej, ani logiki. Aby dodać reprezentację wizualną, musimy dodać komponent sprite’a.
Komponenty służą do rozszerzania obiektów gry o obecność wizualną i dźwiękową oraz funkcjonalność, taką jak fabryki, kolizje i zachowania skryptowe. Komponent nie może istnieć samodzielnie, tylko musi znajdować się wewnątrz obiektu gry. Komponenty są zwykle definiowane bezpośrednio w tym samym pliku co obiekt gry. Jeśli jednak chcesz używać komponentu wielokrotnie, możesz zapisać go w osobnym pliku, tak samo jak obiekty gry, i dołączać go jako referencję w dowolnym pliku obiektu gry. Niektóre typy komponentów, na przykład skrypty Lua, muszą być umieszczone w oddzielnym pliku komponentu, a następnie dołączone jako referencja do obiektów.
Pamiętaj, że komponentami nie manipuluje się bezpośrednio - możesz przesuwać, obracać, skalować i animować właściwości obiektów gry, które z kolei zawierają komponenty.

Zaznacz obiekt gry “car”, kliknij prawym przyciskiem myszy i wybierz Add Component, następnie wybierz Sprite i kliknij Ok. Jeśli zaznaczysz sprite w widoku Outline, zobaczysz, że trzeba ustawić mu kilka właściwości:
Obrazy do naszej gry:

Dodaj te obrazy do atlasu:
![]()
![]()
Następnie dodaj w kolekcji jeszcze dwa obiekty gry. Nazwij je “left_wheel” i “right_wheel” i dodaj do każdego komponent sprite pokazujący obraz opony, który dodaliśmy do sprites.atlas. Następnie chwyć obiekty gry kół i przeciągnij je na “car”, aby stały się dziećmi “car”. Obiekty gry będące dziećmi innych obiektów gry są przyczepione do rodzica, kiedy rodzic się porusza. Można je też przesuwać indywidualnie, ale cały ruch odbywa się względem obiektu rodzica. Dla opon to idealne rozwiązanie, ponieważ chcemy, żeby trzymały się samochodu, a przy skręcaniu możemy po prostu lekko obracać je w lewo i w prawo. Kolekcja może zawierać dowolną liczbę obiektów gry, ustawionych obok siebie, zorganizowanych w złożone drzewa rodzic-dziecko albo w dowolnej mieszance tych układów.
Przesuń obiekty gry opon na miejsce, zaznaczając je, a następnie wybierając Scene ▸ Move Tool. Chwyć uchwyty strzałek albo zielony kwadrat pośrodku, aby przesunąć obiekt w odpowiednie miejsce. Ostatnią rzeczą, którą musimy zrobić, jest upewnienie się, że opony są rysowane pod samochodem. Osiągniemy to przez ustawienie składowej Z pozycji na -0.5. Każdy element wizualny w grze jest rysowany od tyłu do przodu, posortowany według wartości Z. Obiekt z wartością Z równą 0 zostanie narysowany nad obiektem z wartością Z równą -0.5. Ponieważ domyślna wartość Z obiektu gry samochodu wynosi 0, nowa wartość ustawiona dla obiektów opon umieści je pod obrazem samochodu.

Ostatni element układanki to skrypt sterujący samochodem. Skrypt jest komponentem zawierającym program definiujący zachowanie obiektów gry. Dzięki skryptom możesz określić zasady działania gry oraz to, jak obiekty powinny reagować na różne interakcje, zarówno z graczem, jak i z innymi obiektami. Wszystkie skrypty są pisane w języku Lua. Aby pracować z Defold, Ty albo ktoś z Twojego zespołu musi nauczyć się programować w Lua.
Zaznacz “main” w Assets pane, kliknij prawym przyciskiem myszy i wybierz New ▸ Script File. Nazwij nowy plik car.script, a następnie dodaj go do obiektu gry “car”, zaznaczając “car” w widoku Outline, klikając prawym przyciskiem myszy i wybierając Add Component File. Wybierz car.script i kliknij OK. Zapisz plik kolekcji.
Kliknij dwukrotnie car.script, aby go otworzyć.
Defold udostępnia kilka funkcji cyklu życia do kodowania logiki gry. Przeczytaj o nich więcej w Script Manual.
Na początek usuń funkcje final, on_message i on_reload, ponieważ w tym samouczku nie będą nam potrzebne.
Następnie dodaj poniższe linie kodu przed początkiem funkcji init.
-- Constants
local turn_speed = 0.1 -- Slerp factor
local max_steer_angle_left = vmath.quat_rotation_z(math.pi / 6) -- 30 degrees
local max_steer_angle_right = vmath.quat_rotation_z(-math.pi / 6) -- -30 degrees
local steer_angle_zero = vmath.quat_rotation_z(0) -- Zero degrees
local wheels_vector = vmath.vector3(0, 72, 0) -- Vector from center of back and front wheel pairs
local acceleration = 100 -- The acceleration of the car
-- prehash the inputs
local left = hash("left")
local right = hash("right")
local accelerate = hash("accelerate")
local brake = hash("brake")
Wprowadzone tutaj zmiany są dość proste. Dodaliśmy po prostu kilka constants do skryptu, których później użyjemy do zaprogramowania naszego samochodu.
Zwróć uwagę, że wcześniej zapisujemy hasze w zmiennych. To naprawdę dobra praktyka, bo poprawia czytelność kodu i wydajność.
Następnie zmień funkcję init, tak aby zawierała poniższy kod:
function init(self)
-- Wyślij wiadomość do skryptu renderowania (zobacz builtins/render/default.render_script), aby ustawić kolor czyszczenia.
-- Zmienia to kolor tła gry. Wektor4 zawiera informacje o kolorze w kanałach od 0 do 1:
-- Red = 0.2, Green = 0.2, Blue = 0.2 i Alpha = 1.0
msg.post("@render:", "clear_color", { color = vmath.vector4(0.2, 0.2, 0.2, 1.0) } ) --<1>
-- Przejmij fokus wejścia, aby móc reagować na sterowanie
msg.post(".", "acquire_input_focus") -- <2>
-- Kilka zmiennych
self.steer_angle = vmath.quat() -- <3>
self.direction = vmath.quat()
-- Prędkość i przyspieszenie są względne wobec samochodu (bez obrotu)
self.velocity = vmath.vector3()
self.acceleration = vmath.vector3()
-- Wektor wejścia. Później modyfikujemy go w funkcji on_input,
-- aby zapisać wejście.
self.input = vmath.vector3()
end
Zastanawiasz się, co właśnie zmieniliśmy? Oto wyjaśnienie.
acquire_input_focus do obiektu gry, który zawiera ten komponent. W naszym przypadku wysyłamy tę wiadomość do obiektu gry przechowującego skrypt samochodu.To było łatwe, prawda? Teraz przejdźmy dalej i zmieńmy funkcję update, tak aby wyglądała następująco:
function update(self, dt)
-- Ustaw przyspieszenie na podstawie wejścia w osi y
self.acceleration.y = self.input.y * acceleration -- <1>
-- Oblicz nowe pozycje przednich i tylnych kół
local front_vel = vmath.rotate(self.steer_angle, self.velocity)
local new_front_pos = vmath.rotate(self.direction, wheels_vector + front_vel)
local new_back_pos = vmath.rotate(self.direction, self.velocity) -- <2>
-- Oblicz nowy kierunek samochodu
local new_dir = vmath.normalize(new_front_pos - new_back_pos)
self.direction = vmath.quat_rotation_z(math.atan2(new_dir.y, new_dir.x) - math.pi / 2) -- <3>
-- Oblicz nową prędkość na podstawie bieżącego przyspieszenia
self.velocity = self.velocity + self.acceleration * dt -- <4>
-- Zaktualizuj pozycję na podstawie bieżącej prędkości i kierunku
local pos = go.get_position()
pos = pos + vmath.rotate(self.direction, self.velocity)
go.set_position(pos) -- <5>
-- Interpoluj koła za pomocą vmath.slerp
if self.input.x > 0 then -- <6>
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_right)
elseif self.input.x < 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_left)
else
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, steer_angle_zero)
end
-- Zaktualizuj obrót kół
go.set_rotation(self.steer_angle, "left_wheel") -- <7>
go.set_rotation(self.steer_angle, "right_wheel")
-- Ustaw obrót obiektu gry zgodnie z kierunkiem
go.set_rotation(self.direction)
-- Zresetuj przyspieszenie i wejście
self.acceleration = vmath.vector3() -- <8>
self.input = vmath.vector3()
end
To była spora funkcja! Ale bez obaw, działa to tak:
Wreszcie pora sprawić, by samochód reagował na wejście. Zmień funkcję on_input, aby wyglądała tak:
function on_input(self, action_id, action)
-- Ustaw wektor wejścia zgodnie z naciśniętym klawiszem
if action_id == left then
self.input.x = -1
elseif action_id == right then
self.input.x = 1
elseif action_id == accelerate then
self.input.y = 1
elseif action_id == brake then
self.input.y = -1
end
end
Ta funkcja jest w gruncie rzeczy dość prosta. Po prostu odbieramy wejście i ustawiamy nasz wektor wejścia.
Nie zapomnij zapisać zmian.
Nie ma jeszcze skonfigurowanych akcji wejściowych, więc to naprawmy. Otwórz plik /input/game.input_bindings i dodaj wiązania key_trigger dla “accelerate”, “brake”, “left” i “right”. Ustawiamy je na klawisze strzałek (KEY_LEFT, KEY_RIGHT, KEY_UP i KEY_DOWN):

Teraz samochód jest gotowy do jazdy. Utworzyliśmy go wewnątrz “car.collection”, ale nie istnieje jeszcze w grze. Dzieje się tak dlatego, że silnik po uruchomieniu ładuje obecnie “main.collection”. Aby to naprawić, wystarczy dodać car.collection do main.collection. Otwórz main.collection, zaznacz główny węzeł “Collection” w widoku Outline, kliknij prawym przyciskiem myszy i wybierz Add Collection From File, wybierz car.collection i kliknij OK. Teraz zawartość car.collection zostanie umieszczona w main.collection jako nowe instancje. Jeśli zmienisz zawartość car.collection, każda instancja kolekcji będzie aktualizowana automatycznie podczas budowania gry.

Teraz wybierz Project ▸ Build i przejedź się swoim nowym samochodem! Zauważysz, że możesz już sterować samochodem tak, jak chcesz. Ale coś nadal nie działa poprawnie. Kiedy puszczasz sterowanie, samochód się nie zatrzymuje, a przecież powinien. Czas to dodać!
Kiedy obiekt porusza się w świecie rzeczywistym, działa na niego siła oporu, która przeciwdziała ruchowi i powoduje wyhamowanie. Siła ta jest w przybliżeniu proporcjonalna do kwadratu prędkości poruszającego się obiektu i dlatego można ją opisać wzorem D = k * |V| * V, gdzie k jest stałą, V to prędkość, a |V| oznacza jej wartość bezwzględną, czyli szybkość. Dodajmy to.
W sekcji stałych na początku skryptu dodaj następującą stałą:
local drag = 1.1 -- stała oporu <1>
Następnie w funkcji update, tuż nad tym wierszem, dodaj poniższe linie i zapisz plik.
function update(self, dt)
...
-- Oblicz nową prędkość na podstawie bieżącego przyspieszenia
self.velocity = self.velocity + self.acceleration * dt
...
end
function update(self, dt)
...
-- Szybkość to wartość prędkości
local speed = vmath.length_sqr(self.velocity)
-- Zastosuj opór
self.acceleration = self.acceleration - speed * self.velocity * drag
-- Zatrzymaj samochód, jeśli porusza się już wystarczająco wolno
if speed < 0.5 then self.velocity = vmath.vector3(0) end
...
end
Po wykonaniu powyższych kroków Twój car.script powinien wyglądać tak:
local turn_speed = 0.1 -- Slerp factor
local max_steer_angle_left = vmath.quat_rotation_z(math.pi / 6) -- 30 degrees
local max_steer_angle_right = vmath.quat_rotation_z(-math.pi / 6) -- -30 degrees
local steer_angle_zero = vmath.quat_rotation_z(0) -- Zero degrees
local wheels_vector = vmath.vector3(0, 72, 0) -- Vector from center of back and front wheel pairs
local acceleration = 100 -- The acceleration of the car
local drag = 1.1 -- the drag constant
function init(self)
-- Send a message to the render script (see builtins/render/default.render_script) to set the clear color.
-- This changes the background color of the game. The vector4 contains color information
-- by channel from 0-1: Red = 0.2. Green = 0.2, Blue = 0.2 and Alpha = 1.0
msg.post("@render:", "clear_color", { color = vmath.vector4(0.2, 0.2, 0.2, 1.0) } )
-- Acquire input focus so we can react to input
msg.post(".", "acquire_input_focus")
-- Some variables
self.steer_angle = vmath.quat()
self.direction = vmath.quat()
-- Velocity and acceleration are car relative (not rotated)
self.velocity = vmath.vector3()
self.acceleration = vmath.vector3()
-- Input vector. This is modified later in the on_input function
-- to store the input.
self.input = vmath.vector3()
end
function update(self, dt)
-- Set acceleration to the y input
self.acceleration.y = self.input.y * acceleration
-- Calculate the new positions of front and back wheels
local front_vel = vmath.rotate(self.steer_angle, self.velocity)
local new_front_pos = vmath.rotate(self.direction, wheels_vector + front_vel)
local new_back_pos = vmath.rotate(self.direction, self.velocity)
-- Calculate the car's new direction
local new_dir = vmath.normalize(new_front_pos - new_back_pos)
self.direction = vmath.quat_rotation_z(math.atan2(new_dir.y, new_dir.x) - math.pi / 2)
-- Speed is the magnitude of the velocity
local speed = vmath.length(self.velocity)
-- Apply drag
self.acceleration = self.acceleration - speed * self.velocity * drag
-- Stop if we are already slow enough
if speed < 0.5 then self.velocity = vmath.vector3() end
-- Calculate new velocity based on current acceleration
self.velocity = self.velocity + self.acceleration * dt
-- Update position based on current velocity and direction
local pos = go.get_position()
pos = pos + vmath.rotate(self.direction, self.velocity)
go.set_position(pos)
-- Interpolate the wheels using vmath.slerp
if self.input.x > 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_right)
elseif self.input.x < 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_left)
else
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, steer_angle_zero)
end
-- Update the wheel rotation
go.set_rotation(self.steer_angle, "left_wheel")
go.set_rotation(self.steer_angle, "right_wheel")
-- Set the game object's rotation to the direction
go.set_rotation(self.direction)
-- reset acceleration and input
self.acceleration = vmath.vector3()
self.input = vmath.vector3()
end
function on_input(self, action_id, action)
-- set the input vector to correspond to the key press
if action_id == hash("left") then
self.input.x = -1
elseif action_id == hash("right") then
self.input.x = 1
elseif action_id == hash("accelerate") then
self.input.y = 1
elseif action_id == hash("brake") then
self.input.y = -1
end
end
Teraz wybierz Project ▸ Build z głównego menu i przejedź się swoim nowym samochodem!
To kończy ten wprowadzający samouczek. Oto kilka wyzwań, z którymi możesz spróbować zmierzyć się samodzielnie:
properties, aby można je było zmieniać dla różnych instancji samochodu.Teraz śmiało zanurz się w Defold. Przygotowaliśmy wiele instrukcji i samouczków, które Cię poprowadzą, a jeśli utkniesz, serdecznie zapraszamy na forum.
Miłego Defoldowania!