Aprendendo a programar jogos em Unity: povoando a cena do jogo com SpriteRenderers

Vamos iniciar a construção dos elementos 2D de nossa cena utilizando um novo tipo de componente visual.

em 20/10/2023
Seja bem-vindo(a) ao GameDev: Aprendendo a programar jogos em Unity de hoje! Dando sequência ao processo de criação de nosso primeiro platformer 2D, iremos começar a inserir alguns objetos que irão compor o cenário e os elementos de interação entre o jogador e o game.


Caso seja a primeira vez que você acessa nossa série, sinta-se à vontade para juntar-se a nós nesta caminhada. Acompanhando o conteúdo desenvolvido desde o primeiro tópico da jornada, você irá aprender a criar um jogo utilizando Unity desde o início, configurando a ferramenta em seu computador e elaborando os diversos projetos que estamos propondo.

Em nosso último tópico, finalizamos a criação do menu de nosso segundo game, Motorista da Pesada; aos poucos vamos aprendendo juntos sobre diversos aspectos técnicos da ferramenta Unity. Venha conosco e vamos juntos nesta trilha rumo a novos conhecimentos!

Povoando a cena de jogo

Retomando o nosso processo de desenvolvimento, no Unity Hub, abra o projeto Motorista da Pesada e, na interface inicial do Unity, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena Fase01, conforme ilustração a seguir:

Note que, diferentemente da cena do menu, a cena Fase01 está bem vazia, sem nenhum elemento visual; isso significa um “prato cheio” para nós, que iremos povoá-la com itens muito interessantes a partir de agora.

Na aba Hierarchy, clique com o botão direito do mouse sobre alguma área vazia e selecione a opção Create Empty. O nome que daremos a esse objeto será Cenario e as coordenadas de Position e Rotation de seu componente Transform (aba Inspector) terão valor zero, conforme demonstrado na imagem a seguir:

O objeto Cenario servirá de “guarda-chuva” para os elementos de nosso jogo que serão apresentados na tela, com exceção de botões para pausar, placares e afins, cujos objetos iremos criar posteriormente em conjunto a um Canvas.

Ainda na aba Hierarchy, dessa vez iremos clicar com o botão direito sobre o objeto Cenario. Crie três novos objetos usando a opção Create Empty três vezes, sequencialmente. Os nomes dos objetos serão Fundo, Itens e Personagem, todos com valores zero em seus respectivos campos Position e Rotation.

Selecione o GameObject Personagem e, na aba Inspector, clique em Add Component, na opção Rendering e, por fim, em Sprite Renderer.

Dentro das propriedades do componente Sprite Renderer, na aba Inspector, indique no campo Sprite que iremos utilizar o desenho com o nome “carrinho” (sem as aspas), conforme indicação na imagem a seguir. Após isso, se quiser, clique duas vezes sobre o objeto Personagem para enxergá-lo melhor. 

Acabamos de posicionar o desenho de um carrinho na tela, mas uma questão pode ter surgido em sua mente. O leitor que já nos acompanha desde o primeiro projeto pode estar se perguntando agora o por quê de não estarmos utilizando um GameObject contendo um componente do tipo Image, subordinado a um Canvas, assim como fizemos anteriormente no jogo Forest Ping Pong ou mesmo no menu de nosso game atual.

A resposta a esse questionamento vem diretamente da maneira como o Unity trabalha com imagens bidimensionais.

SpriteRenderer e Image

O Unity oferece ao programador diversas ferramentas e métodos para permitir o processamento e exibição de imagens na tela. Em relação à exibição de imagens bidimensionais, os sprites, dois dos componentes que podem ser utilizados são os Images e os SpriteRenderers.

Os objetos com componente do tipo Image, que utilizamos até o momento, sempre são subordinados a um Canvas, a nossa “tela de pintura”. Já os objetos com componente do tipo SpriteRenderers podem ser posicionados em qualquer parte de nosso cenário sem a necessidade de um Canvas.

Quando um jogo desenvolvido utilizando Unity vai realizar a exibição de elementos na tela, em geral, a seguinte sequência é executada:
  • Itens do cenário que apresentem algum nível de transparência são renderizados (“desenhados” na tela) dos mais distantes à câmera até os mais próximos; e
  • Itens opacos do cenário são renderizados dos mais próximos à câmera até os mais distantes; 
Para se renderizar algo na tela, são necessários complexos cálculos matemáticos internamente realizados pelo processador e chip gráfico do dispositivo do jogador. Os objetos de um cenário que em determinado momento não estejam no alcance visual de uma câmera não são renderizados, economizando tempo e processamento.

Esse é um dos motivos que fazem com que alguns jogos fiquem mais “lentos” ou com baixa taxa de quadros (baixo FPS, de frames per second) em cenas com muitos personagens na tela, sobretudo em dispositivos mais antigos.

A diferença interna entre Images e SpriteRenderers é que, para o primeiro, o Unity trata a imagem como um retângulo inteiro e, para o último, ele calcula primeiro quais áreas são visíveis e quais são transparentes antes de pedir ao chip gráfico para desenhá-las. 

Na teoria, esse “passo a mais” que é feito com os SpriteRenderers deixaria o processo mais lento, porém, o que ocorre é o inverso, pois como as áreas transparentes das imagens deixam de ser calculadas para fins de renderização, evita-se o fenômeno chamado de overdraw, em que até os pixels não visíveis no momento são reescritos na memória gráfica a todo momento, deixando o game mais lento.

Para jogos com poucos elementos ou que exibam todos os elementos ao mesmo tempo na tela, como é o caso do Forest Ping Pong, isso não é um problema, mas é bom saber que para projetos maiores e mais complexos, sobretudo os que envolvam a utilização de centenas de elementos de tamanhos variados, existem os elementos certos para as ocasiões corretas.

Dessa forma, em geral, utiliza-se objetos do tipo Image para elementos gráficos que compõem a chamada Interface de Usuário (ou UI, de User Interface) de um jogo ou sistema, tais como botões, menus, textos explicativos, placares, dentre outros itens.

Já os SpriteRenderers são normalmente utilizados para a composição dos elementos das cenas em si, como personagens, plataformas, itens e inimigos.

Elementos adicionais

Dadas as explicações técnicas, vamos voltar ao que nos interessa mais: povoar esse cenário vazio.

Já aproveitando que iremos trabalhar com objetos contendo componentes do tipo SpriteRenderer, vamos deixar reservado um Canvas à parte para, posteriormente, inserirmos elementos do tipo Image nele. Para tal, clique com o botão direito sobre uma área vazia de Hierarchy, selecione UI e, por fim, Canvas. O nome do objeto criado será CanvasFase.


Deixe os parâmetros do componente Canvas da aba Inspector conforme indicado na imagem a seguir:


Observe na imagem a seguir que há um “descolamento” entre o Canvas, indicado pela seta amarela, e os objetos que ficarão atrás, indicados pela seta verde. Isso é causado pela alteração do parâmetro Plane Distance do Canvas para o valor 5. Nossa câmera (Main Camera) já tem valor de profundidade (Z) de Position igual a -10; ou seja, o Canvas ficará entre o conteúdo do cenário e a câmera.

Agora, vamos começar a inserir os elementos que irão compor o fundo da tela. Selecione o GameObject Fundo, subordinado ao objeto Cenario, na aba Hierarchy. Os valores de Position deverão ser X = 0, Y = 2.5 e Z = 18, conforme a imagem a seguir ilustra:

Clique com o botão direito do mouse sobre o objeto Fundo e selecione Create Empty; o nome do objeto criado será Parte1. Repita a operação, criando um GameObject de nome Parte2.

Selecione Parte1 e Parte2 e adicione componentes do tipo Sprite Renderer a eles. Ambos terão como valor de Sprite o desenho de nome “fundoCeuAzul” (sem as aspas), assim como demonstrado a seguir:

Selecione apenas Parte2 e altere o parâmetro X de Position (componente Transform) para 58. Já para Parte1, mantenha o parâmetro X com o valor original (zero). Note que temos agora duas imagens uma ao lado da outra.

Movimentando o fundo

Nós dividimos o fundo do cenário em duas partes para implementarmos uma funcionalidade que fará parecer que o cenário não tem fim, por meio de uma movimentação constante e modificação da posição das duas partes.

Basicamente, o que iremos fazer é o seguinte: as duas partes se movimentam na direção oposta à tecla pressionada pelo usuário (para direita, se pressionou “seta esquerda” e vice versa). Quando uma das partes já está suficientemente longe do alcance da visão da câmera, iremos ordenar seu deslocamento instantâneo para a outra extremidade da tela, do outro lado de sua imagem “vizinha”.

Para tal, vamos elaborar um script que irá controlar essa movimentação. Na aba Project, vá até a pasta Assets e clique duas vezes sobre a pasta Scripts. Clique com o botão direito em uma área vazia, selecione a opção Create e, por fim, C# Script. O nome de nosso novo script será MovFundo.


Clique duas vezes sobre o ícone do script MovFundo e vamos começar a editá-lo no Visual Studio.

Logo acima dos métodos padrão (void Start() e void Update()) insira o seguinte trecho de código:

    public float velocidade, limiteMin, limiteMax;

Essas três variáveis irão determinar a velocidade de deslocamento das imagens de fundo da tela e os limites mínimo e máximo para as imagens saírem de uma extremidade e irem para o outro lado, quando aplicável.


Agora, iremos apagar o conteúdo de void Start(), deixando apenas o que acabamos de inserir e o void Update(). Iremos elaborar agora um código que irá fazer o seguinte:
  • Determinar se a imagem de fundo deverá se deslocar para a direita ou para a esquerda, dependendo do botão pressionado pelo jogador;
  • Modificar o posicionamento da imagem; e
  • “Teletransportar” a imagem para a outra extremidade da tela, caso tenha passado um dos limites determinados.
As teclas que iremos detectar para o deslocamento são a seta para a direita (ou tecla D) e a seta para esquerda (ou tecla A).

Insira então o código a seguir dentro das chaves de void Update():

        // Determinará a movimentação para a direita ou para a esquerda
        float direcao = 0;

        if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
            direcao = 1;
        else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
            direcao = -1;

        // Modifica o posicionamento da imagem do fundo
        transform.position = new Vector3(transform.position.x + (velocidade * direcao * Time.deltaTime * 60),transform.position.y, transform.position.z);

        // Caso passe dos limites laterais, a imagem será deslocada para a outra extremidade
        if (transform.position.x < limiteMin)
            transform.position = new Vector3(limiteMax + (velocidade * direcao * Time.deltaTime * 60),transform.position.y,transform.position.z);
        else if (transform.position.x > limiteMax)
            transform.position = new Vector3(limiteMin + (velocidade * direcao * Time.deltaTime * 60),transform.position.y,transform.position.z);

Salve o script, minimize o Visual Studio e volte ao Unity. Selecione simultaneamente Parte1 e Parte2 e adicione o componente Mov Fundo a ambos (botão Add Component da aba Inspector).

Os valores a serem indicados para os componentes Mov Fundo de ambos GameObjects são estes: Velocidade = 0.5, Limite Min = -58 e Limite Max = 58, conforme ilustração a seguir:

Feito isso, experimente a execução do jogo indo à aba Game e clicando no botão Play. Veja que, ao apertar uma das teclas para direita ou esquerda, o fundo de nossa fase parece não ter fim, o que é muito interessante!

Para fins de curiosidade, veja o que ocorre “por detrás dos panos” na aba Scene durante a execução. Interessante, não?

Finalize a simulação da execução, pressionando novamente o botão Play. Não se esqueça de salvar a cena (menu File, Save) e o projeto (menu File, Save Project).

Próximos passos

Enfim, começamos a povoar a cena da primeira fase de nosso projeto, além de aprendermos um pouco mais sobre outros métodos para exibir imagens 2D diferentes do que utilizamos até então.

Em nossos próximos tópicos, iremos continuar a inserir os elementos que irão compor o núcleo de nosso game e, também, iniciar a elaboração da parte “física” de interação entre os elementos e o personagem, utilizando Colliders, Triggers, Rigidbodys e funções de deslocamento utilizando física.

Nosso próximo texto já encontra-se disponível, continue conosco nessa jornada de conhecimento e fique ligado sempre aqui no GameBlast!

Revisão: Thais Santos

Entendo videogames como sendo uma expressão de arte e lazer e, também, como uma impactante ferramenta de educação. No momento, doutorando em Sistemas da Informação pela EACH-USP, desenvolvendo jogos e sistemas desde 2020. Se quiser bater um papo comigo, nas redes sociais procure por @RodrigoGPontes.
Este texto não representa a opinião do GameBlast. Somos uma comunidade de gamers aberta às visões e experiências de cada autor. Escrevemos sob a licença Creative Commons BY-SA 3.0 - você pode usar e compartilhar este conteúdo desde que credite o autor e veículo original.