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
In questo tutorial partiamo da un progetto vuoto e creiamo un gioco runner completo con un personaggio animato, collisioni fisiche, oggetti da raccogliere e punteggio.
Quando impari a usare un nuovo motore di gioco ci sono molte cose da assimilare, quindi abbiamo creato questo tutorial per aiutarti a cominciare. È un tutorial piuttosto completo che illustra il funzionamento del motore e dell’editor. Presupponiamo che tu abbia una certa familiarità con la programmazione.
Se ti serve un’introduzione alla programmazione in Lua, consulta il nostro manuale di Lua in Defold.
Se questo tutorial ti sembra un po’ troppo impegnativo per iniziare, visita la nostra pagina dei tutorial, dove trovi una selezione di tutorial di vari livelli di difficoltà.
Se preferisci guardare dei video tutorial, consulta la versione video su YouTube.
Utilizziamo asset di gioco provenienti da altri due tutorial, con alcune piccole modifiche. Il tutorial è suddiviso in diversi passaggi, ognuno dei quali ci avvicina in modo significativo al gioco finale.
Il risultato finale sarà un gioco in cui controlli un eroe che corre attraverso un ambiente, raccogliendo monete ed evitando ostacoli. L’eroe corre a velocità costante e il giocatore ne controlla soltanto i salti premendo un unico pulsante (o toccando lo schermo su un dispositivo mobile). Il livello consiste in un flusso infinito di piattaforme su cui saltare e di monete da raccogliere.
Se in qualsiasi momento ti blocchi durante questo tutorial o mentre crei il tuo gioco, non esitare a chiederci aiuto sul forum di Defold. Nel forum puoi discutere di Defold, chiedere aiuto al team di Defold, vedere come altri sviluppatori di giochi hanno risolto i loro problemi e trovare nuove idee. Inizia subito.
Nel corso del tutorial, le descrizioni dettagliate dei concetti e delle procedure sono contrassegnate come questo paragrafo. Se ritieni che queste sezioni siano troppo approfondite, puoi saltarle.
Cominciamo, quindi. Ci auguriamo che questo tutorial ti diverta e ti aiuti a muovere i primi passi con Defold.
Scarica gli asset per questo tutorial qui.
Il primo passaggio consiste nello scaricare i seguenti file.
Se non hai ancora scaricato e installato l’editor Defold, è il momento di farlo:
Vai alla pagina di download di Defold dove troverai i pulsanti Download per macOS, Windows e Linux (Ubuntu):

Per avviare l’editor, apri la cartella “Applications” e fai doppio clic sul file “Defold”.

D:\Defold). Non spostare Defold in C:\Program Files (x86)\ o C:\Program Files\, poiché ciò impedirebbe all’editor di aggiornarsi.Per avviare l’editor, apri la cartella “Defold” e fai doppio clic sul file “Defold.exe”.

Da un terminale, trova il file di archivio “Defold-x86_64-linux.zip” ed estrailo in una directory di destinazione chiamata “Defold”.
$ unzip Defold-x86_64-linux.zip -d Defold
Per avviare l’editor, passa alla directory in cui hai estratto l’applicazione, quindi avvia l’eseguibile Defold, oppure fai doppio clic su di esso sul desktop.
$ cd Defold
$ ./Defold
Il menu Help > Create Desktop Entry offre uno strumento per installare una voce sul desktop.
Se incontri problemi nell’avviare l’editor, nell’aprire un progetto o nell’eseguire un gioco Defold, consulta la sezione delle FAQ su Linux.
Ogni versione beta e stabile di Defold è anche disponibile su GitHub.
Una volta installato e avviato l’editor, è il momento di creare un nuovo progetto e prepararlo. Crea un nuovo progetto dal modello “Empty Project”.
Questo tutorial utilizza le funzionalità Spine dell’estensione Spine. Aggiungi l’estensione alla sezione delle dipendenze di game.project.
Al primo avvio, l’editor è vuoto e non ha alcun progetto aperto: scegli quindi Open Project dal menu e seleziona il progetto appena creato. Ti verrà anche chiesto di creare un “ramo” per il progetto.
Nel pannello Assets vedrai tutti i file che fanno parte del progetto. Facendo doppio clic sul file “main/main.collection”, questo si aprirà nella vista dell’editor al centro:

L’editor è composto dalle seguenti aree principali:
print() e pprint() dei tuoi script. Se l’applicazione o il gioco non si avvia, la console è la prima cosa da controllare. Dietro la console si trovano una serie di schede che mostrano informazioni sugli errori e un editor di curve utilizzato per creare effetti particellari.Il modello di progetto “Empty” è effettivamente del tutto vuoto. Seleziona comunque Project ▸ Build per creare la build del progetto e avviare il gioco.

Uno schermo nero forse non è molto entusiasmante, ma è un’applicazione di gioco Defold in esecuzione e possiamo facilmente trasformarla in qualcosa di più interessante. Facciamolo.
L’editor Defold lavora sui file. Facendo doppio clic su un file nel pannello Assets, lo apri nell’editor adatto. Puoi quindi lavorare sul contenuto del file.
Quando hai finito di modificare un file, devi salvarlo. Seleziona File ▸ Save nel menu principale. L’editor ti ricorda le modifiche non salvate aggiungendo un asterisco ‘*’ al nome del file nella relativa scheda.

Prima di iniziare, configuriamo alcune impostazioni del progetto. Apri l’asset game.project da Assets Pane e scorri fino alla sezione Display. Imposta width e height del progetto rispettivamente su 1280 e 720.
Devi anche aggiungere l’estensione Spine al progetto, così potremo animare l’eroe. Aggiungi una versione dell’estensione Spine compatibile con la versione dell’editor Defold che hai installato. Le versioni disponibili di Spine sono elencate qui:
https://github.com/defold/extension-spine/releases
Fai clic con il pulsante destro sul link al file zip della release che vuoi utilizzare:

Aggiungi il link alla release all’elenco delle dipendenze di game.project. Dopo aver aggiunto l’estensione Spine, devi anche riavviare l’editor per attivare l’integrazione con l’editor inclusa nell’estensione.
Compiamo i primi piccoli passi e creiamo uno spazio per il nostro personaggio, o meglio un tratto di terreno a scorrimento. Lo faremo in pochi passaggi.
Un atlas è un file che combina un insieme di immagini separate in un unico file immagine più grande. Questo permette di risparmiare spazio e migliorare le prestazioni. Puoi approfondire gli atlas e le altre funzionalità grafiche 2D nella documentazione della grafica 2D.


Perché non funziona!? Un problema comune per chi inizia a usare Defold è dimenticarsi di salvare! Dopo aver aggiunto immagini a un atlas, devi salvare il file prima di poter accedere alle immagini.
Crea un file di collezione ground.collection per il terreno e aggiungi 7 oggetti di gioco (fai clic con il pulsante destro sulla radice della collezione nella vista Outline e seleziona Add Game Object). Assegna agli oggetti i nomi “ground0”, “ground1”, “ground2” e così via modificando la proprietà Id nella vista Properties. Tieni presente che Defold assegna automaticamente un ID univoco ai nuovi oggetti di gioco.
Aggiungi un componente sprite a ciascun oggetto (fai clic con il pulsante destro sull’oggetto di gioco nella vista Outline e seleziona Add Component, poi seleziona Sprite e fai clic su OK), imposta la proprietà Image del componente sprite sull’atlas appena creato e imposta l’animazione predefinita dello sprite su una delle due immagini del terreno. Imposta la posizione X del componente sprite (non dell’oggetto di gioco) su 190 e la posizione Y su 40. Poiché l’immagine è larga 380 pixel e la spostiamo lateralmente della metà di tale valore, il punto di pivot dell’oggetto di gioco si troverà sul bordo sinistro dell’immagine dello sprite.


Probabilmente è più semplice creare un oggetto di gioco completo, già ridimensionato e dotato di un componente sprite, e poi copiarlo. Selezionalo nella vista Outline, quindi seleziona Edit ▸ Copy e poi Edit ▸ Paste.
Se vuoi tasselli più grandi o più piccoli, puoi semplicemente cambiare la scala. In questo caso, però, dovrai anche modificare le posizioni X di tutti gli oggetti di gioco del terreno affinché siano multipli della nuova larghezza.
Salva il file, poi aggiungi ground.collection al file main.collection: fai prima doppio clic sul file main.collection, poi fai clic con il pulsante destro sull’oggetto radice nella vista Outline e seleziona Add Collection From File. Nella finestra di dialogo, seleziona ground.collection e fai clic su OK. Assicurati di collocare ground.collection nella posizione 0, 0, 0, altrimenti apparirà spostata. Salva il file.
Avvia il gioco (Project ▸ Build) per verificare che tutto sia al suo posto.

A questo punto potresti avere un po’ di confusione e chiederti che cosa siano davvero tutte le cose che abbiamo creato. Fermiamoci quindi un momento a esaminare gli elementi fondamentali di ogni progetto Defold:
Per il momento queste descrizioni dovrebbero bastare. Puoi però trovare una spiegazione molto più approfondita nel manuale degli elementi fondamentali. Ti consigliamo di consultarlo in seguito per comprendere meglio come funziona Defold.
Ora che tutti i tratti di terreno sono al loro posto, farli muovere è piuttosto semplice. L’idea è questa: spostare i tratti da destra a sinistra e, quando un tratto raggiunge l’estremità sinistra fuori dallo schermo, spostarlo nella posizione più a destra. Per muovere tutti questi oggetti di gioco serve uno script Lua, quindi creiamone uno:
-- ground.script
local pieces = { "ground0", "ground1", "ground2", "ground3",
"ground4", "ground5", "ground6" } -- <1>
function init(self) -- <2>
self.speed = 360 -- Speed in pixels/s
end
function update(self, dt) -- <3>
for i, p in ipairs(pieces) do -- <4>
local pos = go.get_position(p)
if pos.x <= -228 then -- <5>
pos.x = 1368 + (pos.x + 228)
end
pos.x = pos.x - self.speed * dt -- <6>
go.set_position(pos, p) -- <7>
end
end
init() viene chiamata quando l’oggetto di gioco prende vita nel gioco. Inizializziamo una variabile membro locale all’oggetto che contiene la velocità del terreno.update() viene chiamata una volta per fotogramma, in genere 60 volte al secondo. dt contiene il numero di secondi trascorsi dall’ultima chiamata.dt per ottenere una velocità in pixel/s indipendente dalla frequenza dei fotogrammi.Il nucleo del motore Defold gestisce rapidamente i dati e gli oggetti di gioco. Tutta la logica e tutti i comportamenti necessari per il tuo gioco vengono creati in Lua. Lua è un linguaggio di programmazione veloce e leggero, ottimo per scrivere la logica di gioco. Esistono eccellenti risorse per impararlo, come il libro Programming in Lua e il manuale di riferimento ufficiale di Lua.
Defold aggiunge a Lua una serie di API e un sistema di scambio di messaggi che permette di programmare la comunicazione tra gli oggetti di gioco. Consulta il manuale dello scambio di messaggi per i dettagli sul suo funzionamento.
Puoi mostrare o nascondere le sezioni Assets Pane, Console e Outline dell’editor usando rispettivamente i tasti F6, F7 e F8.
Ora che abbiamo un file di script, dobbiamo aggiungere un riferimento a esso in un componente di un oggetto di gioco. In questo modo lo script verrà eseguito come parte del ciclo di vita dell’oggetto di gioco. Creiamo quindi un nuovo oggetto di gioco in ground.collection e aggiungiamogli un componente Script che faccia riferimento al file di script Lua appena creato:

Ora, quando esegui il gioco, l’oggetto di gioco “controller” eseguirà lo script nel suo componente Script, facendo scorrere il terreno in modo fluido sullo schermo.
L’eroe sarà un oggetto di gioco composto dai seguenti componenti:
Inizia importando le immagini delle parti del corpo, poi aggiungile a un nuovo atlas che chiameremo hero.atlas:

Dobbiamo anche importare i dati delle animazioni Spine e configurare una Spine Scene per utilizzarli:

Il file hero.spinejson è stato esportato in formato Spine JSON. Per creare file di questo tipo ti serve il software di animazione Spine. Se vuoi usare un altro software di animazione, puoi esportare le animazioni come fogli di sprite e utilizzarle come animazioni flipbook da risorse Tile Source o Atlas. Consulta il manuale delle animazioni per maggiori informazioni.
Ora possiamo iniziare a costruire l’oggetto di gioco dell’eroe:

Ora è il momento di aggiungere la fisica affinché le collisioni funzionino:
Le collisioni di tipo “Kinematic” vengono rilevate, ma il motore fisico non le risolve automaticamente e non simula gli oggetti. Il motore fisico supporta diversi tipi di oggetti di collisione. Puoi approfondirli nella documentazione della fisica.
È importante specificare con cosa deve interagire l’oggetto di collisione:
Infine, crea un nuovo file hero.script e aggiungilo all’oggetto di gioco.
handle_geometry_contact().)
Gestiamo le collisioni autonomamente perché, se impostassimo l’oggetto di collisione del personaggio come dinamico, il motore eseguirebbe una simulazione newtoniana dei corpi coinvolti. Per un gioco come questo, una simulazione del genere è tutt’altro che ottimale: invece di contrastare il motore fisico applicando varie forze, assumiamo quindi il pieno controllo.
Per farlo e gestire correttamente le collisioni serve un po’ di matematica vettoriale. Trovi una spiegazione approfondita su come risolvere le collisioni cinematiche nella documentazione della fisica.
-- gravity pulling the player down in pixel units/sˆ2
local gravity = -20
-- take-off speed when jumping in pixel units/s
local jump_takeoff_speed = 900
function init(self)
-- this tells the engine to send input to on_input() in this script
msg.post(".", "acquire_input_focus")
-- save the starting position
self.position = go.get_position()
-- keep track of movement vector and if there is ground contact
self.velocity = vmath.vector3(0, 0, 0)
self.ground_contact = false
end
function final(self)
-- Return input focus when the object is deleted
msg.post(".", "release_input_focus")
end
function update(self, dt)
local gravity = vmath.vector3(0, gravity, 0)
if not self.ground_contact then
-- Apply gravity if there's no ground contact
self.velocity = self.velocity + gravity
end
-- apply velocity to the player character
go.set_position(go.get_position() + self.velocity * dt)
-- reset volatile state
self.correction = vmath.vector3()
self.ground_contact = false
end
local function handle_geometry_contact(self, normal, distance)
-- project the correction vector onto the contact normal
-- (the correction vector is the 0-vector for the first contact point)
local proj = vmath.dot(self.correction, normal)
-- calculate the compensation we need to make for this contact point
local comp = (distance - proj) * normal
-- add it to the correction vector
self.correction = self.correction + comp
-- apply the compensation to the player character
go.set_position(go.get_position() + comp)
-- check if the normal points enough up to consider the player standing on the ground
-- (0.7 is roughly equal to 45 degrees deviation from pure vertical direction)
if normal.y > 0.7 then
self.ground_contact = true
end
-- project the velocity onto the normal
proj = vmath.dot(self.velocity, normal)
-- if the projection is negative, it means that some of the velocity points towards the contact point
if proj < 0 then
-- remove that component in that case
self.velocity = self.velocity - proj * normal
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("contact_point_response") then
-- check if we received a contact point message. One message for each contact point
if message.group == hash("geometry") then
handle_geometry_contact(self, message.normal, message.distance)
end
end
end
local function jump(self)
-- only allow jump from ground
if self.ground_contact then
-- set take-off speed
self.velocity.y = jump_takeoff_speed
end
end
local function abort_jump(self)
-- cut the jump short if we are still going up
if self.velocity.y > 0 then
-- scale down the upwards speed
self.velocity.y = self.velocity.y * 0.5
end
end
function on_input(self, action_id, action)
if action_id == hash("jump") or action_id == hash("touch") then
if action.pressed then
jump(self)
elseif action.released then
abort_jump(self)
end
end
end
Se vuoi, ora puoi provare ad aggiungere temporaneamente l’eroe alla collezione principale ed eseguire il gioco per vederlo cadere attraverso il mondo.
L’ultima cosa che serve per rendere operativo l’eroe è l’input. Lo script qui sopra contiene già una funzione on_input() che risponde alle azioni “jump” e “touch” (per gli schermi tattili). Aggiungiamo i binding di input per queste azioni.

Ora che abbiamo un eroe completo di collisioni e di tutto il necessario, dobbiamo aggiungere le collisioni anche al terreno, in modo che l’eroe abbia qualcosa con cui collidere (o su cui correre). Lo faremo tra un momento, ma prima riorganizziamo un po’ il progetto: mettiamo tutti gli elementi del livello in una collezione separata e riordiniamo la struttura dei file:
Come forse avrai già notato, la gerarchia dei file visibile nel pannello Assets è indipendente dalla struttura dei contenuti che costruisci nelle collezioni. I file di collezione e di oggetto di gioco contengono riferimenti ai singoli file, ma questi possono trovarsi dove preferisci.
Se vuoi spostare un file, Defold ti aiuta aggiornando automaticamente i riferimenti al file (refactoring). Quando realizzi un software complesso come un gioco, è estremamente utile poter cambiare la struttura del progetto man mano che cresce e si evolve. Defold lo incoraggia e rende il processo semplice, quindi non temere di spostare i file!
Dobbiamo anche aggiungere alla collezione del livello un oggetto di gioco controller con un componente script:
Apri il file di script, copia al suo interno il codice seguente e salva il file:
-- controller.script
go.property("speed", 360) -- <1>
function init(self)
msg.post("ground/controller#ground", "set_speed", { speed = self.speed })
end

L’oggetto di gioco “controller” non esiste in un file, ma viene creato direttamente nella collezione del livello. Questo significa che l’istanza dell’oggetto di gioco viene creata a partire dai dati incorporati nella collezione. È una soluzione adatta agli oggetti di gioco con un unico scopo, come questo. Se ti servono più istanze di un oggetto di gioco e vuoi poter modificare il prototipo o modello usato per creare ciascuna istanza, crea un file di oggetto di gioco e aggiungi l’oggetto alla collezione dal file. In questo modo crei un oggetto di gioco che fa riferimento al file come prototipo o modello.
Lo scopo di questo oggetto di gioco “controller” è controllare tutto ciò che riguarda il livello in esecuzione. Presto questo script si occuperà di generare piattaforme e monete con cui l’eroe potrà interagire, ma per ora imposterà soltanto la velocità del livello.
Nella funzione init() dello script controller del livello viene inviato un messaggio al componente script dell’oggetto controller del terreno, indirizzato tramite il suo ID:
msg.post("ground/controller#controller", "set_speed", { speed = self.speed })
L’ID dell’oggetto di gioco controller è impostato su "ground/controller" perché si trova nella collezione “ground”. Aggiungiamo poi l’ID del componente, "controller", dopo il carattere cancelletto "#" che separa l’ID dell’oggetto da quello del componente. Lo script del terreno non contiene ancora codice per reagire al messaggio set_speed, quindi dobbiamo aggiungere una funzione on_message() a ground.script e la logica necessaria.
-- ground.script
function on_message(self, message_id, message, sender)
if message_id == hash("set_speed") then -- <1>
self.speed = message.speed -- <2>
end
end

A questo punto dobbiamo aggiungere le collisioni fisiche al terreno:

Ora dovresti poter provare a eseguire il gioco (Project ▸ Build). L’eroe dovrebbe correre sul terreno e dovrebbe essere possibile saltare con il tasto Space. Se esegui il gioco su un dispositivo mobile, puoi saltare toccando lo schermo.
Per rendere la vita nel nostro mondo di gioco un po’ meno monotona, aggiungiamo delle piattaforme su cui saltare.
Crea un file Script chiamato platform.script (fai clic con il pulsante destro nel pannello Assets, poi seleziona New ▸ Script File), inserisci il codice seguente nel file e salvalo:
-- platform.script
function init(self)
self.speed = 540 -- Default speed in pixels/s
end
function update(self, dt)
local pos = go.get_position()
if pos.x < -500 then
go.delete() -- <1>
end
pos.x = pos.x - self.speed * dt
go.set_position(pos)
end
function on_message(self, message_id, message, sender)
if message_id == hash("set_speed") then
self.speed = message.speed
end
end

Sia platform.go sia platform_long.go hanno componenti Script che fanno riferimento allo stesso file di script. È utile, perché qualsiasi modifica al file di script influenzerà il comportamento sia delle piattaforme normali sia di quelle lunghe.
L’idea è creare un semplice runner senza fine. Questo significa che non possiamo collocare gli oggetti di gioco delle piattaforme in una collezione nell’editor. Dobbiamo invece generarli dinamicamente:
-- controller.script
go.property("speed", 360)
local grid = 460
local platform_heights = { 100, 200, 350 } -- <1>
function init(self)
msg.post("ground/controller#controller", "set_speed", { speed = self.speed })
self.gridw = 0
end
function update(self, dt) -- <2>
self.gridw = self.gridw + self.speed * dt
if self.gridw >= grid then
self.gridw = 0
-- Maybe spawn a platform at random height
if math.random() > 0.2 then
local h = platform_heights[math.random(#platform_heights)]
local f = "#platform_factory"
if math.random() > 0.5 then
f = "#platform_long_factory"
end
local p = factory.create(f, vmath.vector3(1600, h, 0), nil, {}, vmath.vector3(0.6, 0.6, 1))
msg.post(p, "set_speed", { speed = self.speed })
end
end
end
update() viene chiamata una volta per fotogramma e la usiamo per decidere se generare una piattaforma normale o lunga a determinati intervalli (per evitare sovrapposizioni) e altezze. È facile sperimentare diversi algoritmi di generazione per creare gameplay differenti.Ora esegui il gioco (Project ▸ Build).
Wow, sta cominciando a diventare qualcosa di (quasi) giocabile…

La prima cosa da fare è dare vita all’eroe. In questo momento il poveretto è bloccato in un ciclo di corsa e non reagisce bene ai salti o ad altro. Il file Spine che abbiamo aggiunto dal pacchetto di asset contiene proprio una serie di animazioni adatte a questo scopo.
update() esistente: -- hero.script
local function play_animation(self, anim)
-- only play animations which are not already playing
if self.anim ~= anim then
-- tell the spine model to play the animation
local anim_props = { blend_duration = 0.15 }
spine.play_anim("#spinemodel", anim, go.PLAYBACK_LOOP_FORWARD, anim_props)
-- remember which animation is playing
self.anim = anim
end
end
local function update_animation(self)
-- make sure the right animation is playing
if self.ground_contact then
play_animation(self, hash("run"))
else
play_animation(self, hash("jump"))
end
end
update() e aggiungi una chiamata a update_animation: ...
-- apply it to the player character
go.set_position(go.get_position() + self.velocity * dt)
update_animation(self)
...

Lua usa un “ambito lessicale” per le variabili locali e tiene conto dell’ordine in cui inserisci le funzioni local. La funzione update() chiama le funzioni locali update_animation() e play_animation(): questo significa che il runtime deve aver già incontrato le funzioni locali per poterle chiamare. Per questo dobbiamo inserirle prima di update(). Se inverti l’ordine delle funzioni, otterrai un errore. Questo vale soltanto per le variabili local. Puoi approfondire le regole di ambito e le funzioni locali di Lua su http://www.lua.org/pil/6.2.html
Questo è tutto ciò che serve per aggiungere le animazioni di salto e caduta all’eroe. Se esegui il gioco, noterai che giocare è molto più piacevole. Potresti anche accorgerti che, purtroppo, le piattaforme possono spingere l’eroe fuori dallo schermo. È un effetto collaterale della gestione delle collisioni, ma il rimedio è semplice: aggiungiamo un po’ di violenza e rendiamo pericolosi i bordi delle piattaforme!
Salva il file.

Apri hero.go, seleziona il Collision Object e aggiungi il nome “danger” alla proprietà Mask. Poi salva il file.

Apri hero.script e modifica la funzione on_message() in modo da ottenere una reazione quando l’eroe collide con un bordo “danger”:
-- hero.script
function on_message(self, message_id, message, sender)
if message_id == hash("reset") then
self.velocity = vmath.vector3(0, 0, 0)
self.correction = vmath.vector3()
self.ground_contact = false
self.anim = nil
go.set(".", "euler.z", 0)
go.set_position(self.position)
msg.post("#collisionobject", "enable")
elseif message_id == hash("contact_point_response") then
-- check if we received a contact point message
if message.group == hash("danger") then
-- Die and restart
play_animation(self, hash("death"))
msg.post("#collisionobject", "disable")
-- <1>
go.animate(".", "euler.z", go.PLAYBACK_ONCE_FORWARD, 160, go.EASING_LINEAR, 0.7)
go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, go.get_position().y - 200, go.EASING_INSINE, 0.5, 0.2,
function()
msg.post("#", "reset")
end)
elseif message.group == hash("geometry") then
handle_geometry_contact(self, message.normal, message.distance)
end
end
end
Modifica la funzione init() affinché invii un messaggio “reset” per inizializzare l’oggetto, poi salva il file:
-- hero.script
function init(self)
-- this lets us handle input in this script
msg.post(".", "acquire_input_focus")
-- save position
self.position = go.get_position()
msg.post("#", "reset")
end
Se provi il gioco ora, ti accorgerai subito che il meccanismo di ripristino non funziona. L’eroe viene ripristinato correttamente, ma può facilmente ritrovarsi in una situazione in cui cade immediatamente sul bordo di una piattaforma e muore di nuovo. Vogliamo invece ripristinare correttamente l’intero livello alla morte dell’eroe. Poiché il livello è soltanto una serie di piattaforme generate, basta tenere traccia di tutte le piattaforme generate ed eliminarle al ripristino:
Apri il file controller.script e modifica il codice in modo che memorizzi gli ID di tutte le piattaforme generate:
-- controller.script
go.property("speed", 360)
local grid = 460
local platform_heights = { 100, 200, 350 }
function init(self)
msg.post("ground/controller#controller", "set_speed", { speed = self.speed })
self.gridw = 0
self.spawns = {} -- <1>
end
function update(self, dt)
self.gridw = self.gridw + self.speed * dt
if self.gridw >= grid then
self.gridw = 0
-- Maybe spawn a platform at random height
if math.random() > 0.2 then
local h = platform_heights[math.random(#platform_heights)]
local f = "#platform_factory"
if math.random() > 0.5 then
f = "#platform_long_factory"
end
local p = factory.create(f, vmath.vector3(1600, h, 0), nil, {}, vmath.vector3(0.6, 0.6, 1))
msg.post(p, "set_speed", { speed = self.speed })
table.insert(self.spawns, p) -- <1>
end
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("reset") then -- <2>
-- Tell the hero to reset.
msg.post("hero#hero", "reset")
-- Delete all platforms
for i,p in ipairs(self.spawns) do
go.delete(p)
end
self.spawns = {}
elseif message_id == hash("delete_spawn") then -- <3>
for i,p in ipairs(self.spawns) do
if p == message.id then
table.remove(self.spawns, i)
go.delete(p)
end
end
end
end
Apri platform.script e modificalo in modo che, invece di eliminare semplicemente una piattaforma che ha raggiunto l’estremità sinistra, invii un messaggio al controller del livello chiedendo di rimuoverla:
-- platform.script
...
if pos.x < -500 then
msg.post("/level/controller#controller", "delete_spawn", { id = go.get_id() })
end
...

-- hero.script
...
go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, go.get_position().y - 200, go.EASING_INSINE, 0.5, 0.2,
function()
msg.post("controller#controller", "reset")
end)
...

Ora il ciclo principale di ripartenza e morte è pronto!
Passiamo a qualcosa per cui valga la pena vivere: le monete!
L’idea è inserire nel livello delle monete che il giocatore possa raccogliere. La prima domanda è come inserirle. Potremmo, per esempio, sviluppare uno schema di generazione coordinato in qualche modo con l’algoritmo di generazione delle piattaforme. Alla fine, però, abbiamo scelto un approccio molto più semplice: lasciare che siano le piattaforme stesse a generare le monete:
Crea un nuovo file di script coin.script (fai clic con il pulsante destro su level nel pannello Assets e seleziona New ▸ Script File). Sostituisci il codice del modello con quanto segue:
-- coin.script
function init(self)
self.collected = false
end
function on_message(self, message_id, message, sender)
if self.collected == false and message_id == hash("collision_response") then
self.collected = true
msg.post("#sprite", "disable")
elseif message_id == hash("start_animation") then
pos = go.get_position()
go.animate(go.get_id(), "position.y", go.PLAYBACK_LOOP_PINGPONG, pos.y + 24, go.EASING_INOUTSINE, 0.75, message.delay)
end
end
Aggiungi il file di script come componente Script all’oggetto della moneta (fai clic con il pulsante destro sulla radice in Outline e seleziona Add Component from File).

Vogliamo generare le monete dagli oggetti delle piattaforme, quindi aggiungi le fabbriche per le monete a platform.go e platform_long.go.

Ora dobbiamo modificare platform.script affinché generi ed elimini le monete:
-- platform.script
function init(self)
self.speed = 540 -- Default speed in pixels/s
self.coins = {}
end
function final(self)
for i,p in ipairs(self.coins) do
go.delete(p)
end
end
function update(self, dt)
local pos = go.get_position()
if pos.x < -500 then
msg.post("/level/controller#controller", "delete_spawn", { id = go.get_id() })
end
pos.x = pos.x - self.speed * dt
go.set_position(pos)
end
function create_coins(self, params)
local spacing = 56
local pos = go.get_position()
local x = pos.x - params.coins * (spacing*0.5) - 24
for i = 1, params.coins do
local coin = factory.create("#coin_factory", vmath.vector3(x + i * spacing , pos.y + 64, 1))
msg.post(coin, "set_parent", { parent_id = go.get_id() }) -- <1>
msg.post(coin, "start_animation", { delay = i/10 }) -- <2>
table.insert(self.coins, coin)
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("set_speed") then
self.speed = message.speed
elseif message_id == hash("create_coins") then
create_coins(self, message)
end
end
Le relazioni genitore-figlio modificano esclusivamente il grafo della scena. Un figlio viene trasformato (spostato, ridimensionato o ruotato) insieme al suo genitore. Se ti servono ulteriori relazioni di “appartenenza” tra gli oggetti di gioco, devi tenerne traccia esplicitamente nel codice.
L’ultimo passaggio di questo tutorial consiste nell’aggiungere un paio di righe a controller.script:
-- controller.script
...
local platform_heights = { 100, 200, 350 }
local coins = 3 -- <1>
...
-- controller.script
...
local coins = coins
if math.random() > 0.5 then
f = "#platform_long_factory"
coins = coins * 2 -- Twice the number of coins on long platforms
end
...
-- controller.script
...
msg.post(p, "set_speed", { speed = self.speed })
msg.post(p, "create_coins", { coins = coins })
table.insert(self.spawns, p)
...

Ora abbiamo un gioco semplice ma funzionante! Se sei arrivato fin qui, potresti voler continuare per conto tuo e aggiungere quanto segue:
Scarica la versione completa del progetto qui.
Questo conclude il tutorial introduttivo. Ora continua a esplorare Defold. Abbiamo preparato molti manuali e tutorial per guidarti e, se ti blocchi, sei il benvenuto sul forum.
Buon divertimento con Defold!