Artigo original: The Ultimate Beginners Guide To Game Development In Unity

Tradução em português europeu

O Unity é uma ótima ferramenta para criar protótipos de tudo, desde jogos até visualizações interativas. Neste artigo, analisamos tudo o que precisas saber para começar a utilizar o Unity.

Primeiro, um pouco sobre mim: sou um programador amador em Unity, modelador 3D e designer gráfico, que já trabalho com Unity e Blender há mais de 5 anos. Agora, sou aluno de Matemática Financeira na Universidade College Dublin e, ocasionalmente, faço design gráfico, protótipos para a web e protótipos de jogos como freelance.

Q-bD3OdyDZX2X2cmqgyyRIFwRpy9mngdYAtC
Arte conceptual é uma das fases iniciais no processo de desenvolvimento de games. Nos últimos anos, tive muita exposição a todas as áreas do desenvolvimento de jogos. Consulta o meu portfólio de gráficos, UX, arte conceptual, desenvolvimento de jogos e outros…

Introdução

Este artigo é para aqueles que nunca utilizaram o Unity, mas que tenham alguma experiência em programação ou design/desenvolvimento para a web. No final deste artigo, deves ter uma boa compreensão geral do motor (em inglês, engine), assim como de todas as funções e código necessários para começar a criar um jogo básico.

Por que o Unity?

Se quiseres criar jogos

Existem apenas algumas opções quando falamos de desenvolvimento de jogos independentes. As três escolhas principais, se quiseres desenvolver jogos, são o Unreal, o Unity ou o GameMaker.

O Unity é provavelmente a opção menos restritiva entre as 3 plataformas. Este fornece-te um produto muito cru logo à partida, mas é muito flexível, bem documentado e altamente extensível para criar basicamente qualquer género de jogo que possas imaginar.

Existem bastantes jogos muito bem-sucedidos, como o Escape from Tarkov (Atirador), Monument Valley (Puzzle) e o This War of Mine (Estratégia/Sobrevivência), todos criados com o Unity.

Na realidade, o motor em que crias o teu primeiro jogo provavelmente não é muito importante. Por isso, o meu conselho é que escolhas um e sigas com esse.

Se quiseres criar protótipos de experiências de utilizador

Visto que o Unity é apenas um motor com algumas físicas, animações e renderização 3D em tempo real, também é um ótimo espaço para criar protótipos completos e interativos para estudos de Experiências de Utilizador (UX – do inglês, User Experience).

O Unity tem suporte total para Realidade Virtual e Realidade Aumentada. Ele pode, então, ser uma ótima ferramenta para explorar arquitetura, automatizações e simulações com os clientes.

Secções deste artigo

  • Por que o Unity?
  • Janela de edição do Unity
  • Objetos de jogo do Unity
  • Componentes incorporados do Unity
  • Criar componentes personalizados
  • Estrutura de MonoComportamento
  • Manipular GameObjects
  • Raycasting
  • Deteção de colisão
  • Características avançadas
  • Conselhos para iniciantes
  • Bons recursos e comunidades
  • Conclusão

Janela de edição do Unity

A janela de edição é dividida em várias secções. Vamos abordar isto muito brevemente porque vamos referir esta janela constantemente ao longo deste artigo. Se já te sentires à vontade com isto, podes passar esta parte à frente!

Vista da cena: permite a colocação e o movimento de GameObjects na cena

Vista do jogo: permite visualizar como o jogador verá a cena a partir da câmara

Inspetor: fornece detalhes sobre o GameObject selecionado na cena

Recursos e projeto: todos os pré-fabricados, texturas, modelos, scripts e outros estão armazenados aqui

Hierarquia: permite colocar e estruturar GameObjects dentro da cena

Agora, estamos prontos para começar!

Objetos de jogo do Unity

O que são os GameObjects

Os GameObjects são o bloco de construção principal para tudo no motor de jogos Unity. O nome quase que revela tudo:

Tudo o que colocares dentro de uma cena no Unity deve estar envolvida por um "game object" (objeto de jogo).

Se tiveres experiência em design para a web, podes pensar em GameObjects muito como elementos <div>! Recipientes extremamente aborrecidos, mas altamente extensíveis, para criar funcionalidades ou visuais complexos.

vlXNfFyr4lsQgC-DL05daoOtSWT35ZFUcA1l
Tirei isto diretamente da janela de edição do Unity só para provar este ponto.

Praticamente tudo, desde efeitos de partículas, câmaras, jogadores, elementos de Interface de Utilizador, … (a lista continua) é um GameObject.

Criar hierarquia

Tal com uma <div> em desenvolvimento para a web, um GameObject também é um recipiente. Tal como colocas <div>s umas dentro de outras para criar layouts variados e desejáveis ou abstrações, podes querer fazer o mesmo com os objetos de jogo.

A lógica por trás da colocação de game objects uns dentro de outros é muito parecida com o que é feito em desenvolvimento para a web, vou fornecer alguns exemplos…

Desorganização e eficiência

Analogia com a web: tens muitos elementos semelhantes que podem ser gerados dinamicamente em tempo real em resposta a interações de utilizadores e desejam manter as coisas organizadas.
Tradução do Unity: estás a criar uma cópia do Minecraft e tens muitos blocos na cena, então precisas de adicionar e remover 'pedaços' de blocos da cena por questões de desempenho. Então, faz sentido ter cada pedaço associado a um GameObject pai vazio, visto que remover o pedaço pai remove todos os blocos filhos.

Posicionamento

Analogia com a web: queres manter a posição do conteúdo contido 'relativo' ao recipiente e não à página da web.
Tradução do Unity: criaste um conjunto de drones ajudantes que pairam à volta do jogador. Preferias claramente não escrever código para indicar aos drones para seguir o jogador. Então, em vez disso, instancias os drones como filhos do game object do jogador.

Componentes incorporados do Unity

O Modelo de componente do ator

Os GameObjects por si só são bastante inúteis — tal como vimos, são praticamente apenas recipientes. De maneira a fornecer funcionalidade, temos de adicionar componentes, que são essencialmente scripts escritos em C# ou em Javascript.

O Unity funciona com base num modelo de componente de ator – simplificando, os GameObjects são os atores e os componentes são os teus scripts.

Se já tiveres alguma vez criado uma aplicação para a web, estarás à vontade com a ideia de criar pequenos componentes reutilizáveis, tais como botões, elementos de formulário, layouts flexíveis que têm várias diretrizes diferentes e propriedades personalizáveis. Depois, podes montar estes pequenos componentes em páginas da web maiores.

A grande vantagem dessa abordagem é o nível de reutilização e canais de comunicação claramente definidos entre elementos. Tal como em desenvolvimento de jogos, queremos minimizar o risco de efeitos secundários indesejados. Pequenos erros tendem a ficar fora do controlo se não tivermos cuidado e são extremamente difíceis de depurar. Por isso, criar componentes pequenos, robustos e reutilizáveis é crucial.

Componentes chave incorporados

Acho que está na hora de mostrar alguns exemplos dos componentes incorporados fornecidos pelo motor de jogos Unity.

  • MeshFilter: permite-te atribuir materiais a uma malha (em inglês, mesh) 3D para um GameObject
  • MeshRender: permite-te atribuir materiais a uma malha 3D
  • [Box | Mesh]Collider: ativa a deteção do GameObject durante colisões
  • Rigidbody: aciona uma simulação física realista para ser aplicada num GameObjects com malhas 3d e aciona eventos de deteção em colisões de caixas
  • Light: ilumina partes da tua cena
  • Camera: define a vista do jogador para ser anexada a um GameObject
  • Vários componentes de tela da Interface de Utilizador para exibir Interfaces Gráficas de Utilizador

Existem muitos mais, mas estes são os principais que precisas conhecer. Uma dica é que podes aceder a todos os documentos através do manual do Unity e na referência de scripting off-line, onde quer que estejas:

CNB5Rb4DWImRiHh04xThXYtZnojyCj5OJJ1f
Apenas clica na secção help, os documentos por norma são bastante bons

Criar componentes personalizados

Os componentes incorporados controlam primeiramente a física e o visual, mas, para realmente criar um jogo, vais precisar de aceitar inputs do utilizador e manipular esses componentes padrão assim como os próprios GameObjects.

Para começar a criar componentes, vai até ao GameObject desejado > Add Component (Adicionar componente) > escreve o nome do teu novo componente na barra de pesquisa > new script (novo script) (C#).

Como recomendação geral, eu desaconselho utilizar Javascript no Unity. Não foi mantido atualizado com todas as coisas boas que vieram com o ES6 e a maioria das coisas mais avançadas depende de coisas em C# transferidas para Javascript… Por experiência própria, torna-se simplesmente numa solução alternativa gigantesca.

Estrutura de um MonoComportamento

Funções-chave

Todos os componentes herdam da classe do MonoComportamento. Ela inclui vários métodos predefinidos, principalmente:

  • void Start(), que é chamado quando um objeto que contém o script é instanciado na cena. Isso é útil sempre que queremos executar algum código de inicialização (por exemplo, definir o equipamento de um jogador após este ser colocado num jogo).
  • void Update(), que é chamado a cada frame. É aqui que fica a parte do código que envolve o input do utilizador, atualizando várias propriedades como o movimento de um jogador na cena.

Variáveis do inspetor

Por vezes, queremos tornar os componentes o mais flexíveis possível. Por exemplo, todas as armas podem ter um dano diferente, cadência de tiro, se tem visão etc. Embora todas as armas sejam essencialmente a mesma coisa, podemos querer ser capazes de criar rapidamente diferentes variedades no editor do Unity.

Outro exemplo onde podemos querer fazer isso é quando criamos um componente da Interface de Utilizador, que acompanha os movimentos do rato do utilizador e coloca o cursor na janela. Aqui, podemos querer controlar a sensibilidade dos movimentos do cursor (se o utilizador está a utilizar um joystick ou controlador x um rato de computador). Então, faria sentido ter essa variável fácil de alterar, tanto no modo de edição como durante o tempo de execução.

ARiWcy5AEsVyRicp7demoZnzQR0MjPQnFzXJ
Variáveis na janela de inspeção pode ser alteradas a qualquer momento durante a execução ou no modo de edição. Observação: alterações feitas durante a execução não serão permanentes.

Podemos fazer isso facilmente ao declará-las como variáveis públicas no corpo do componente.

dMqFuop796E9p2Y4urYkJtsuM3Rh6oM07cIJ
Observa como podemos fazer com que as variáveis tenham diferentes níveis de acesso. Privadas, públicas, ou públicas mas não visíveis na janela de inspeção.

Aceitar inputs de utilizador

Como é óbvio, queremos que o nosso jogo responda a inputs de utilizador. Uma das maneiras mais comuns de se fazer isso é utilizar os seguintes métodos na função Update() de um componente (ou noutro local que queiras):

  • Input.GetKey(KeyCode.W): retorna verdadeiro quando a tecla W está a ser pressionada
  • Input.GetKeyDown(KeyCode.W): retorna verdadeiro quando a tecla W é pressionada pela primeira vez
  • Input.GetAxis("Vertical"), Input.GetAxis("Horizontal") Retorna entre -1,1 a partir do movimento de input do rato

Manipular GameObjects

Assim que recebermos inputs do utilizador, vamos querer que os GameObjects na nossa cena respondam. Existem vários tipos de respostas que podemos considerar:

  • Translação, rotação, escala
  • Criar GameObjects
  • Enviar mensagens a GameObjects/componentes existentes

Transformações

Todos os GameObjects têm uma propriedade transform, que permite que várias manipulações úteis sejam realizadas no game object atual.

cqutjhXkSZxKNCywMSjGbxewqeDo5GCp0R-d

Os métodos são bastante auto-explicativos, basta observar que utilizamos  gameObject em minúsculas para nos referirmos ao GameObject que detém essa instância em específico do componente.

Por norma, é boa prática utilizar local[Position,Rotation] em vez da posição/rotação global de um objeto. Isso, geralmente, torna mais fácil mover objetos num modo que faça sentido, pois o eixo do espaço local estará orientado e centrado no objeto pai e não na origem do mundo e direções x, y e z.

sgr4nHfYLQYqjEEPSxlxPA0BhCrNjgzzvOmW
Os benefícios do espaço local ficam um pouco mais óbvios com um diagrama!

Caso precises de fazer a conversão entre espaço local ou global (que é geralmente o caso), podes utilizar o seguinte:

vNEw9xUc-B2vZOaeAqXcQn5W-lxfmJbLiEZw

Como podes imaginar, existe alguma álgebra linear bastante simples atrás disso, indicada pelo 'Inverse' no nome do método.

Criar GameObjects

Visto que os GameObjects são basicamente tudo na tua cena, podes querer ser capaz de gerá-los na hora. Por exemplo, se o teu jogador tiver um género de um lançador de projéteis, podes querer ser capaz de criar projéteis na hora, que vão ter a sua própria lógica encapsulada para voar, efetuar dano etc…

Primeiro, precisamos de introduzir a noção de um Prefab. Podemos criar esses ao arrastar qualquer GameObject na hierarquia da cena para a pasta dos assets.

BEBGqpePGsVgtEY5y89WrnLAbdZjPHZiagjZ
O aspeto de um prefab no separador dos assets

Isso, basicamente, armazena um template do objeto que tínhamos na cena, com todas as mesmas configurações.

wFLWTKOYgxfMEAEzUjgMsTCETSmTA3JIPLGp
Um exemplo de um objeto de tijolo personalizado, que é utilizado para gerar dinamicamente tijolos de Lego numa cena. Tem um conjunto de componentes anexados a ele com vários valores predefinidos.

Assim que tivermos estes componentes prefab, podemos atribuí-los a variáveis de inspeção (como abordamos anteriormente) em qualquer componente na cena. Então, podemos criar GameObjects como especificado pelo prefab a qualquer altura.

Podemos realizar 'Instanciação' do prefab e manipulá-lo para a localização desejada na cena e estabelecer as relações hierárquicas necessárias.

xAzUlbgEAIkyS8bsX8W0xlVI0YiSTiVyMljj

Aceder a outros GameObjects e componentes

Por vezes, precisamos de comunicar com outros GameObjects assim como os seus componentes associados. Assim que tiveres uma referência a um game object, isso fica muito simples.

ComponentName comp = some_game_object.GetComponent<ComponentName>();

Depois disso, podes aceder a quaisquer métodos/variáveis públicos do componente de maneira a manipular o GameObject. Essa é a parte mais direta, no entanto. Obter realmente a referência para o GameObject pode ser feito de várias formas diferentes…

Aceder através de variável de inspeção

Esta é a maneira mais direta. Cria simplesmente uma variável pública para o GameObject, tal como demonstramos anteriormente com os prefabs, e arrasta-a manualmente para o componente através do inspetor. De seguida, acede à variável tal como acima.

Aceder através de tags

Podemos atribuir tags a GameObjects ou prefabs através do inspetor e depois utilizar as funções find do game object para localizar as referências para eles.

9Ur13zYuVV3r17CGo9hDMnyCoG44jTXCwv4g

Isso é simplesmente feito como abaixo.

GameObject some_game_object = GameObject.FindGameObjectWithTag(“Brick”);

Aceder através do atributo transform

Se quisermos aceder a componentes em algum objeto pai, podemos simplesmente fazer isto através do atributo transform.

ComponentName comp = gameObject.transform.parent.GetComponent<ComponentName>();

Aceder através de funções SendMessage

Como alternativa, se quisermos enviar uma mensagem a muitos outros componentes ou desejarmos enviar uma mensagem a um objeto que está bastante acima na hierarquia, podemos utilizar as funções de envio de mensagem, que aceitam o nome da função, seguido pelos argumentos.

gameObject.SendMessage("MethodName",params); // Mensagem de Broadcast
gameObject.SendMessageUpwards("MethodName", params); // Apenas recebido por componentes que estão hierarquicamente acima.

Raycasting

Podes ter ouvido falar disso antes, quando as pessoas comparam jogos de FPS que são 'baseados em físicas' ou 'baseados em raios'. Raycasting é essencialmente como ter um laser que, quando entra em contacto com um 'collider' ou 'rigidbody', retorna um 'hit' e passa de volta os detalhes do objeto.

Existem dois cenários onde isto é útil (existem provavelmente muitos mais):

  1. Se estivesses a desenhar um sistema de armas para um jogo, poderias utilizar raycasting para detetar a colisão e até mesmo personalizar o comprimento do raio de modo a que as armas corpo-a-corpo 'atinjam' apenas a curtas distâncias.
  2. Criar um raio desde o ponteiro do rato até um ponto num espaço 3D, ou seja, se desejares que o utilizador seja capaz de selecionar unidades com o seu rato num jogo de estratégia.
GcxZnE2hbosbWwoDp94ecEd1g8aVlwrFKOhB
Exemplo 2 detalhado acima

Como podes ver, o código para isso é um pouco mais complicado. A coisa principal a compreender é que, para criar um raio para onde o rato está a apontar num espaço 3D, é necessária a transformação ScreenPointToRay. A razão para isso é que a câmara está a renderizar um espaço 3D como uma janela 2D no ecrã do teu computador. Então, existe naturalmente uma projeção envolvida para transferir de volta para 3D.

Deteção de colisão

Anteriormente, mencionamos os componentes Collider e Rigidbody, que podem ser adicionados a um objeto. A regra para colisões é que um objeto na colisão deve ter um rigidbody e o outro deve ter um collider (ou ambos terem os dois componentes). Repara que, ao utilizar raycasting, os raios vão interagir apenas com objetos com componentes collider anexados.

Assim que fizermos a configuração dentro de qualquer componente personalizado anexado ao objeto, podemos utilizar os métodos OnCollisionEnter, OnCollisionStay e OnCollisionExit para responder a colisões. Assim que tivermos a informação de colisão, podemos obter o GameObject responsável e utilizar o que aprendemos anteriormente para interagir também com componentes anexados a ele.

ppKZgvdAjKDqbW80Nin2wG9izk08UjyuaqAt

Uma coisa a ter em conta é que os rigid-bodies fornecem físicas como gravidade aos objetos. Então, se desejares desligar isto, vais precisar de marcar o is_kinematic.

I9UU3oy-UoVWOwjhXUy9PRiGDT5eFO4O9-IB
Marca a caixa is kinematic para desativar físicas não desejadas mas manter uma boa deteção de colisão.

Características avançadas

Não vamos abordar nada disto para já, mas talvez num artigo futuro — isso é apenas para ficares a par da sua existência.

Criar interfaces gráficas de utilizador (GUI)

O Unity tem um motor de UI completo para definir a GUI do teu jogo. Por norma, esses componentes funcionam de maneira muito semelhante ao resto do motor.

Estender o editor do Unity

O Unity permite-te adicionar botões personalizados aos teus inspetores para que possas afetar o mundo no modo de edição. Por exemplo, para ajudar com a construção do mundo, podes desenvolver uma janela personalizada de ferramentas para criar casas modulares.

Animação

O Unity tem um sistema de animação baseado em gráficos que te permite juntar e controlar animações em vários objetos, tais como jogadores a implementar um sistema de animação com base nos ossos.

Materiais e PBR

O Unity funciona num motor de renderização com base em físicas que permite ter luzes em tempo real e materiais realistas. A realidade é que vais aprender primeiro modelação 3D ou vais utilizar modelos já feitos e otimizados por alguém, de modo a criares algo que realmente tenha bom aspeto.

Dicas para principiantes

Se estiveres a planear criar o teu primeiro jogo, não subestimes a complexidade e o tempo que leva a escrever até o mais trivial dos jogos. Lembra-te que a maior parte dos jogos lançados na Steam têm equipas a trabalhar neles durante anos a tempo inteiro!

Escolhe um conceito simples e divide-o em pequenos objetivos alcançáveis. É altamente recomendado dividir o teu jogo em tantos componentes independentes quanto possível, pois é muito menos provável encontrares erros se mantiveres os componentes simples em vez de blocos de código monolíticos.

Antes de avançares para a escrita de qualquer código para qualquer parte do teu jogo, pesquisa o que outras pessoas fizeram antes para resolver o mesmo problema — é provável que tenham uma solução bastante mais otimizada.

Bons recursos e comunidades

O desenvolvimento de jogos tem uma das melhores comunidades de todas. Existem muitos profissionais altamente habilitados na indústria, que fornecem conteúdo gratuitamente ou por quase nada. É uma área que requer modeladores 3D, artistas conceptuais, designers de jogos, programadores e assim adiante. Abaixo, coloquei os links para alguns recursos gerais ótimos (em inglês) que encontrei para cada um desses campos:

Arte Conceptual

  • Feng Zhu Design School (mais de 90 horas de tutoriais de arte conceptual)
  • Tyler Edlin Art (ótima comunidade de arte BST com feedback de profissionais em desafios mensais)
  • Art Cafe (entrevistas e workshops com artistas conceptuais famosos)
  • Trent Kaniuga (ilustrador e artista 2D que também está a criar o seu próprio jogo)

Modelação 3D

  • CG Cookie (melhores princípios básicos de malhas 3D no Blender – eles têm muitos outros excelentes conteúdos para o Blender)
  • Tor Frick (modeladores de Hard Surface e escultores no Blender)
  • Gleb Alexandrov (pequenos tutoriais potentes sobre renderização no Blender)

Desenvolvimento de jogos

Programação

Conclusão

Espero que tenhas gostado deste tutorial! Também faço um pouco de design gráfico, assim como protótipos de jogos e Interfaces de Utilizador.

Se o tutorial foi do teu agrado, dá uma vista de olhos ao portfólio do autor! Ele também está presente no LinkedIn.