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
Neste tutorial, começamos com um projeto vazio e criamos um jogo runner completo com um personagem animado, colisão física, pickups e pontuação.
Há muito a absorver ao aprender uma nova game engine, então criamos este tutorial para ajudar você a começar. É um tutorial bastante completo que mostra como a engine e o editor funcionam. Assumimos que você tem alguma familiaridade com programação.
Se precisar de uma introdução à programação em Lua, confira nosso manual de Lua no Defold.
Se achar que este tutorial é um pouco demais para começar, confira nossa página de tutoriais, onde temos uma seleção de tutoriais com dificuldades variadas.
Se preferir assistir a tutoriais em vídeo, confira a versão em vídeo no Youtube.
Usamos assets de jogo de dois outros tutoriais, com pequenas modificações. O tutorial é dividido em várias etapas, com cada parte nos levando um passo importante em direção ao jogo final.
O resultado final será um jogo em que você controla um personagem herói que corre por um ambiente, coletando moedas e evitando obstáculos. O herói corre a uma velocidade fixa, e o jogador controla apenas o pulo do personagem pressionando um único botão (ou tocando a tela em um dispositivo móvel). O nível consiste em um fluxo infinito de plataformas para pular - e de moedas para coletar.
Se em algum momento você ficar preso neste tutorial ou ao criar seu jogo, não hesite em pedir ajuda no Defold Forum. No fórum, você pode discutir Defold, pedir ajuda à equipe do Defold, ver como outros desenvolvedores de jogos resolveram seus problemas e encontrar nova inspiração. Comece agora.
Ao longo do tutorial, descrições detalhadas sobre conceitos e sobre como fazer certas etapas são marcadas como este parágrafo. Se você achar que essas seções entram em detalhes demais, pode pulá-las.
Então vamos começar. Esperamos que você se divirta bastante ao seguir este tutorial e que ele ajude você a engrenar com o Defold.
Baixe os assets deste tutorial aqui.
O primeiro passo é baixar os seguintes arquivos.
Agora, se você ainda não baixou e instalou o editor Defold, é hora de fazer isso:
Acesse a página de download do Defold, onde você encontrará botões Download para macOS, Windows e Linux (Ubuntu):

Instalação no macOS :: O arquivo baixado é uma imagem DMG que contém o programa.
Para iniciar o editor, abra sua pasta “Applications” e dê um clique duplo no arquivo “Defold”.

Instalação no Windows :: O arquivo baixado é um arquivo ZIP que precisa ser extraído:
D:\Defold). Você não deve mover o Defold para C:\Program Files (x86)\ ou C:\Program Files\, pois isso impedirá que o editor seja atualizado.Para iniciar o editor, abra a pasta “Defold” e dê um clique duplo no arquivo “Defold.exe”.

Instalação no Linux :: O arquivo baixado é um arquivo ZIP que precisa ser extraído:
Em um terminal, localize o arquivo “Defold-x86_64-linux.zip” e descompacte-o em um diretório de destino chamado “Defold”.
$ unzip Defold-x86_64-linux.zip -d Defold
Para iniciar o editor, mude para o diretório onde você extraiu a aplicação e execute o executável Defold, ou dê um clique duplo nele na sua área de trabalho.
$ cd Defold
$ ./Defold
Há um auxiliar para instalar uma entrada de desktop no menu Help > Create Desktop Entry.
Se você tiver problemas para iniciar o editor, abrir um projeto ou rodar um jogo Defold, consulte a seção de Linux da FAQ.
Todas as versões beta e estáveis do Defold também estão disponíveis no GitHub.
Quando o editor estiver instalado e iniciado, é hora de criar um novo projeto e prepará-lo. Crie um novo projeto a partir do template “Empty Project”.
Este tutorial usa recursos de Spine, que foram movidos para sua própria extensão após o Defold 1.2.188. Se você estiver usando uma versão mais nova, adicione a Spine Extension à seção de dependências de game.project.
Na primeira vez que você inicia o editor, ele começa em branco, sem nenhum projeto aberto. Então escolha Open Project no menu e selecione seu projeto recém-criado. Você também será solicitado a criar um “branch” para o projeto.
Agora, no Assets pane, você verá todos os arquivos que fazem parte do projeto. Se der um duplo clique no arquivo “main/main.collection”, o arquivo será aberto na visualização do editor no centro:

O editor consiste nas seguintes áreas principais:
print() e pprint() nos seus scripts. Se o seu app ou jogo não iniciar, o console é o primeiro lugar a verificar. Atrás do console há um conjunto de abas exibindo informações de erro, além de um editor de curvas usado ao criar efeitos de partículas.O template de projeto “Empty” é, de fato, completamente vazio. Mesmo assim, selecione Project ▸ Build para compilar o projeto e iniciar o jogo.

Uma tela preta talvez não seja muito empolgante, mas é uma aplicação de jogo Defold em execução, e podemos modificá-la facilmente para algo mais interessante. Então vamos fazer isso.
O editor Defold trabalha com arquivos. Ao dar duplo clique em um arquivo no Assets pane, você o abre em um editor adequado. Depois, pode trabalhar com o conteúdo do arquivo.
Quando terminar de editar um arquivo, você precisa salvá-lo. Selecione File ▸ Save no menu principal. O editor dá uma dica adicionando um asterisco ‘*’ ao nome do arquivo na aba de qualquer arquivo que contenha alterações não salvas.

Antes de começar, vamos configurar várias opções do nosso projeto. Abra o asset game.project no Assets Pane e role até a seção Display. Defina width e height do projeto como 1280 e 720, respectivamente.
Você também precisa adicionar a extensão Spine ao projeto para que possamos animar o personagem herói. Adicione uma versão da extensão Spine que seja compatível com a versão do editor Defold instalada. As versões disponíveis da Spine podem ser vistas aqui:
https://github.com/defold/extension-spine/releases
Clique com o botão direito no link para o arquivo zip da release que você quer usar:

Adicione o link da release à sua lista de dependências de game.project. Depois que a extensão Spine for adicionada, você também precisa reiniciar o editor para ativar a integração com o editor incluída na extensão Spine.
Vamos dar os primeiros passos e criar uma arena para nosso personagem, ou melhor, um trecho de chão com rolagem. Fazemos isso em algumas etapas.
Um Atlas é um arquivo que combina um conjunto de imagens separadas em um único arquivo de imagem maior. O motivo para fazer isso é economizar espaço e também ganhar performance. Você pode ler mais sobre Atlases e outros recursos de gráficos 2D na documentação de gráficos 2D.


Por que não funciona!? Um problema comum de quem está começando com Defold é esquecer de salvar! Depois de adicionar imagens a um atlas, você precisa salvar o arquivo antes de conseguir acessar essa imagem.
Crie um arquivo de coleção ground.collection para o chão e adicione 7 objetos de jogo a ele (clique com o botão direito na raiz da coleção na visualização Outline e selecione Add Game Object). Nomeie os objetos “ground0”, “ground1”, “ground2” etc. alterando a propriedade Id na visualização Properties. Observe que o Defold atribui automaticamente um id único aos novos objetos de jogo.
Em cada objeto, adicione um componente de sprite (clique com o botão direito no objeto de jogo na visualização Outline e selecione Add Component, depois selecione Sprite e clique em OK), defina a propriedade Image do componente de sprite para o atlas que você acabou de criar e defina a animação padrão do sprite para uma das duas imagens de chão. Defina a posição X do componente de sprite (não do objeto de jogo) como 190 e a posição Y como 40. Como a largura da imagem é 380 pixels e a deslocamos lateralmente pela metade disso, o pivô do objeto de jogo ficará na borda mais à esquerda da imagem do sprite.


Provavelmente é mais fácil criar um objeto de jogo completo já escalado com um componente de sprite e depois copiá-lo. Marque-o na visualização Outline, selecione Edit ▸ Copy e depois Edit ▸ Paste.
Vale observar que, se você quiser tiles maiores ou menores, pode simplesmente alterar a escala. Porém, ao fazer isso, também precisará alterar as posições X de todos os objetos de jogo do chão para múltiplos da nova largura.
Salve o arquivo e então adicione ground.collection ao arquivo main.collection: primeiro dê um duplo clique no arquivo main.collection, depois clique com o botão direito no objeto raiz na visualização Outline e selecione Add Collection From File. No diálogo, selecione ground.collection e clique em OK. Certifique-se de colocar ground.collection na posição 0, 0, 0, ou ela ficará deslocada visualmente. Salve.
Inicie o jogo (Project ▸ Build) para ver se tudo está no lugar.

Neste ponto, talvez você esteja confuso e se perguntando o que são todas essas coisas que criamos. Então vamos parar um momento e olhar para os blocos de construção mais básicos de qualquer projeto Defold:
Por enquanto, essas descrições provavelmente bastam. Porém, um mergulho muito mais abrangente nesses conceitos pode ser encontrado no manual de Blocos de Construção. É uma boa ideia visitar esse manual em uma etapa posterior para entender melhor como as coisas funcionam no Defold.
Agora que temos todas as peças do chão no lugar, é bastante simples fazê-las se mover. A ideia é esta: mover as peças da direita para a esquerda e, quando uma peça alcançar a borda esquerda fora da tela, movê-la para a posição mais à direita. Para mover todos esses objetos de jogo, é necessário um script Lua, então vamos criar um:
-- ground.script
local pieces = { "ground0", "ground1", "ground2", "ground3",
"ground4", "ground5", "ground6" } -- <1>
function init(self) -- <2>
self.speed = 360 -- Velocidade em 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() é chamada quando o objeto de jogo ganha vida no jogo. Inicializamos uma variável de membro local do objeto que contém a velocidade do chão.update() é chamada uma vez a cada frame, normalmente 60 vezes por segundo. dt contém o número de segundos desde a última chamada.dt para obter velocidade independente de framerate em pixels/s.Defold é um núcleo de engine rápido que gerencia seus dados e objetos de jogo. Qualquer lógica ou comportamento de que você precise para seu jogo é criado na linguagem Lua. Lua é uma linguagem de programação rápida e leve, excelente para escrever lógica de jogo. Há ótimos recursos disponíveis para aprender a linguagem, como o livro Programming in Lua e o manual de referência Lua oficial.
O Defold adiciona um conjunto de APIs sobre Lua, além de um sistema de passagem de mensagens que permite programar comunicações entre objetos de jogo. Veja o manual de passagem de mensagens para detalhes sobre como isso funciona.
Você pode alternar as seções Assets Pane, Console e Outline do editor usando as teclas F6, F7 e F8, respectivamente
Agora que temos um arquivo de script, devemos adicionar uma referência a ele em um componente de um objeto de jogo. Assim, o script será executado como parte do ciclo de vida do objeto de jogo. Fazemos isso criando um novo objeto de jogo em ground.collection e adicionando ao objeto um componente Script que se refere ao arquivo de script Lua que acabamos de criar:

Agora, quando você executar o jogo, o objeto de jogo “controller” executará o script em seu componente Script, fazendo o chão rolar suavemente pela tela.
O personagem herói será um objeto de jogo composto pelos seguintes componentes:
Comece importando as imagens das partes do corpo e depois adicione-as a um novo atlas que chamaremos de hero.atlas:

Também precisamos importar os dados de animação Spine e configurar uma Spine Scene para eles:

O arquivo hero.spinejson foi exportado no formato Spine JSON. Você precisará do software de animação Spine para criar arquivos assim. Se quiser usar outro software de animação, pode exportar suas animações como sprite-sheets e usá-las como animações flip-book a partir de recursos Tile Source ou Atlas. Veja o manual sobre Animação para mais informações.
Agora podemos começar a construir o gameobject do herói:

Agora é hora de adicionar física para a colisão funcionar:
Colisão “Kinematic” significa que queremos que colisões sejam registradas, mas a engine de física não resolverá colisões automaticamente nem simulará os objetos. A engine de física oferece suporte a vários tipos diferentes de objetos de colisão. Você pode ler mais sobre eles na documentação de Física.
É importante especificar com o que o objeto de colisão deve interagir:
Por fim, crie um novo arquivo hero.script e adicione-o ao objeto de jogo.
handle_geometry_contact().)
O motivo de tratarmos a colisão por conta própria é que, se em vez disso definíssemos o tipo do objeto de colisão do personagem como dynamic, a engine executaria uma simulação newtoniana dos corpos envolvidos. Para um jogo como este, tal simulação está longe do ideal; então, em vez de lutar contra a engine de física com várias forças, assumimos controle total.
Para fazer isso e tratar colisão corretamente, é necessário um pouco de matemática vetorial. Uma explicação completa sobre como resolver colisões cinemáticas é dada na documentação de Física.
-- gravidade puxando o jogador para baixo em unidades de pixel/sˆ2
local gravity = -20
-- velocidade de decolagem ao pular em unidades de pixel/s
local jump_takeoff_speed = 900
function init(self)
-- isto diz a engine para enviar entrada para on_input() neste script
msg.post(".", "acquire_input_focus")
-- salva a posicao inicial
self.position = go.get_position()
-- acompanha o vetor de movimento e se ha contato com o chao
self.velocity = vmath.vector3(0, 0, 0)
self.ground_contact = false
end
function final(self)
-- Devolve o foco de entrada quando o objeto e excluido
msg.post(".", "release_input_focus")
end
function update(self, dt)
local gravity = vmath.vector3(0, gravity, 0)
if not self.ground_contact then
-- Aplica gravidade se nao houver contato com o chao
self.velocity = self.velocity + gravity
end
-- aplica velocidade ao personagem do jogador
go.set_position(go.get_position() + self.velocity * dt)
-- redefine estado volatil
self.correction = vmath.vector3()
self.ground_contact = false
end
local function handle_geometry_contact(self, normal, distance)
-- projeta o vetor de correcao sobre a normal do contato
-- (o vetor de correcao e o vetor 0 no primeiro ponto de contato)
local proj = vmath.dot(self.correction, normal)
-- calcula a compensacao que precisamos fazer para este ponto de contato
local comp = (distance - proj) * normal
-- adiciona ao vetor de correcao
self.correction = self.correction + comp
-- aplica a compensacao ao personagem do jogador
go.set_position(go.get_position() + comp)
-- verifica se a normal aponta para cima o suficiente para considerar que o jogador esta no chao
-- (0.7 e aproximadamente igual a 45 graus de desvio da direcao vertical pura)
if normal.y > 0.7 then
self.ground_contact = true
end
-- projeta a velocidade sobre a normal
proj = vmath.dot(self.velocity, normal)
-- se a projecao for negativa, significa que parte da velocidade aponta para o ponto de contato
if proj < 0 then
-- nesse caso, remove esse componente
self.velocity = self.velocity - proj * normal
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("contact_point_response") then
-- verifica se recebemos uma mensagem de ponto de contato. Uma mensagem para cada ponto de contato
if message.group == hash("geometry") then
handle_geometry_contact(self, message.normal, message.distance)
end
end
end
local function jump(self)
-- permite pular apenas a partir do chao
if self.ground_contact then
-- define a velocidade de decolagem
self.velocity.y = jump_takeoff_speed
end
end
local function abort_jump(self)
-- interrompe o pulo se ainda estivermos subindo
if self.velocity.y > 0 then
-- reduz a velocidade para cima
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 quiser, agora você pode tentar adicionar temporariamente o personagem herói à coleção principal e executar o jogo para vê-lo cair através do mundo.
A última coisa de que precisamos para o herói ser funcional é entrada. O script acima já contém uma função on_input() que responde às ações “jump” e “touch” (para telas de toque). Vamos adicionar mapeamentos de entrada para essas ações.

Agora que temos um personagem herói configurado com colisão e tudo mais, precisamos também adicionar colisão ao chão para que o personagem tenha algo com que colidir (ou sobre o que correr). Faremos isso em um segundo, mas primeiro devemos fazer uma pequena refatoração, colocar tudo do nível em uma coleção separada e limpar um pouco a estrutura de arquivos:
Como você talvez já tenha notado, a hierarquia de arquivos vista no Assets pane é desacoplada da estrutura de conteúdo que você cria em suas coleções. Arquivos individuais são referenciados por arquivos de coleção e de objeto de jogo, mas sua localização é completamente arbitrária.
Se você quiser mover um arquivo para um novo local, o Defold ajuda atualizando automaticamente as referências ao arquivo (refatoração). Ao criar um software complexo, como um jogo, é extremamente útil poder alterar a estrutura do projeto conforme ele cresce e muda. O Defold incentiva isso e torna o processo suave, então não tenha medo de mover seus arquivos!
Também devemos adicionar um objeto de jogo controller com um componente de script à coleção de nível:
Abra o arquivo de script, copie o código a seguir para ele e salve:
-- controller.script
go.property("speed", 360) -- <1>
function init(self)
msg.post("ground/controller#ground", "set_speed", { speed = self.speed })
end

O objeto de jogo “controller” não existe em um arquivo, mas é criado no local dentro da coleção de nível. Isso significa que a instância do objeto de jogo é criada a partir dos dados no local. Isso é adequado para objetos de jogo de propósito único como este. Se você precisar de várias instâncias de algum objeto de jogo e quiser poder modificar o protótipo/template usado para criar cada instância, basta criar um arquivo de objeto de jogo e adicionar o objeto de jogo a partir do arquivo à coleção. Isso cria um objeto de jogo com uma referência ao arquivo como protótipo/template.
Agora, o propósito desse objeto de jogo “controller” é controlar tudo relacionado ao nível em execução. Em breve, esse script ficará encarregado de criar plataformas e moedas para o herói interagir, mas por enquanto ele apenas definirá a velocidade do nível.
Na função init() do script controller do nível, ele envia uma mensagem ao componente de script do objeto controller do chão, endereçado por seu id:
msg.post("ground/controller#controller", "set_speed", { speed = self.speed })
O id do objeto de jogo controller é definido como "ground/controller", já que ele vive na coleção “ground”. Depois adicionamos o id do componente "controller" após o caractere hash "#", que separa o id do objeto do id do componente. Observe que o script do chão ainda não tem nenhum código para reagir à mensagem set_speed, então precisamos adicionar uma função on_message() a ground.script e adicionar lógica para isso.
-- 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

Neste ponto, devemos adicionar colisão física ao chão:

Agora você deve conseguir executar o jogo (Project ▸ Build). O personagem herói deve correr no chão, e deve ser possível pular com o botão Space. Se você executar o jogo em um dispositivo móvel, pode pular tocando na tela.
Para deixar a vida no mundo do jogo um pouco menos monótona, devemos adicionar plataformas para pular.
Crie um arquivo Script platform.script (clique com o botão direito no Assets pane e selecione New ▸ Script File), coloque o código a seguir no arquivo e salve:
-- platform.script
function init(self)
self.speed = 540 -- Velocidade padrao em 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

Observe que tanto platform.go quanto platform_long.go têm componentes Script que se referem ao mesmo arquivo de script. Isso é bom, pois quaisquer alterações que fizermos no arquivo de script afetarão o comportamento tanto das plataformas normais quanto das longas.
A ideia do jogo é ser um endless runner simples. Isso significa que os objetos de jogo de plataforma não podem ser colocados em uma coleção no editor. Em vez disso, precisamos criá-los 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
-- Talvez crie uma plataforma em altura aleatoria
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, {}, 0.6)
msg.post(p, "set_speed", { speed = self.speed })
end
end
end
update() é chamada uma vez a cada frame e usamos isso para decidir se devemos criar uma plataforma normal ou longa em certos intervalos (para evitar sobreposições) e alturas. É fácil experimentar vários algoritmos de spawn para criar jogabilidades diferentes.Agora execute o jogo (Project ▸ Build).
Uau, isso está começando a virar algo (quase) jogável…

A primeira coisa que vamos fazer é dar vida ao personagem herói. Agora, o coitado está preso em um loop de corrida e não responde bem a pulos nem a qualquer outra coisa. O arquivo Spine que adicionamos a partir do pacote de assets na verdade contém um conjunto de animações exatamente para isso.
update() existente: -- hero.script
local function play_animation(self, anim)
-- reproduz apenas animacoes que ainda nao estao em execucao
if self.anim ~= anim then
-- informa ao spine model para reproduzir a animacao
local anim_props = { blend_duration = 0.15 }
spine.play_anim("#spinemodel", anim, go.PLAYBACK_LOOP_FORWARD, anim_props)
-- lembra qual animacao esta tocando
self.anim = anim
end
end
local function update_animation(self)
-- garante que a animacao correta esteja tocando
if self.ground_contact then
play_animation(self, hash("run"))
else
play_animation(self, hash("jump"))
end
end
update() e adicione uma chamada a update_animation: ...
-- aplica isso ao personagem do jogador
go.set_position(go.get_position() + self.velocity * dt)
update_animation(self)
...

Lua tem “escopo léxico” para variáveis locais e é sensível à ordem em que você coloca funções local. A função update() chama as funções locais update_animation() e play_animation(), o que significa que o runtime precisa ter visto as funções locais para conseguir chamá-las. É por isso que precisamos colocar as funções antes de update(). Se você inverter a ordem das funções, receberá um erro. Observe que isso se aplica apenas a variáveis local. Você pode ler mais sobre as regras de escopo de Lua e funções locais em http://www.lua.org/pil/6.2.html
Isso é tudo que é necessário para adicionar animações de pulo e queda ao herói. Se você executar o jogo, perceberá que ele fica muito melhor de jogar. Talvez também perceba que, infelizmente, as plataformas podem empurrar o herói para fora da tela. Isso é um efeito colateral do tratamento de colisão, mas o remédio é fácil: adicionar perigo e tornar as bordas das plataformas perigosas!
Salve o arquivo.

Abra hero.go, marque o Collision Object e adicione o nome “danger” à propriedade Mask. Depois salve o arquivo.

Abra hero.script e altere a função on_message() para que tenhamos uma reação se o personagem herói colidir com uma borda “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
-- verifica se recebemos uma mensagem de ponto de contato
if message.group == hash("danger") then
-- Morre e reinicia
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
Altere a função init() para enviar uma mensagem “reset” que inicializa o objeto, depois salve o arquivo:
-- hero.script
function init(self)
-- isto nos permite lidar com entrada neste script
msg.post(".", "acquire_input_focus")
-- salva a posicao
self.position = go.get_position()
msg.post("#", "reset")
end
Se você testar o jogo agora, rapidamente ficará claro que o mecanismo de reset não funciona. O reset do herói está ok, mas você pode facilmente reiniciar em uma situação em que cairá imediatamente sobre a borda de uma plataforma e morrerá de novo. O que queremos é redefinir corretamente o nível inteiro quando houver morte. Como o nível é apenas uma série de plataformas criadas por spawn, só precisamos rastrear todas as plataformas criadas e então excluí-las no reset:
Abra o arquivo controller.script e edite o código para armazenar os ids de todas as plataformas criadas:
-- 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
-- Talvez crie uma plataforma em altura aleatoria
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, {}, 0.6)
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>
-- Diz ao heroi para reiniciar.
msg.post("hero#hero", "reset")
-- Exclui todas as plataformas
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
Abra platform.script e modifique-o para que, em vez de apenas excluir uma plataforma que alcançou a borda mais à esquerda, ele envie uma mensagem ao controller do nível pedindo para remover a plataforma:
-- 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)
...

E agora o loop principal de reiniciar-morrer está no lugar!
Próximo passo - algo pelo que viver: moedas!
A ideia é colocar moedas no nível para o jogador coletar. A primeira pergunta é como colocá-las no nível. Poderíamos, por exemplo, desenvolver um esquema de spawn que esteja de alguma forma em sintonia com o algoritmo de spawn de plataformas. Porém, no fim escolhemos uma abordagem muito mais fácil: fazer as próprias plataformas criarem moedas:
Crie um novo arquivo de script coin.script (clique com o botão direito em level no Assets pane e selecione New ▸ Script File). Substitua o código do template pelo seguinte:
-- 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
Adicione o arquivo de script como componente Script ao objeto da moeda (clique com o botão direito na raiz em Outline e selecione Add Component from File).

O plano é criar as moedas a partir dos objetos de plataforma, então coloque fábricas para as moedas em platform.go e platform_long.go.

Agora precisamos modificar platform.script para que ele crie e exclua as moedas:
-- platform.script
function init(self)
self.speed = 540 -- Velocidade padrao em 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
Relações pai-filho são estritamente uma modificação do scene graph. Um filho será transformado (movido, escalado ou rotacionado) junto com seu pai. Se você precisar de relações adicionais de “posse” entre objetos de jogo, precisará rastrear isso especificamente no código.
A última etapa deste tutorial é adicionar algumas linhas 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 -- O dobro de moedas em plataformas longas
end
...
-- controller.script
...
msg.post(p, "set_speed", { speed = self.speed })
msg.post(p, "create_coins", { coins = coins })
table.insert(self.spawns, p)
...

E agora temos um jogo simples, mas funcional! Se você chegou até aqui, talvez queira continuar por conta própria e adicionar o seguinte:
Baixe a versão concluída do projeto aqui
Isso conclui este tutorial introdutório. Agora vá em frente e mergulhe no Defold. Temos muitos manuais e tutoriais preparados para guiar você e, se ficar preso, será bem-vindo no fórum.
Boas criações com Defold!