Apostila Desenv Jogos Digitais PDF
Apostila Desenv Jogos Digitais PDF
Apostila Desenv Jogos Digitais PDF
Mobile
Um jogo digital refere-se a qualquer jogo eletrônico projetado para ser utilizado em
um computador, console ou dispositivo móvel, entre outras plataformas eletrônicas.
Os jogos digitais fazem parte da cultura de massa desde a popularização dos consoles
de videogames, como o Atari Video Computer Systems (Atari VCS). Lançado em 1977,
o console que seria mais tarde chamado de Atari 2600, chegou a vender 8 milhões de
unidades até 1983.
Um jogo digital é uma atividade lúdica, composta por uma série de ações e decisões,
limitada por regras e pelo universo do jogo, dentro de um ambiente eletrônico próprio,
interativo e de fácil utilização.
A combinação dessas regras com objetivos específicos de cada jogo é o que caracteriza
o desafio, a emoção e a diversão ao jogador em sua jornada pelo jogo.
BECTA (2003), que delineou uma classificação que tenta englobar os vários gêneros
de jogos digitais existentes, de acordo com os estilos, narrativas, temáticas e
atividades.
GRAELLS (2000), que estabelece uma tipologia para jogos digitais levando em conta
a estrutura dos jogos e as principais competências mobilizadas pelo jogador no
DESENVOLVIMENTO DE JOGOS MOBILE 2
desenrolar do jogo, nomeadamente a psicomotricidade, o raciocínio, a lógica, a
estratégia e a memória.
Esse documento define todos os pontos de um jogo digital e guia todas as equipes
envolvidas no processo de produção, e o profissional responsável pelo GDD e por todo
o projeto de um jogo é chamado de Game Designer.
SONORIZAÇÃO - músicas nos menus e em cada fase, bem como efeitos sonoros em
geral.
Alguns jogos são criados utilizando apenas elementos 2D, mesmo que com efeitos de
sombra, simulando efeitos 3D, o que tem sido muito comum nos jogos de plataforma.
A modelagem dos elementos 3D pode ser feita com ferramentas diversas, a exemplo
do Blender, 3DS Max, Maya, entre várias outras, e o processo inicia-se com a
construção de uma malha, sobre a qual serão dispostos materiais e texturas,
utilizando-se ao final elementos de iluminação sobre o conjunto.
Game Engine
Motor de jogo, também conhecido pelo termo em inglês Game Engine, é um
programa de computador utilizado para simplificar e abstrair o desenvolvimento de
jogos eletrônicos ou outras aplicações com gráficos em tempo real.
A unidade básica de trabalho na Unity 3D são os Assets, e pode-se obter muitos deles,
inclusive podendo ser gratuitos, a partir da Game Engine, pelo Asset Store.
Essa engine traz de um ambiente completo e bastante intuitivo para a criação de jogos,
aceitando também diversos formatos para importação de outras ferramentas, como o
3DS Max e o Blender, além de apresentar muitos tutoriais no site do fornecedor
(https://unity3d.com/pt).
Ela pode ser baixada e utilizada livremente, porém ressarcindo ao seu fabricante sob
a forma de royalties a partir de determinada quantidade de cópias do jogo
comercializadas.
Em termos de jogos 3D, as representações mais comuns para o jogador são a primeira
e a terceira pessoa, e a Unity 3D fornece Assets prontos para os dois modos, devendo
apenas ser acrescentado o pacote correto ao projeto e utilizado o controlador
adequado.
Tão simples quanto trabalhar com a primeira pessoa é trabalhar na terceira pessoa.
Certamente a definição de um personagem em terceira pessoa exigirá alguma
modelagem 3D, mas ThirdPersonController já está disponível no pacote
Unity traz boas ferramentas para a criação da interface de usuário, aqui se tratando
de menus e avisos, entre outros elementos de interação e HUD. Os componentes para
construção de GUI incluem: Painéis, Botões, Textos e Imagens, Campos de Entrada
e Sistema de Eventos.
Linguagem C#
Foi desenvolvida pela Microsoft como parte da plataforma dotNET, e como nas demais
linguagens dessa plataforma, o código fonte é compilado para Common Intermediate
Language (CIL) que é interpretado pela máquina virtual do dotNET, a Common
Language Runtime (CLR).
C1 C2 C1 && C2 C1 || C2
b1 b2 b1 & b2 b1 | b2 b1 ^b2
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
int a, b, c;
// Operações diversas sobre as variáveis...
if ( ( a > b ) && ( a > c ) )
Console.WriteLine ("A tem o maior valor");
else
if ( b > c )
Console.WriteLine("B tem o maior valor");
else
Console.WriteLine("C tem o maior valor");
if ( a > b )
c = 2;
else c = ( a > b ) ? 2 : 5;
c = 5;
int a = 1;
int a = 1;
do {
Console.WriteLine( a );
a = a * 2;
} while ( a <= 256 );
A conversão de tipo, ou type cast, faz com que o C# reinterprete um determinado tipo
em situações específicas. Essa é uma técnica muito utilizada na orientação a objetos
quando precisamos acessar um atributo específico de um descendente utilizado no
lugar da classe inicial.
float f;
int a = 5, b = 2;
f = a / b; // f recebe 2.0
f = a / (float) b; // f recebe 2.5
Na sintaxe C#, uma classe é definida pela palavra class, devendo ser observado o
namespace utilizado. Para a definição de seus atributos e métodos, os níveis de
acesso disponíveis são: public, private e protected.
Exemplo( 123 );
Exemplo( nome: "Ana", id: 352);
Exemplo( 478, "Carlos" );
[WebService(Namespace= "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service () { }
[WebMethod]
public string HelloWorld() {
return "Hello World";
}
}
Um jogo é como uma animação, mas onde os quadros de animação são gerados a
cada instante, e uma técnica fundamental na sua programação é efetuar mudanças
para a posição, comportamento e estado de objetos antes do processamento de cada
quadro.
O método Update é o lugar correto para esse tipo de código na Unity, pois é chamado
antes do processamento da imagem e antes que as animações sejam calculadas.
Enfim, a matemática está em todo lugar, nas mais diversas áreas de programação,
mas nos jogos ela se tornará ainda mais relevante. Como a maior parte dos jogos
envolve elementos gráficos, o uso de geometria descritiva e álgebra linear
tornam-se evidentes na construção dos jogos.
Por exemplo, um vetor 2D definido pelo ponto (x,y) = (3,4), em relação à origem,
apresenta magnitude 5 e direção de 53°. Para o cálculo da magnitude, basta utilizar
o Teorema de Pitágoras, e para a direção pode ser utilizado o arco-tangente, arco-
seno ou arco-cosseno, considerando o eixo x como a linha do cosseno e o eixo y como
a linha do seno.
arctan( 4 / 3 ) = 53,13°
Unity permite representar vetores 2D, 3D e 4D com grande facilidade, e as classes que
representam os vetores ainda trazem métodos específicos para normalização,
magnitude etc.
[ ax , ay ] k = k [ ax, ay ] = [ k * ax , k * ay ]
[ ax , ay ] + [ bx , by ] = [ ax + bx , ay + by ]
Matrizes também são de grande interesse para a área de jogos e computação gráfica
em geral, tratando de tabelas com linhas e colunas de dados.
Uma matriz quadrada é aquela que tem a mesma dimensão para linhas e colunas,
e os elementos diagonais são aqueles com mesmo índice de linha e coluna. Em geral,
uma matriz de transformação M quadrada pode descrever qualquer transformação
linear.
Para o caso de duas dimensões é bastante simples, pois a rotação ocorre apenas em
torno da origem, mas, ao trabalhar com 3D, será necessário considerar três matrizes
de transformação, Rx, Ry e Rz, representando rotações em torno dos eixos x, y e z,
respectivamente.
A maior parte dos jogos explora de uma forma mais intensa a álgebra linear, devido à
parte gráfica envolvida, o que não significa que outras áreas da matemática se tornem
desnecessárias, como no caso do uso de probabilidade e estatística em "jogos de azar".
Esses são apenas alguns exemplos, mas ilustram um pouco a importância de definir
técnicas para identificar o momento e o espaço onde ocorre o contato (colisão) entre
dois ou mais elementos do jogo.
Essa técnica apresenta uma performance muito boa devido ao baixo números de
contas e de verificações, mas, como ela testa colisões entre retângulos, em figuras
muito curvas e disformes pode não apresentar um resultado satisfatório.
Por exemplo, se um corpo está apoiado em uma superfície sem inclinação e com atrito,
e sob a ação da gravidade, ele estará inicialmente com velocidade zero, mas recebendo
uma força horizontal capaz de quebrar a resistência desse atrito irá iniciar uma
aceleração, modificando sua velocidade original.
Materiais distintos podem ser mais ásperos ou mais lisos, apresentar diferentes
densidades, além de outras características físico-químicas capazes de interferir
diretamente nos cálculos físicos.
Inicialmente a Unity 3D apresenta colisores 3D, pela própria natureza da Engine, sendo
mais utilizados os colisores Box Collider, Sphere Collider e Capsule Collider. Em
situações de maior detalhamento pode ser utilizado o Mesh Collider, e outro colisor
importante é aquele utilizado na criação de terrenos, o Terrain Collider.
Outro elemento interessante na física do Unity3D são as junções entre objetos, como
na simulação de dobradiças (Hinge Joint) e molas (Spring Joint). Da mesma forma
que existem junções 3D, existem as junções 2D, como Hinge Joint 2D.
Quanto aos corpos rígidos, a Unity 3D oferece suporte simples, pelo acréscimo
de um Rigidbody ou Rigidbody2D. Um exemplo típico de utilização da gravidade
Inteligência Artificial
Os dicionários consideram inteligência como a capacidade de aprender, compreender
e adaptar-se às situações que se apresentarem. Os modelos de Inteligência Artificial
(AI, do inglês Artificial Intelligence) são aqueles cuja programação visa simular
ações de inteligência similares às humanas, mas em meio computacional.
Uma Rede Neural Artificial (RNA) é composta por várias unidades de processamento,
cujo funcionamento é bastante simples. Essas unidades geralmente são conectadas
por canais de comunicação que estão associados a determinado peso.
As unidades fazem operações apenas sobre seus dados locais, que são entradas
recebidas pelas conexões. O comportamento inteligente de uma RNA vem das
interações entre as unidades de processamento da rede, cujas saídas funcionam como
entradas para outras unidades, assim como ocorre nos neurônios físicos.
Em jogos como Xadrez e Pôquer, por exemplo, as diversas heurísticas tomam como
base o conhecimento prévio de estratégias, análise estatística e árvores de decisão.
Para evitar essas situações, podem ser utilizadas heurísticas para a formação da
árvore, as quais não tratarão de todo o espectro combinatorial de um algoritmo guloso,
mas trarão resultados com níveis de qualidade e dificuldade adequados à garantia da
jogabilidade.
Para jogos que envolvem personagens animados, naves, tiros, e demais elementos
autônomos, é comum o uso de heurísticas de busca para a localização do jogador e
tomada de decisão acerca de movimentação, defesa e ataque.
Um sistema de regras para cada agente fará com que siga o caminho livre que mais o
aproxima do jogador, e estando em posição adequada, este pode entrar em modo de
combate, segundo a máquina de estados (FSM).
void Update ( ) {
NavMeshAgent agente = GetComponent<NavMeshAgent> ( );
agente.destination = alvo.position;
}
Geração Procedural
Geração Procedural em jogos corresponde à adoção de um conjunto de algoritmos,
utilizados como um ferramental para criar grandes quantidades de conteúdo em
arquivos de tamanhos mínimos.
Essa técnica não é nova, pois os jogos mais antigos, limitados à baixa capacidade
computacional e de pouco espaço de armazenamento da época, precisavam criar
elementos dinamicamente para melhor aproveitamento do hardware.
As formas mais comuns de geração procedural são o modelo matricial e uso de fractais.
Em jogos 2D de visão superior, os mapas podem ser obtidos como a combinação de
figuras menores de forma matricial, e alguns exemplos típicos disso são jogos de
corrida infinitos, jogos de campanha, bullet hell e RPG de visão superior.
Nos jogos de bullet hell o uso de aleatoriedade controlada pode trazer um desafio
maior para o jogador, pois ele não será capaz de prever movimentos inimigos.
Jogos como Minecraft e Spore utilizam a geração procedural para criar imensos
mundos 3D, permitindo aos jogadores experiências inéditas a cada novo cenário
gerado.
Esse código pode ser adaptado para gerar qualquer terreno 3D com grande facilidade,
mas um cuidado que deve ser tomado é o de não aplicar o script ao objeto a ser
replicado, pois traria ao jogo um problema de recursividade infinita.
Elementos mais independentes podem ser utilizados, por exemplo, a Main Camera,
artifício bastante utilizado em muitos jogos que precisam de algum tipo de
configuração inicial de ambiente. Poderia também ser criado um Game Object do tipo
Empty.
Isso pode gerar problemas com relação às sobreposições não tratadas, mas a
aleatoriedade poderia ser melhor explorada, por outro lado, na concessão de bônus,
vida extra, e demais elementos especiais.
Interface de Usuário
Uma interface pode ser considerada como uma fronteira entre dois sistemas sejam
esses eletrônicos, mecânicos ou orgânicos. Exemplos de interfaces de origem
eletrônica ou mecânica incluem conectores e barramentos, entre outros.
O hardware para entrada de dados, como mouse e teclado, constitui uma interface
física com o usuário. Partindo do âmbito físico para o lógico, ou software, este também
apresenta necessidade de interação com o usuário (sistema orgânico), por um
conjunto de componentes denominado interface de usuário.
O menu Game Object >> UI dará acesso à criação dos seguintes componentes
relacionados com a interface de usuário: Panel, Button, Text, Image, Raw Image,
Slider, ScrollBar, Toggle, Input Field, Canvas e Event System.
World Space faz com que a interface seja integrada ao espaço do ambiente 3D,
constituindo parte do cenário.
Uma forma simples de responder aos eventos é adicionar os ouvintes corretos através
do método AddListener. Um método callback deve ser criado para a resposta, e
utilizado como parâmetro de AddListener.
Para tal, é necessário adicionar atributos públicos de ligação com os game objects
necessários, além da implementação do callback responsável pela operação. Há
métodos visuais de configuração de eventos, mas eles acabam se tornando mais
complexos que a utilização de programação.
Podem ser criados novos elementos Panel, constituindo todo um sistema interativo de
janelas. Nesse contexto, uma propriedade muito interessante é o Alpha do Panel, a
qual permite a sobreposição de painéis com transparência, o que mantém a
informação do anterior, ao mesmo tempo em que utiliza um sofisticado efeito
translúcido.
A visibilidade de cada Panel pode ser controlada, e as telas podem ser exibidas
alternadamente nos momentos de utilização da interface. Para isso, basta habilitar ou
desabilitar o Panel, a partir da janela do Inspector ou via programação.
A forma mais simples de relacionar o Script ao segundo painel é com o uso de uma
propriedade GameObject, a qual permitirá informar o relacionamento com o objeto
do cenário a partir do Inspector.
Alguns jogos podem ser construídos apenas com o uso dos componentes UI,
principalmente os de tabuleiro como, por exemplo, o popular "Jogo da Velha".
void OnGUI() {
if (GUILayout.Button("Press Me"))
Debug.Log("Hello!");
}
HUD
Alguns exemplos da literatura traduzem a sigla HUD como Hands up Display, o que
não traz nenhum impacto real ao seu significado. A ideia básica é ter a informação
disponível na "altura dos olhos", ou no campo de visão, de forma simples e sem
atrapalhar a interatividade e visualização do ambiente.
Nos jogos, esse conceito é antigo, e não precisa chegar à complexidade alcançada por
esses exemplos. Com a utilização da Unity é bastante simples criar elementos HUD,
por componentes UI ou IMGUI.
Elementos como mapas, bússolas e radares são comuns em jogos do tipo FPS, como
Counter Strike. Como são elementos auxiliares, é comum a utilização de teclas e
controles específicos para ativar e desativar esses recursos por parte do jogador. Nesse
mesmo tipo de jogo é crucial também a apresentação da informação acerca de
munições e armas disponíveis para utilização.
Apenas para exemplificar o início da construção do HUD, pode ser utilizado um texto
simples em um projeto 2D. Esse componente pode ser programado de forma a exibir
a contagem regressiva de tempo, com a atualização do texto efetuada por
programação C#.
Embora não seja funcional para um jogo real, esse pequeno exemplo demonstra o
posicionamento e a programação de um elemento HUD. Exemplos mais complexos e
de maior qualidade gráfica podem ser observados nos diversos tutoriais do fabricante
da Unity 3D.
Ambiente Mobile
Um dispositivo móvel caracteriza-se como um "computador" de bolso, normalmente
operado por bateria, com uma tela de dimensões pequenas, além de um teclado ou
entrada pelo toque direto na tela (touchscreen).
Pode ser utilizado como objeto pessoal, ou com finalidade profissional, apresentando
como vantagens elementos como a mobilidade, a comunicação facilitada e a
versatilidade.
Apesar da categoria móvel englobar tablets, TVs inteligentes e diversos outros tipos
de aparelhos, SmartPhones foram o produto de melhor absorção pelo mercado.
Nessas plataformas, assim como nas demais plataformas móveis, os jogos se tornaram
um mercado de grande relevância, assumindo diversos modelos de negócios.
Embora seja possível utilizar o emulador do Android, apenas no aparelho físico é viável
o teste de todas as características do jogo. Para depurar o jogo por um SmartPhone
DESENVOLVIMENTO DE JOGOS MOBILE 66
Android, é necessário habilitar a opção de depuração USB, dentro de opções de
desenvolvedor.
A configuração do ambiente iOS na Unity 3D exige que seja feito o cadastro na Apple
Developer Program. Deve ser instalada a versão mais recente do XCode, a qual
pode ser obtida na Mac App Store.
Para associar o XCode ao Apple ID, devem ser seguidos alguns passos:
Esse projeto deve ser compilado no ambiente do Xcode para implantar e executar no
dispositivo escolhido. Uma vez que Unity foi usado para construir o projeto XCode é
possível efetuar a compilação e executar a partir da linha de comando.
Uma solução muito interessante para depurar o jogo criado na Unity é o aplicativo
Unity Remote, o qual foi projetado no intuito de ajudar no desenvolvimento para
Android ou iOS. O app deve ser baixado e se conecta com o Unity enquanto você está
executando o projeto no modo Play do editor.
A saída visual do editor é enviada para a tela do dispositivo e o input em tempo real é
enviado de volta para o projeto em execução no Unity. Isso permite uma boa
impressão do funcionamento do jogo, sem precisar de uma compilação completa para
cada teste.
Para utilizar essa ferramenta é necessário baixar o projeto Unity na Asset Store (requer
compilação), e o app na loja de seu dispositivo, iOS ou Android. Em seguida é
necessário conectar o dispositivo através da USB e efetuar a configuração correta da
Unity 3D, através da opção de menu Edit >> Project Settings >> Editor,
selecionando em seguida a seção Unity Remote, a qual permite escolher o tipo de
dispositivo, compressão utilizada e resolução de tela.
O primeiro componente que pode ser considerado na criação de jogos para dispositivos
móveis é a classe HandHeld. Essa classe permite controlar os aspectos mais globais
do jogo, como o uso de vibração, modo de tela cheia e indicador de atividade.
Vários jogos foram lançados com uso dessa tecnologia de sensores, posteriormente
substituída pelo giroscópio. Jogos denominados "runner" obtiveram grande sucesso e
até hoje conseguem manter um público fiel.
void Update() {
dir.x = -Input.acceleration.y;
dir.z = Input.acceleration.x;
dir *= Time.deltaTime;
transform.Translate(dir * 10.0F);
}
A aceleração do usuário também pode ser obtida com Gyroscope, com a propriedade
userAcceleration.
As telas de toque são um padrão adotado por qualquer tecnologia móvel da atualidade,
particularmente os SmartPhones. Na verdade, a grande maioria dos jogos trabalha
com o conceito de tela de toque, incluindo movimentos de arraste e até mesmo toques
múltiplos.
Não há como pensar em jogos para dispositivos móveis sem pensar em algum controle
da tela de toque, mesmo quando lidamos com acelerômetros e giroscópios.
void Update() {
float speed = 0.1F;
if (Input.touchCount > 0 &&
Input.GetTouch(0).phase == TouchPhase.Moved) {
// Pega o movimento do dedo desde o último quadro
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
transform.Translate(-touchDeltaPosition.x * speed,
-touchDeltaPosition.y * speed, 0);
}
}
Além do toque de tela simples, padrão adotado pelos diversos SmartPhones e Tablets,
alguns dispositivos oferecem a possibilidade de múltiplos toques simultâneos, os quais
possibilitam operações como "pinch", muito utilizadas no controle de zoom, por
exemplo.
É possível tratar também toques simultâneos na Unity 3D, desde que o dispositivo
suporte essa funcionalidade.
void Update ()
{
Touch[] myTouches = Input.touches;
for(int i = 0; i < Input.touchCount; i++)
{
// Cada toque do grupo pode ser manipulado aqui
}
}
if (Input.location.isEnabledByUser)
Input.location.Start();
Vários dados podem ser obtidos após ativar o serviço, sendo o acesso feito diretamente
a partir de Input.location.
Alguns dispositivos trazem uma bússola interna, a qual pode ser acessada por
Input.compass, objeto da classe Compass.
Outro elemento que deve ser desabilitado, se possível, são os mipmaps, os quais
tratam da mesma imagem em diferentes resoluções, técnica voltada para o aumento
da velocidade de rendering.
Além dos Assets, deve ocorrer uma preocupação também com relação ao
processamento e uso de memória pelos scripts, devendo sempre ser reutilizados os
objetos que forem possíveis, por exemplo. Várias técnicas podem ser exploradas nesse
contexto, mas levando sempre a um estudo acerca de algoritmos e uso de memória
com objetos e variáveis.
Jogo de Exemplo 1
Além dos recursos utilizados no tutorial original, será demonstrado como pode ser feito
o acréscimo de sons ao jogo.
Inicialmente deverá ser criado um projeto 2D, sem acréscimo de qualquer Asset
Package opcional. O nome adotado para o projeto do jogo de exemplo será
FlappyBirdStyle.
É comum trabalhar com várias camadas sobrepostas em modo 2D, e essas camadas
podem ser observadas mais facilmente chaveando para 3D. A alternância entre a
visualização 2D e 3D é obtida dcom o botão 2D, localizado na parte superior da Scene
View.
Jogos 2D são vistos em um plano, e não no espaço, como ocorre nos jogos 3D, e para
efetuar a sobreposição de elementos em um jogo 2D, o uso de camadas é um recurso
de grande relevância.
As operações de I/O são muito dispendiosas para qualquer dispositivo. Como os sprites
são constituídos de várias figuras, haveria um grande custo computacional para a
obtenção de arquivos individuais, e a solução é a criação de um arquivo denominado
Sprite Sheet, ou atlas, que reúne todas as figuras, reduzindo o número de operações.
Qualquer imagem adicionada ao projeto é considerada como uma textura pela Unity
3D, e para que seja classificada como sprite, a propriedade Texture Type deve estar
como Sprite (2D and UI).
No modo Single o sprite é considerado como uma figura única e estática, sendo
normalmente utilizada na concepção do cenário.
O principal uso do Sprite Editor é para a divisão do Sprite Sheet nas figuras parciais
para animação quadro-a-quadro. Através da opção Slice é muito simples efetuar a
divisão, ou fatiamento, do Sprite Sheet original.
Após efetuar a divisão nos quadros constituintes, estes ficam acessíveis na área dos
Assets do projeto, e basta selecionar os quadros desejados e arrastar para a Scene
View para que sejam criados a animação e o controle de estados de forma
automática.
O sprite BirdHero deverá ser configurado como Multiple, sendo aberto o Sprite
Editor em seguida. Na janela do Sprite Editor será utilizada a opção Slice e selecionado
Type Automatic e, ao sair dessa janela, serão gerados BirdHero_0, BirdHero_1
e BirdHero_2.
O objetivo de uma Sorting Layer é funcionar como uma camada de ordenação para o
desenho dos objetos, trazendo a noção de profundidade ao ambiente 2D.
Como resultado dessa última operação, serão geradas a animação Idle e a máquina
de estados Bird no novo diretório, além de um componente Animator, na lista de
propriedade do Sprite Bird, relacionando-o com a nova máquina de estados.
Serão geradas duas chaves de animação com Bird, mas como Idle representa o
pássaro parado, a segunda chave deve ser excluída. A chave restante pode ser copiada
Com o botão de gravação ativado (círculo vermelho), deve ser escolhido o sprite
BirdHero_1 entre os Assets, e arrastado para a propriedade Sprite do Sprite
Renderer de Bird, a qual deverá estar com fundo vermelho.
Outra animação deverá ser criada (Die), e esta seguirá o mesmo processo anterior,
porém com o uso de BirdHero_2.
Será criada uma transição de Idle para Die, condicionada à ocorrência da Trigger Die
e sem Exit Time. Da mesma forma, será criada uma transição para Flap condicionada
à Trigger Flap e sem Exit Time. Por fim deverá ser criada uma transição de Flap para
Idle sem condicionais ou quaisquer configurações adicionais.
Qualquer colisão do pássaro, com o solo ou obstáculos, levará ao fim do jogo, logo, é
necessário definir os colisores dos diversos sprites utilizados.
Para Bird será utilizado um PolygonCollider2D, pois trará uma envoltória mais
detalhada para o sprite. No solo (Ground) será utilizado um BoxCollider2D, mas
exigirá a sua edição. Como Ground é baseado em um PNG, o colisor do tipo Box
envolve toda a figura, inclusive a parte transparente, devendo ser ajustado apenas
para a parte visível.
Nesse jogo de exemplo será utilizado o toque rápido de tela (tap) para imprimir força
vertical ascendente ao pássaro, de forma a "combater" a ação da gravidade e evitar
que ele chegue a tocar o solo.
Deverá ser adicionado um Script C#, nomeado como "Bird", ao sprite de mesmo nome,
e programado o evento Update para captura dos comandos e aplicação da força
vertical.
Será criado um atributo interno "isDead", que indicará o fim de jogo por causa da
ocorrência de uma colisão. Também será utilizada uma propriedade "upForce",
indicando a intensidade da força aplicada verticalmente a cada ocorrência de "tap" do
jogador.
void Update () {
if (isDead == false) {
if (Input.touchCount == 1)
for(int i = 0; i < Input.GetTouch(0).tapCount; i++) {
rb2d.velocity = Vector2.zero;
rb2d.AddForce(new Vector2(0,upForce));
anim.SetTrigger("Flap");
}
}
}
void OnCollisionEnter2D () {
isDead = true;
anim.SetTrigger("Die");
}
Com relação ao HUD, esse jogo de exemplo apresenta informações muito simples,
incluindo apenas a pontuação, atualizada via programação, e a mensagem de fim de
jogo. Essas informações serão exibidas em modo texto, utilizando uma fonte
personalizada, no caso a LuckiestGuy, presente entre os Assets do projeto, com
tamanho 32.
Por fim, outro componente de texto será adicionado como filho de GameOverText,
utilizando fonte um pouco menor e com a mensagem "Flap to Restart".
Nesse ponto é necessário criar um Controlador para o jogo, o qual servirá de gestor
global do projeto.
Para iniciar a sua criação, deverá ser adicionado à cena um Game Object do tipo
Empty com o nome GameController, e a esse objeto será adicionado um script com
o mesmo nome.
Deverá existir um acesso global ao controlador de jogo, o que é obtido por uma
instância estática e a programação correta do método Awake.
void Awake () {
if (instance == null)
instance = this;
else if (instance != this)
Destroy (gameObject);
}
void OnCollisionEnter2D () {
isDead = true;
anim.SetTrigger("Die");
GameController.instance.BirdDied ();
}
void Start () {
rb2d = GetComponent<Rigidbody2D> ();
rb2d.velocity = new Vector2 (scrollSpeed, 0);
}
void Update () {
if (GameController.instance.gameOver)
rb2d.velocity = Vector2.zero;
}
Com a adoção desse script o fundo irá se mover, mas irá desaparecer da tela após
algum tempo.
Para resolver esse problema deverá ser feita a repetição do fundo, o que implica na
cópia de Ground, seguida do agrupamento das duas instâncias sob um novo Game
Object do tipo Empty, o qual será nomeado Scenery.
O próximo passo é a geração de obstáculos, para os quais deverão ser utilizados dois
objetos ColumnSprite, ajustados corretamente via rotação e translação, e com Box
Collider 2D.
Após efetuar todas essas operações, deve ser criado um diretório Prefabs nos Assets,
e arrastado o objeto Columns para esse novo diretório. Será gerado um Prefab, ou
No método Start deve ser alocado o pool de colunas, e para que funcione
corretamente, o Prefab Columns deve ser arrastado a partir de Assets para a
propriedade columnPrefab do script, no Inspector.
void Start () {
columns = new GameObject[ columnPoolSize ];
for (int i=0; i<columnPoolSize; i++)
columns [i] = (GameObject)
Instantiate (columnPrefab, objectPoolPosition,
Quaternion.identity);
}
No método Update deve ser atualizada a posição da coluna corrente no tempo indicado
por spawnRate, e a classe Random permite a escolha de uma posição vertical
aleatória para a coluna que irá ser modificada.
timeSinceLastSpawned += Time.deltaTime;
if (!GameController.instance.gameOver &&
timeSinceLastSpawned >= spawnRate) {
timeSinceLastSpawned = 0;
float spawnYPosition = Random.Range(columnMin,columnMax);
columns[currentColumn].transform.position =
new Vector2(spawnXPosition,spawnYPosition);
currentColumn++;
if(currentColumn>=columnPoolSize)
currentColumn = 0;
}
Nesse ponto o jogo está totalmente funcional, sendo possível trabalhar com facilidade
a parte de áudio. O controle de recursos sonoros na Unity 3D apresenta características
muito avançadas, como som 3D e componentes de mixagem, mas para um jogo
voltado para a plataforma móvel basta utilizar os elementos básicos.
Inicialmente deve ser criada uma pasta Sound em Assets para receber os arquivos de
áudio, e para o som ambiente será adicionado um Audio Source na cena, setando
Audio Clip para um arquivo OGG ou MP3 que deverá ficar na nova pasta.
Não é necessário um mixer, sendo a saída voltada de forma automática para o Audio
Listener associado à Main Camera, e as propriedades Loop e Play on Awake
deverão ser marcadas.
O script de Bird será modificado para incluir esse reconhecimento no Start, bem como,
ao final do método Update, após a linha anim.SetTrigger("Flap"), será adicionada a
linha audioQuack.play( ), fazendo com que o som seja ativado a cada toque de tela.
Jogo de Exemplo 2
O segundo jogo de exemplo trata de um jogo de nave, e segue passos muito próximos
aos do primeiro exemplo. Inicialmente é necessário criar um projeto 2D chamado
JogoNave, e adicionar alguns PNGs no diretório Sprites.
Mísseis
Jogador
Começando com a configuração da nave do jogador, ela deve trabalhar com uma
máquina de estados e animações adequadas. Inicialmente deve ser configurada como
Multiple e feita a divisão pelo Sprite Editor, obtendo três instantes da nave:
inclinando à direita, inclinando à esquerda e centralizada.
Com Nave selecionada, deve ser aberta a janela Animation, e criada uma nova
animação chamada Centro que, por questões de organização, deve ser salva em um
diretório Animation, dentro de Assets.
O mesmo processo deve ser feito para criar a animação Direita, com a nave inclinando
para a direita, e com as três animações criadas, deve ser configurada a FSM através
do Animator.
Em seguida, deve ser adicionado à nave um Rigidbody 2D, marcado como Fixed
Angle, arraste linear 10 e escala de gravidade zero, além de acrescentar também um
Polygon Collider 2D.
Esse objeto deverá ser renomeado para Explosao, e a animação deverá ter
desmarcada a opção Loop Time, pois ela não deve se repetir.
Os mísseis também utilizarão modo Multiple, porém precisam do Loop Time, pois
a animação, nesse caso, repete-se. Como a figura utilizada aqui tem o míssil na
horizontal, ele será arrastado para a cena e rotacionado de 90° no eixo Z, sendo
renomeado para Missil, e representando o tiro da Nave.
Após esses passos, podem ser adicionados os recursos de áudio do jogo. Nesse caso,
o som será proveniente apenas dos mísseis e das explosões.
Para os próximos passos será necessário o reconhecimento simples dos Game Objects
envolvidos, e isso é obtido com o uso de Tags.
As tags serão utilizadas da seguinte forma: Nave e Inimigo recebem tags com seus
mesmos nomes, enquanto Missil recebe a tag Tiro, e MissilInimigo recebe a tag
TiroInimigo. A escolha da tag é feita pelo Object Inspector, e o uso dessas tags
permitirá reconhecer de forma muito simples os objetos que colidiram no evento
OnCollisionEnter2D.
Apenas observando que, apesar de não ter sido criado ainda o controlador de jogo, a
sua propriedade gameOver representará o fim do jogo.
void Awake () {
if (instance == null)
instance = this;
else if (instance != this)
Destroy (gameObject);
}
Será fácil observar uma grande semelhança, nesse início, com o controlador utilizado
no Jogo de Exemplo 1 – Flapp Bird Style. No entanto, esse controlador também
faz o spawn das naves inimigas, mas da mesma forma que no primeiro exemplo,
devem ser configuradas as propriedades do script pelo Inspector, a partir do objeto
Controlador, definindo o scoreText e o gameOverText, além do inimigo. Esses
elementos estarão prontos para uso em passos posteriores.
void Start () {
GerarInimigo ();
}
void Update () {
if (gameOver) {
void Start () {
rb2d = GetComponent<Rigidbody2D> ();
}
void Update () {
if ((rb2d.position.y < -6)||(Controlador.instance.gameOver)) {
Destroy (gameObject);
return;
}
Vector3 origem = transform.position;
origem.y -= 2f;
RaycastHit2D alvo=Physics2D.Raycast(origem, -Vector2.up);
if (alvo.collider != null) {
if (alvo.collider.gameObject.tag == "Nave")
Atirar ();
}
}
O método Update também faz a remoção da nave inimiga da memória quando ela
passa pelo extremo inferior do jogo, não tendo mais chance de colisão com o objeto
Nave, ou quando ocorre o fim do jogo.
Finalmente, na ocorrência de colisão, deve ser testado pela tag se o que colidiu foi
um tiro do objeto Nave e, sendo verdade, instanciar a explosão, aumentar o score, e
remover da memória tanto o tiro quanto a nave inimiga atingida.
void Update () {
if (Controlador.instance.gameOver )
return;
if (transform.position.y != -3.7f) {
Neste ponto podem ser criadas as Sorting Layers, assumindo os mesmos valores do
primeiro exemplo: Background, Midground e Foreground.
Com mais essa configuração, Inimigo deve ser transformado em PreFab e removido
da cena. O controlador será configurado em seguida, recebendo como a propriedade
inimigo do script o PreFab Inimigo.
Ao final de mais esse passo, restará apenas a Nave visível na cena, e deve ser
adicionado o fundo, de forma similar ao primeiro exemplo, porém com movimentação
vertical. Para a definição do fundo é utilizado mais um arquivo PNG.
Para o fundo escolhido deve ser aplicada uma rotação de 90°. Talvez haja a
necessidade de escalonar os demais objetos para que se tornem proporcionais ao
fundo, ou escalonar o próprio fundo, de forma a gerar uma boa área de movimentação
para a Nave.
Também devem ser adicionados dois Box Collider 2D, editados para que fique um
em cada lateral do fundo, com o objetivo de limitar a movimentação da Nave. Os
colisores utilizados aparecerão na cor verde com a opção Edit Collider. Também
será adicionado um Rigidbody 2D do tipo Kinematic.
Como foi feito no primeiro exemplo, o fundo deverá ser duplicado e a cópia (Fundo1)
movida, agora na vertical até o topo do original, de forma a transmitir a sensação de
continuidade. Apenas por questão de organização, esses dois componentes serão
agrupados sob um novo Empty Object de nome Cenario.
Para que ocorra o movimento correto, dois scripts devem ser utilizados em Fundo e
Fundo1: ScrollBackground, responsável pela rolagem do componente, e
RepeatBackground, para transmitir a sensação de repetição do fundo.
Completando esses passos, o jogo estará completo e poderá ser testado, apresentando
como resultado final as telas abaixo para as figuras PNG utilizadas.
DUNN, F.; PARBERRY, I. 3D Math primer for graphics and game development. Texas:
Wordware Publishing, 2002.
FERGUSON, J.; PATTERSON, B.; BERES, J.; BOUTQUIN, P.; GUPTA, M. C# bible.,
Indiana: Wiley, 2002.
GOLDSTONE, W. Unity 3.x game development essentials. s/l: Packt Publishing Ltd,
2011.