Seu guia completo para construir APIs de sucesso e acelerar sua Transformação Digital

Low-code: como criar um jogo 2D de plataforma no Unity

Low-code: como criar um jogo 2D de plataforma no Unity

Desenvolver um software não costuma ser uma tarefa fácil e exige um grande conhecimento sobre programação, além de tempo investido. Sabendo disso, diversas empresas constroem plataformas que permitem elaborar um programa utilizando o mínimo de código possível.

Crie um jogo 2D rapidamente com a engine Unity, seguindo o conceito de low-code.

Desenvolver um software não costuma ser uma tarefa fácil e exige um grande conhecimento sobre programação, além de tempo investido. Sabendo disso, diversas empresas constroem plataformas que permitem elaborar um programa utilizando o mínimo de código possível.

Isso diz respeito ao conceito de low-code. Através do uso de uma interface gráfica, plataformas criadas seguindo essa filosofia reduzem o tempo de desenvolvimento e o conhecimento necessário para a produção de um software.

Para o desenvolvimento de jogos eletrônicos, existem as engines, também conhecidas como motores de jogos. Elas são plataformas low-code que visam simplificar o desenvolvimento, tomando para si as responsabilidades de renderizar os gráficos, calcular a física, oferecer suporte a diversos outros requisitos de um game e diversas ferramentas que agilizam o processo.

Dentre os motores mais conhecidos estão: Unreal Engine (Final Fantasy VII Remake, Crash Bandicoot 4), Unity (Fall Guys, Genshin Impact), Game Maker (Hotline Miami, Shovel Knight), Source (The Stanley Parable, Portal 2), RAGE (GTA V, Red Dead Redemption 2), Frostbite (Dragon Age Inquisition, Battlefield 5), Havok (No Man’s Sky, Dark Souls, Zelda: Breath of the Wild) e CryEngine (Prey, Warface). Eles podem ser gratuitos, pagos ou até mesmo de uso restrito, onde apenas a empresa criadora pode utilizá-la.

Em 2020, a desenvolvedora Media Molecule lançou um sistema completamente gamificado para o PlayStation 4. O projeto, chamado Dreams, deu a muitos gamers a oportunidade de experimentarem uma plataforma Low-Code e também criou uma comunidade de criadores “semi-profissionais”. O servidor do Dreams permite que você viage no meio de todas as criações e experimente o quanto quiser.

Quer aprender a fazer um jogo como este? Continue lendo o artigo!

Mãos a obra

Neste artigo, você aprenderá a como desenvolver um jogo 2D de plataforma, utilizando a licença gratuita da engine Unity.

Configuração inicial do Unity

  1. Baixe e instale o Unity Hub (caso não tenha um editor de código, permita a instalação do Visual Studio).
  2. Crie uma conta e faça login pelo próprio Unity Hub.
  3. Instale o editor Unity.
  4. Adicione uma licença de uso:

Criando o projeto

Acesse a área Projects, clique em NEW, selecione o template 2D, nomeie o projeto, selecione uma pasta para salvar e clique em CREATE.

Se estiver tudo certo, seu editor do Unity será iniciado e o projeto será aberto pronto para começar sua criação.

Baixando os assets

Os assets são todos os componentes que compõem os jogos, como personagens, cenários, obstáculos e diversos objetos que podem compor a cena. Você pode criar seus assets do zero, desenhando as texturas, animando e desenvolvendo a lógica ou utilizar os que já foram desenvolvidos por outras pessoas.

O Unity disponibiliza a Asset Store, onde é possível encontrar assets gratuitos ou pagos. Neste tutorial, vamos utilizar modelos gratuitos que estão disponíveis nessa loja virtual e faremos nossas alterações.

Para adicionar um asset ao seu Unity, você deve fazer login na Asset Store, acessar o link onde está disponível e clicar em Add to My Assets. Após este processo, ele estará salvo na sua conta e poderá ser acessado no editor. Vamos utilizar os seguintes assets:

Hero Knight - Pixel Art

Free 2D Mega Pack

Para adicionar os assets no editor, basta acessar o Package Manager...

... e baixar o pacote em questão.

Após efetuar o download, clique em Import para importar os arquivos para dentro do projeto. Este botão substituirá o antigo botão de Download.

Altere o editor de código - opcional

Caso tenha instalado o Visual Studio durante a instalação do Unity Hub e queira utilizá-lo, pule esta etapa. Se tiver um outro editor e queira utilizá-lo, vá em EditPreferences... ExternalTools External Script Editor → Selecione o seu editor de preferência.

Contruindo o ambiente

Comece seu desenvolvimento definindo uma proporção de tela que será utilizada no jogo.

No pacote do guerreiro há alguns recursos básicos de ambiente, como chão e paredes, que também conseguimos usar como plataformas. Utilizaremos eles para compor o cenário, através do Tilemap. Clique com o botão direito em uma área vazia na janela Hierarchy e adicione uma Tilemap retangular:

Abra a janela paleta de tiles para “pintarmos” o cenário.

Aparecerão os tiles criados no pacote. Com a ferramenta de pincel é possível selecionar um bloco que queremos utilizar e pintar o cenário de acordo com sua vontade.

Rotacionando uma parede, você pode criar uma plataforma.

Agora, você pode adicionar o personagem na cena.

É possível executar seu jogo e testar a Scene que está sendo editada, basta utilizar os botões de controle localizados na parte superior da tela.

Se tentar testar a Scene, notará que o cenário não tem física.

Importante: lembre-se de desativar o modo de teste clicando novamente no botão de play (►), pois nenhuma alteração feita enquanto o jogo está rodando é salva.

O responsável por gerar a física do nosso personagem é o componente RigidBody2D que já foi adicionado pelo criador do asset HeroKnight. No Unity, para que o objeto com física aplicada não caia infinitamente, é preciso de um colisor que impeça isso.

Adicione um Tilemap Collider 2D ao cenário que criamos. Para isso, selecione o Tilemap (vou renomear o elemento pai do Tilemap de Grid para Environment por questões de organização apenas), clique em Add Component e Tilemap Collider 2D.

Neste momento, o personagem e o cenário têm o comportamento esperado. Porém, ainda restam 2 problemas. O primeiro é que em determinados momentos o personagem fica preso entre os blocos do chão. No segundo, o personagem fica “enganchado” nos cantos das paredes e isso ocorre devido ao atrito existente no colisor.

Para resolver o primeiro problema, marque a opção Used By Composite dentro do Tilemap Collider 2D do Tilemap, adicione o componente Composite Collider 2D e altere o Body Type do Rigidbody 2D criado para Kinematic.

O desenvolvedor do pacote criou um material que resolve o segundo problema. Selecione o Tilemap, e adicione o material Walls_noFriction no Composite Collider 2D.

Criando a pontuação

No jogo criado neste tutorial, existirão bolinhas que deverão ser coletas para a fase estar completa. Vamos cria-las agora. Crie um sprite de círculo, adicione o componente Circle Collider 2D nele e ajuste o tamanho para 0.5. Na pasta assets, crie uma outra chamada Scripts para salvar os códigos com a lógica de funcionamento dos nossos elementos dinâmicos.

Crie a interface que exibirá os pontos coletados criando um Text. Selecione o Canvas criado, altere o UI Scale Mode para Scale With Screen Size e altere o Match para 1.

Personalize a interface conforme sua preferência e visualize-a na janela Game.

Crie um objeto vazio e nomeie como GameController. Aqui armazenaremos informações de controle do jogo, como por exemplo quantos pontos o jogador coletou.

Crie um C# Script dentro da pasta Scripts e nomeie-o como GameController. A partir deste momento, serão introduzidos códigos escritos na linguagem de programação C#.

Para mais informações sobre os scripts do Unity, acesse esta documentação.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // Importação da interface

public class GameController : MonoBehaviour
{
    public int totalScore; // Variável pública que armazena o total de pontos
    public Text scoreText; // O componente de texto da UI

    public static GameController instance; // Instancia para ser chamada por outras classes

    // Função chamada antes do primeiro frame atualizar
    void Start()
    {
        instance = this;
    }

    // Função pública para atualizar o texto da UI
    public void UpdateScoreText()
    {
        scoreText.text = totalScore.ToString();
    }
}

Adicione o Script criado ao objeto vazio GameController e passe o textScore para ele.

Renomeie o círculo criado anteriormente para Point e habilite a opção isTrigger do seu colisor. Isso permitirá que o jogador atravesse o objeto, perdendo sua física.

Crie um C# Script dentro da pasta Scripts, nomeie-o como Point e adicione-o ao círculo.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Point : MonoBehaviour
{
    // Variável que armazena quantos pontos são adicionados no totalScore ao ser coletado
    public int Score;

    // Função que é chamada quando algo atravessa o objeto
    void OnTriggerEnter2D(Collider2D collider)
    {
        // Se a tag do objeto de quem colidiu for igual a Player
        if(collider.gameObject.tag == "Player") {
            // Soma o totalScore da classe GameController ao Score
            GameController.instance.totalScore += Score;
            // Atualiza o texto da UI através da função da classe GameController
            GameController.instance.UpdateScoreText();
            // Destroy o objeto dono deste Script (Point)
            Destroy(gameObject);
        }
    }
}

Adicione o valor do Score como 1 no objeto Point.

A partir de agora sua pontuação já está funcionando. Porém, vamos alterar o HeroKnight para que em seu pulo, ele caia mais rápido. Basta alterar a escala da gravidade para 3 em seu Rigidbody 2D e a força do pulo para 12.

Criando um inimigo

Por enquanto está muito fácil para o nosso guerreiro. Vamos adicionar um inimigo para dificultar as coisas. No segundo pacote de assets, há um canhão e uma munição. Vamos utilizá-los para compor o inimigo.

Adicione 2 Box Collider 2D para compor a hitbox do inimigo. Uma será do tamanho do inimigo e outra será um pouco maior, para simular a distância da espada. Esta segunda terá a opção isTrigger ativada.

No HeroKnight, altere o Sleeping Mode do Rigidbody 2D para Never Sleep para que o trigger do Box Collider 2D do inimigo identifique a colisão do player quando ele estiver parado.

Na pasta Scripts crie um C# Script, nomeie-o para Cannon, adicione-o ao objeto do canhão e passe o objeto da munição.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cannon : MonoBehaviour
{
    public GameObject bullet; // Variável que armazena o objeto da munição

    // Variável que armazena a distância da munição em relação ao canhão
    private float bulletDistance; 

    // Função chamada quando há um objeto dentro do objeto gatilho
    void OnTriggerStay2D(Collider2D col)
    {
        // Se o objeto que está colidindo tem a tag Player
        if (col.gameObject.tag == "Player") {
            // Se o jogador clicou
            if (Input.GetMouseButtonDown(0)) {
                if (col.transform.position.x - gameObject.transform.position.x > 0) {
                    // Se o jogador está olhando na direção do canhão
                    if(col.GetComponent().flipX == true) {
                        this.DestroyEnemy(); // Chama a função que destrói este objeto e o da munição
                    }
                } else { // Se o jogador está atrás do canhão
                    // Se o jogador está olhando na direção do canhão
                    if(col.GetComponent().flipX == false) {
                        this.DestroyEnemy(); // Chama a função que destrói este objeto e o da munição
                    }
                }
            }
        }
    }

    // Função que destrói este objeto e o da munição
    private void DestroyEnemy() {
        Destroy(gameObject); // Destrói o canhão
        // Calcula distância da munição e do canhão
        bulletDistance = bullet.transform.position.x - gameObject.transform.position.x;

        if (bulletDistance >= -25 && bulletDistance < -15) {
            Destroy(bullet); // Destrói a munição
        } else if (bulletDistance >= -15 && bulletDistance < -10) {
            Destroy(bullet, 1f); // Destrói a munição depois de 1 segundo
        } else if (bulletDistance >= -10 && bulletDistance <= 0) {
            Destroy(bullet, 2f); // Destrói a munição depois de 2 segundos
        }
        
    }
}

Adicione um Box Collider 2D na munição. Na pasta Scripts, crie um C# Script, nomeie-o para Bullet, adicione-o ao objeto da munição e passe o objeto do canhão.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    // Variável que recebe e armazena o objeto do canhão
    public GameObject cannon;
    // Função chamada a cada frame
    void Update()
    {
        // Se a posição x da bala é menor que -20
        if (transform.position.x < -20)
        {
            // Volta a munição à sua posição inicial
            transform.position =  new Vector3(cannon.transform.position.x -0.75f, cannon.transform.position.y + 0.25f, 0);
        }
        // Move a munição para a esquerda
        transform.position = transform.position + new Vector3(-5 * Time.deltaTime, 0, 0);
    }
}

Até o momento, nosso personagem não morre de fato, apenas é empurrado pela munição do inimigo. Vamos construir a tela de fim de jogo para finalizarmos o funcionamento do canhão.

Desenvolvendo o Game Over

Adicione uma imagem à interface ou faça do jeito que achar melhor. Vou criar uma imagem com opacidade reduzida, uma mensagem e um botão para tentar novamente.

Ficando assim:

Deixe esta “imagem” de fim de jogo oculta por padrão, e iremos exibi-la quando o jogo realmente “acabar”.

Agora vamos voltar ao script GameController e criar as funções RestartGame e ShowGameOver

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // Importação da interface
using UnityEngine.SceneManagement; // Importação do gerenciador de cenas

public class GameController : MonoBehaviour
{
    public int totalScore; // Variável pública que armazena o total de pontos
    public Text scoreText; // O componente de texto da UI

    public GameObject gameOver; // Recebe e aramazena a tela de fim de jogo

    public static GameController instance; // Instancia para ser chamada por outras classes

    // Função chamada antes do primeiro frame atualizar
    void Start()
    {
        instance = this;
    }

    // Função pública para atualizar o texto da UI
    public void UpdateScoreText()
    {
        scoreText.text = totalScore.ToString();
    }

    // Função pública que exibe o a tela de fim de jogo
    public void ShowGameOver()
    {
        gameOver.SetActive(true);
    }

    // Função pública que recarrega a fase
    public void RestartGame(string lvlName)
    {
        SceneManager.LoadScene(lvlName);
    }
}

Agora, passe a tela de Game Over criada na imagem através da variável gameOver do GameController.

Volte na tela de game over, selecione o botão e adicione a função RestartGame na lista On Click para reiniciar a fase ao clicar no botão. Renomeie a cena e passe o nome dela no parâmetro da função.

Para renomear a cena, basta clicar com o botão direito nela, Save Scene As e escolher o local e o nome, salve como lvl_1.

Implementando o Game Over

Agora que temos a tela criada, precisamos implementar a funcionalidade que chama o fim de jogo quando a munição atinge o personagem.

Altere o Script HeroKnight dentro da pasta dos Assets para implementar a funcionalidade de game over. Adicione esta função ao final da classe HeroKnight:

    // Game Over
    void OnCollisionEnter2D(Collision2D collision)
    {
        // Se o objeto que está colidindo tem a tag Bullet
        if (collision.gameObject.tag == "Bullet") {
            m_animator.SetTrigger("Death"); // Ativa a animação de morte
            GameController.instance.ShowGameOver(); // Exibe a tela de fim de jogo
            Destroy(gameObject, 1.2f); // Destrói o personagem depois de 1.2 segundo
        }
    }

Crie uma tag chamada Bullet e adicione no objeto da munição:

Implementando o sistema de vitória

Altere o Script GameController, adicionando algumas variáveis e incrementando a função de atualiza score.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // Importação da interface
using UnityEngine.SceneManagement; // Importação do gerenciador de cenas

public class GameController : MonoBehaviour
{
    public int totalScore; // Variável pública que armazena o total de pontos
    public Text scoreText; // O componente de texto da UI

    public GameObject gameOver; // Recebe e armazena a tela de fim de jogo
    public int pointsToWin; // Recebe e armazena a quantidade de pontos necessários para ganhar
    public string nextLevel; // Recebe e armazena o nome da próxima fase

    public static GameController instance; // Instancia para ser chamada por outras classes

    // Função chamada antes do primeiro frame atualizar
    void Start()
    {
        instance = this;        
    }

    // Função pública para atualizar o texto da UI
    public void UpdateScoreText()
    {
        scoreText.text = totalScore.ToString(); // Atualiza o texto do score
        // Se o total de pontos do player é igual a quantidade de pontos para vencer
        if (totalScore == pointsToWin) {
            // Carrega a próxima Scene
            SceneManager.LoadScene(nextLevel);
        }
    }

    // Função pública que exibe o a tela de fim de jogo
    public void ShowGameOver()
    {
        gameOver.SetActive(true);
    }

    // Função pública que recarrega a fase
    public void RestartGame(string lvlName)
    {
        SceneManager.LoadScene(lvlName);
    }
}

Informe um valor para as novas variáveis:

Voilà

Neste caso, só há uma fase, então ao coletar todos os pontos, o jogo reinicia a mesma fase. Mas caso fosse preciso criar mais, é necessário apenas alterar a variável nextLevel e colocar o nome da próxima fase.

Criando um menu inicial e exportando o jogo

 

Altere a variável nextLevel para finalScreen, iremos criar uma Scene de final de jogo. Caso tenha criado mais fases, coloque este nome apenas na última delas.

Duplique uma das Scenes, renomeie para finalScreen e crie a tela de vitória para o jogador que tenha concluído todas as fases. Lembre-se de alterar o botão, neste caso coloquei para redirecionar para o mainMenu (próxima Scene que será criada).

Faça o mesmo para criar uma tela inicial, crie com o nome de mainMenu.

Para finalizar, configure sua build para importar todas as Scenes utilizadas no jogo, caso o contrário haverá um erro.

Topo