Sistema de diálogos no Unity

Muito bem, no vídeo abaixo eu mostro brevemente o funcionamento de um sisteminha de diálogos que eu fiz para usar no Unity. A ideia desse sistema é permitir mostrar pro jogador uma sequência de falas para contar uma história. Por enquanto, é possível apenas exibir um texto sequencial, não há interação do jogador (exceto pela opção de acelerar o texto) nem múltiplos caminhos. Como bifurcações na história não é algo que eu preciso por agora, não devo implementar essa funcionalidade tão cedo, mas é algo que pode ser interessante de mexer no futuro.

No mais, o sistema também permite configurar diversos parâmetros nos blocos de texto pra obter diferentes resultados em diferentes situações (além de diálogos, eu também pretendo usar isto para exibir textos de ajuda nos menus do jogo). Entre os parâmetros disponíveis, é possível ativar/desativar a quebra automática de linhas (que move para linha seguinte quando o limite de caracteres ou a largura máxima estoura) e também um limite de linhas exibidas simultaneamente. Também é possível configurar o alinhamento do texto (esquerda, direita ou centralizado), a velocidade do rolamento e se os blocos de texto devem avançar automaticamente depois de um tempo ou não (com esta opção desligada, o jogador precisa apertar um botão para prosseguir, como em vários RPGs antigos).

Para dar mais dinamismo e poder ser usado em uma cutscene, por exemplo, é possível mover o texto pela tela. Usando o método Camera.WorldToScreenPoint, é bem simples de atrelar a posição de um texto na tela à de um objeto na cena (em 2D ou 3D) e fazer o texto seguir um personagem ou algo do tipo.

Estudos de shaders no Unity

Eu já mexi um pouco com shaders, tanto no Unity quanto em XNA, mas nunca tive um conhecimento muito grande sobre o assunto. Na semana passada eu encontrei um material bem bacana sobre shaders no Unity e decidi fazer as vídeo-aulas pra me aprofundar um pouco mais nessa área. São dez aulas de uns 15 ou 20 minutos que ensinam desde o básico de como os shaders funcionam dentro do Unity (que acaba sendo um pouco diferente por causa do ShaderLab) até como criar seus próprios efeitos.

Até aqui, eu fiz seis aulas. O mais interessante, pra mim, foi justamente a aula seis, que discute como criar seu próprio modelo de iluminação. Por padrão, o Unity suporta os modelos  de Lambert ou BlinnPhong. Com isso, ao escrever um shader você pode “herdar” de um destes dois modelos e basicamente escrever o pixel shader, deixando o Unity tomar conta do resto. Como isso deixa muita coisa escondida por trás dos panos, esse tutorial serve justamente pra entender como o shader funciona sem usar um modelo pronto. Acaba sendo uma ótima visão mais geral de como a coisa toda funciona. O resultado esta na imagem abaixo, à esquerda (é só um Phong bem simples).

Também consegui brincar um pouco carregando um mapa de especular separado pra complementar o quarto tutorial, que trata de adicionar este componente no modelo de iluminação. No vídeo, ele ensina a usar o alfa da própria textura difusa para definir onde o especular seria aplicado, mas eu adicionei um mapa de especular como um parâmetro extra e busquei nele essa informação do alfa. É uma alteração simples, mas já dá pra sentir um pouco como é fazer este tipo de alteração nos shaders.

Unity shaders

Pra programar, eu uso o Visual Studio 2012 e ele não tem um syntax highlighting para shaders. Pra solucionar isso, usei o NShader, mas como os shaders do Unity não estão diretamente em HLSL, CG ou GLSL, é preciso um passo extra pra fazer a coloração funcionar. A solução está aqui e consiste em alterar um arquivo do NShader pra ele passar a colorir arquivos com a extensão .shader também. Ainda estou procurando uma solução de auto-completar, mas por enquanto dá pra me virar bem sem isso.

Por fim, eu criei um repositório pra armazenar o resultado das aulas e os experimentos que eu fizer sobre isso. Quem tiver interesse, pode acessar aqui.

Menu de Save/Load no Unity

Eu estou desenvolvendo um jogo com um amigo no Unity e nos últimos dias eu venho trabalhando na parte da interface gráfica, criando principalmente os menus do jogo. A despeito do sistema de GUI do Unity ser bem ruim (falo disso em outra oportunidade), até que deu pra fazer algo bacana. Eu fiz um sistema de troca de telas e adicionei agora os menus que o jogador irá usar para criar um novo jogo ou carregar um jogo salvo.

O vídeo no início do post mostra o menu em uso e ele basicamente funciona assim: ao iniciar o jogo pela primeira vez, a opção de carregar está inativa e só é possível começar um novo jogo. Quando ao menos um jogo salvo for encontrado, esta opção é habilitada. Na tela de novo jogo, se o jogador escolher salvar sobre um save existente, uma mensagem de confirmação é exibida pra garantir que ele não apague o jogo sem querer (esqueci de colocar essa parte no vídeo ¬¬). Enfim, na tela de carregar é possível escolher um dos jogos salvos. Depois disso, o jogo em si é carregado (aqui, ele simplesmente volta pro menu principal).

No jogo estamos usando a serialização de objetos do .Net para salvar os dados no formato XML. Essa parte é bem besta, eu tenho uma classe que guarda os dados a serem salvos e no momento adequado mando ler ou escrever no XML e o .Net faz o resto pra mim.

Pra controlar o nome dos saves, foi mais prático usar o PlayerPrefs do Unity mesmo. Essa classe permite salvar alguns dados diretamente no registro do Windows. Nesse caso, eu uso o PlayerPrefs pra salvar o nome dos saves criados pelo jogador (que, internamente, são simplesmente save1.xml, save2.xml e save3.xml). Depois, ao carregar o jogo, se um desses saves existir, significa que a opção “Continue” deve ser habilitada. No PlayerPrefs também é salvo o nome do save que aparece para o usuário, aquele que aparece no vídeo com a data e hora em que o jogo foi salvo.

Com essa combinação, eu consigo dar ao jogador a escolha de qual jogo carregar e passar para minha classe que gerencia os arquivos o nome do save que ele deve buscar em disco pra iniciar o jogo e também usar quando tiver que salvar novamente.

Ah sim, toda nossa interface funciona também com teclado ou joystick, ao contrário da GUI padrão do Unity. Deu um trabalhinho, especialmente pra manter a compatibilidade com o mouse, mas agora funciona bem. Se eu animar, falo disso num próximo post. Até lá.

Game of Life no Unity

O Game of Life (não confundir com o Jogo da Vida de tabuleiro) é um sistema elaborado pelo matemático inglês John Horton Conway que simula a evolução de um conjunto de células (ou uma população) a partir de um estado inicial e um conjunto de regras. Ele é conhecido como um jogo de zero jogadores, já que a interação acontece só na criação da configuração inicial. Depois disso, resta observar a evolução das células.

Não vou entrar em muitos detalhes sobre seu histórico e aplicações, então pra quem quiser mais detalhes recomendo uma passada rápida pela Wikipédia, pois lá existe um material bem interessante sobre o tema e pode servir, no mínimo, como um bom ponto de partida.

O Game of Life parte de uma premissa bem simples. Células distribuídas em uma grade bidimensional podem estar vivas ou mortas e, a cada nova atualização, interagem com seus oito vizinhos (todos as células ao redor, incluindo diagonais) para determinar seu estado na próxima iteração. O interessante é que foi se descobrindo que certos padrões iniciais fazem com que a “população” se comporte de formas diferentes. Por exemplo, é possível criar populações iniciais que nunca morrem, que alternam entre diversos padrões e eventualmente voltam ao original, ou até mesmo que se deslocam pela grade em um padrão específico e previsível.

Mas como é feita a atualização das células? Bem, uma célula vive ou não na próxima geração dependendo do seu estado atual e da quantidade de vizinhos vivos. As regras são as seguintes:

  • Uma célula viva com menos de dois vizinhos morre por sub-população.
  • Uma célula viva com dois ou três vizinhos continua viva na próxima geração.
  • Uma célula viva com mais de três vizinhos morre por super-população.
  • Uma célula morta com exatos três vizinhos se torna viva na próxima geração, como resultado de reprodução.

Com essas simples quatro regras consegue-se fazer populações com comportamentos bem interessantes. No vídeo abaixo, eu configurei a cena com padrões osciladores (que trocam várias vezes de estado até voltar ao original) e fixos (que, mesmo respeitando as regras acima, nunca trocam de estado). Só uma observação: o vídeo na verdade vai até onze segundos. Por algum motivo o YouTube duplicou o vídeo, portanto depois de onze segundos é a mesma coisa repetida.

Essa implementação foi feita no Unity e quem quiser pode testar clicando aqui ou no link abaixo. Os controles são simples: com o mouse você liga/desliga as células e os botões no topo permitem avançar a geração um passo (step) ou em modo automático (play/stop) e o botão clear apaga todas as células de uma só vez.

Game of Life

Adicionando buracos de bala em um FPS

Continuando na minha demo de FPS, implementei dessa vez marcas de bala nas paredes e portas.

A marca de bala é feita com um objeto texturizado e pra posicionar no cenário eu uso o resultado do raycast do Unity, que retorna o objeto interceptado, o ponto de interseção e a normal naquele ponto. Com esses dados, eu posiciono a marca de forma que ela fique na frente do objeto acertado.

Como uma forma de otimização, as marcas de bala são guardadas em uma lista que pode ter seu tamanho máximo configurado para qualquer valor. Quando o tamanho máximo for atingido, os objetos mais antigos são destruídos liberando memória. Dessa forma, é possível gerar marcas de bala no cenário sem se preocupar em ter estouro de memória e problemas de desempenho.

No vídeo abaixo tem uma demonstração dessa nova função, com os buracos de bala sendo gerados nas superfícies atingidas por tiros e sendo removidos quando o tamanho máximo da lista é atingido. Pra deixar a demonstração mais fácil, eu limitei a cinco itens o máximo da lista, mas esse valor pode ser bem maior.

 

Estou disponibilizando também a demo atualizada aqui: FPS Demo.

Vídeo da demo de FPS no Unity

Conforme eu comentei antes, estou fazendo uma demo de um FPS no estilo dos primeiros jogos do gênero. A ideia é usar essa demo pra estudar algumas coisas sobre desenvolvimento de jogos e Unity, ter mais um item pra adicionar no portfólio e, quem sabe, transformá-lo em algo que as pessoas realmente queiram jogar.

Mas vamos fazer uma coisa por vez. Por hora, deixo vocês com um vídeo da fase demo que montei até agora. Nessa fase, há um contador de tempo disparado ao pisar sobre a faixa de largada e inimigos aparecem no cenário bloqueando as portas até chegar na faixa de chegada, onde o tempo para de correr. Quem se interessar, pode jogar aqui: FPS Demo.

Criando um FPS old school no Unity

Um dia desses ouvindo o 99 Vidas sobre Doom, Quake e Duke Nuken bateu aquela vontade de implementar um jogo nesse estilo, só pela diversão! Ontem resolvi pegar o Unity e ver o que dava pra fazer. Nesse post vou mostrar um pouco do que implementei de ontem pra hoje.

A ideia inicial é implementar um FPS usando as mecânicas desses jogos antigos. Sendo assim, a principal diferença em relação à maioria dos FPS do mundo é a ausência de mira no mouse. Aqui só é possível mirar no eixo horizontal. Eu fiz isso por dois motivos: pra manter um estilo parecido com o Doom original e porque pretendo, no futuro, aproveitar essa base em um jogo de iPhone (pelo que eu andei vendo, jogos em primeira pessoa funcionam mais ou menos bem no iPhone usando esse estilo de controle – vide Wolfenstein 3D).

A programação foi toda feita por mim, não usei nenhum script pronto. Pra arte, eu usei algumas texturas do Doom (retiradas principalmente deste site), a arma é do Duke Nuken 3D e os sons eu arrumei por aí – isso não é importante.

Bem, e o quê tem no jogo? Resumidamente, eis a lista das funções implementadas:

  • Controle do personagem em primeira pessoa: o personagem anda para frente e para trás, esquerda e direita, e pode girar ao redor do eixo Y para ambos os lados também;
  • Função de tiro: uso o raycast do Unity pra testar se os tiros acertaram os NPCs da cena (usando uma layer específica pra isso, garantindo que o raio não vai ser testado contra todos os objetos da cena);
  • Animação de tiro: eu fiz a animação de tiro usando aquele meu componente de sprite 2D. A arma não é um objeto 3D, mas sim uma imagem chapada na tela que é animada quadro a quadro quando um tiro é disparado;
  • Morte dos NPCs: quando um tiro acerta um NPC, este recebe um dano e desaparece da cena quando sua energia e zerada;
  • Apresentação dos NPCs: nessa versão, os NPCs aparecem como aquelas placas de estandes de tiro. Quando o jogador entra em um trigger, os NPCs associados ao trigger são ativados;
  • Diferentes tipos de porta: as portas no jogo impedem o progresso do jogador. Eu programei três tipos de porta. A normal é aberta quando o jogador se aproxima e entra no seu trigger. A porta com chave funciona da mesma forma, mas exige que o jogador tenha a chave da mesma cor no seu inventario. Por fim, tem umas portas que se abrem somente quando todos os NPCs da área foram mortos;
  • Chaves e inventario: por hora, o inventário serve só pra guardar as chaves que o jogador coletou. O inventario é consultado nas portas que exigem uma chave pra abrir. A chave tem um script fica rotacionando o objeto, pra dar um efeito visual melhor;
  • Cronômetro: na demo que eu montei, o objetivo é alcançar o final da fase no menor tempo possível. A contagem de tempo se inicia quando o jogador entra no trigger de início do cronômetro e termina quando ele toca o trigger de final.

Devido ao tempo de desenvolvimento bem curto (foram 24 horas corridas, mas nesse meio tempo eu parei pra fazer várias outras coisas, dormi, vim de Niterói pra Teresópolis…) e à falta de planejamento (fui implementando as funções conforme ia pensando em coisas interessantes pra fazer, ao invés de planejar tudo antes), até que o resultado ficou legal. A ideia agora é fazer uma fase mais elaborada e adicionar NPCs com inteligência artificial. Tem ainda muitas funções pra implementar, como uso de múltiplas armas, munição limitada e controle de energia do jogador. Mais pra frente também pretendo fazer uns testes com multiplayer.

Quem se interessar, pode testar o jogo diretamente no navegador através desse link: FPS Demo. Os controles são:

  • W, S: para para frente/para trás;
  • A, D: vira para esquerda/direita;
  • Q, E: anda para esquerda/direita;
  • Espaço: atira;
  • Shift esquerdo (segurando): corre.

Usando o Unity em jogos 2D

Pra alguns, usar o Unity para jogos 2D é o equivalente a usar um canhão pra matar mosca. Eu discordo. Durante o Global Game Jam deste ano nós fizemos justamente isso e acho que o restante das funcionalidades do Unity (o editor visual, o desenvolvimento orientado a componentes, a facilidade pra testar e a física integrada) ajudou muito. É claro que precisamos lidar com um trabalho extra pra trabalhar com imagens e especialmente animar o personagem.

A solução implementada nesse projeto foi criar um material para cada quadro de animação e um array de materiais contendo as animações. Assim, a gente podia chamar uma animação pelo nome no código e o gerenciador de sprites se responsabilizava trocar o material, alternando a textura do objeto exibido na tela. É uma solução boa e funcionou bem, principalmente se levarmos em conta que foi implementada em um projeto e, ao todo, durou menos de 48 horas.

Mas essa não é uma solução livre de problemas. Criar um material para CADA quadro e ainda ter que arrastar todos os quadros na ordem certa pra lista de animação é um processo bem trabalhoso. Além disso, imagens separadas gastam mais espaço na memória e spritesheets são bastante usados em jogos, então uma implementação que contemple seu uso seria interessante.

Eu pesquisei rapidamente algumas soluções de 2D para o Unity, mas o que eu achei ou era simples demais, complicado demais ou pago. Sim, eu sou um chato e não gostei de nada que encontrei (exceto por uma ferramenta paga que parece muito bom, mas não quis arriscar a compra E acabei por perder o link). Então resolvi fazer minha própria implementação de um sistema de animacão 2D pro Unity.

As animações usam o um spritesheet que contém todos os quadros de animação de um objeto. Como entrada, o componente recebe a largura e altura de cada quadro de animação (nesse formato todos os quadros devem ter largura e altura iguais) e índice do quadro que se deseja exibir. Com isso, o tiling da textura é ajustado para exibir somente o equivalente a um quadro e quando o índice do quadro é alterado os offsets são alterados para buscar exatamente a parte da textura que deve ser exibida. Isso, é claro, fica transparente ao usuário. As únicas entradas necessárias são mesmo o número de tiles em cada eixo e o quadro atual. O resto é feito de forma automática.

Esse já é um resultado legal, mas eu queria também uma forma simples de manipular o índice do quadro, podendo definir uma lista de quadros que formam uma animação. Assim, eu poderia dizer, por exemplo, que os quadros de 0 a 3 formam a animação “movimento para a esquerda” e chamar esta animação pelo nome quando a seta esquerda for pressionada.

E como eu gosto de deixar as coisas o mais genérico possível, fiz dois modelos de animação: sequencial e personalizado. No primeiro caso, basta dizer qual o primeiro e o último quadro e a animação vai sendo tocada trocando cada quadro em ordem (uma animação entre os quadros 1 e 5, por exemplo, tocaria os quadros 1, 2, 3, 4 e 5). Na segunda forma, basta passar uma string com os quadros de animação separados por vírgula e eles serão tocados na ordem definida (por exemplo, ao passar a string “5,3,4,2″, os quadros serão exibidos na sequencia 5, 3, 4 e 2). Há também um controle que define a velocidade de animação e se ela deve se repetir ao chegar no final.

Chamar uma animação via código fica muito simples. No exemplo abaixo, eu chamo as animações “5,4,3,2″ (alterna entre os quadros que mostram essa sequência de números na tela) e “2-5″ (que mostra os números de 2 a 5), conforme configurados na imagem acima.

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        spriteSheet.PlayCustomAnimation("5,4,3,2");
        spriteSheet.PlaySequencialAnimation("2-5");
    }
}

Pra fechar, também um incluí uma forma de chamar um método sempre que uma animação acabar. Isso é interessante pra encadear animações diferentes ou realizar qualquer ação que pode ser disparada ao término de uma animação (um exemplo seria uma animação do personagem tomando dano, que quando acaba deve chamar novamente à animação normal).

Usando os delegates do C#, essa funcionalidade é implementada de forma muito fácil. Basta definir um método que recebe como parâmetro um SpriteAnimation e atribuir à animação este método. No exemplo abaixo, primeiro o definimos um método que imprime o nome da animação que acaba de terminar e depois buscamos na lista de animações aquela que queremos que execute o método, atribuindo o método em questão à sua lista de delegates.

// Método executado quando a animação termina
private void AnimationEndExample(SpriteAnimation animation)
{
    Debug.Log(string.Format("Animation: {0} is finished.", animation.name));
}
 
// Busca a animação desejada na lista de animações e atribui o delegate acima
SpriteAnimation anim1 = spriteSheet.FindAnimation("my_animation", 
    spriteSheet.customAnimations);
anim1.onAnimationEnd += 
    new SpriteAnimation.OnAnimationEnd(AnimationEndExample);

Estou disponibilizando um projeto com alguns exemplos a mais e os scripts necessários para usar este gerenciador de animações. Quem se interessar, pode fazer o download neste link.

Até a próxima.

Desenvolvendo e testando aplicações do Unity para iOS

Com a distribuição da versão básica do Unity para iOS de graça (lembrando que as versões de iOS e Android estão de graça até dia 8/4), eu resolvi fazer uns testes para ver como o Unity funciona nesta plataforma.

De cara, a decepção foi grande com a trabalheira que é configurar tudo pra fazer um simples Hello World rodar, mesmo no emulador. Comparado com o que eu fazia antes, que é desenvolver e testar direto pelo Xcode, essa complicação toda atrapalha, mas dá pra entender seus motivos, já que o iOS é uma plataforma bem fechada e o Unity meio que tem que dar uma volta pra fazer tudo funcionar (enquanto no Windows, por exemplo, toda essa parte fica transparente na hora de compilar o projeto).

Enfim, meu primeiro objetivo era colocar uma cena vazia rodando no emulador e em um iPod que eu uso aqui para testes. Aí já aparece uma questão importante: antes de gerar um build pelo Unity, é preciso configurar em Player Settings onde este build irá rodar, isto é, se ele vai ser executado no emulador do Xcode ou num dispositivo real. Essa configuração é simples, basta escolher o tipo em “SDK Version”, mas é bem importante pra evitar dores de cabeça, já que um projeto feito pro emulador não compila pro dispositivo e vice-versa.

Diferente de quando estamos desenvolvendo pra Mac ou Windows, esse build do Unity não gera realmente um executável. Ele compila uns arquivos do Unity (gera um pacote com os assets, se não me engano) e cria um projeto para ser aberto no Xcode. Aí sim, a partir do Xcode podemos compilar e fazer debug no emulador ou no iPod, iPhone ou iPad.

Já deu pra perceber que é inviável ficar gerando builds e compilando no Xcode a cada novo teste, não é? Mas, a principio, essa é a única forma de testar o programa no emulador ou em um dispositivo, algo vai ter que ser feito a todo momento por causa do input diferenciado da plataforma (salvo casos onde inputs de mouse e teclado podem ser mapeados posteriormente pra input de toque e acelerômetro). Pra facilitar, o pessoal do Unity lançou o Unity Remote, que nada mais é que um aplicativo rodando no iOS que se comunica com o Unity através de uma rede wifi ou pelo cabo USB. A saída gráfica da aba “Game” do Unity é jogada para o dispositivo e os inputs realizados no dispositivo são enviados de volta para o Unity.

É claro que com esta solução não dá pra ter uma noção exata do desempenho do programa porque ele ainda está rodando no Mac, mas quebra um galho enorme pra fazer todo o tratamento do input. Os testes no aparelho e eventuais otimizações vão mesmo ter que passar pelo caminho tortuoso descrito acima.

Pra fechar, resolvi fazer uma demo com um cubo que tem um rigidbody e um script que aplica uma força neste corpo quando a tela é tocada, justamente pra testar o input através do Unity Remote e do aplicativo rodando no iPod. Os métodos de input são descritos aqui e o que eu fiz foi basicamente aplicar uma força para cima a cada vez que a tela for tocada. O código (em C#) ficou assim:

public class InputTest : MonoBehaviour
{
    void Update ()
    {
        foreach (Touch touch in Input.touches)
        {
            if (touch.phase == TouchPhase.Began)
            {
                rigidbody.AddForce(Vector3.up * 500);
            }
        }
    }
}

O projeto é bem básico, mas estou anexando ele no post de qualquer forma para o caso de alguém se interessar. Inclui só a parte do Unity e não coloquei nada do projeto gerado para o Xcode por dois motivos: eu não tenho certeza, mas é possível que o Unity adicione alguma informação de licença e do perfil de desenvolvimento no projeto que faria com que ele não funcionasse diretamente em outra máquina e, mais importante, o projeto do Xcode ficou com mais de 300MB, enquanto este do Unity tem uns poucos KB. O aqui para download está aqui.