Introdução à Programação de Jogos Ferramentas Universidade Federal do Rio de Janeiro Pedro Demasi demasi@ufrj.br http://www.labic.nce.ufrj.br/jogos O que é? Modo de jogo em que o jogador apenas observa (ou seja, o computador é quem joga). Pra que serve? Demonstração geral do jogo. Demonstração de comandos e funções (pode servir como uma espécie de tutorial do jogo). Efeito estético (como um protetor de tela). Divulgação. Movimentos são capturados do teclado com alguém realmente jogando e gravados em disco. Quando o modo demo é ativado, os dados são lidos do arquivo e simulam o pressionar das teclas. Jogos com ambientes não-determinísticos podem causar problemas. Solução: gravar no início do arquivo a semente de geração de números aleatórios. A cada frame de jogo, o estado de todas as teclas que correspondem a ações do jogador são capturados e gravados em arquivo (pode ser feito o mesmo para joystick e mouse). Cuidado para gravar o estado real da entrada (evitar ler duas vezes o mesmo estado, pois isso certamente irá gerar erros!) NÃO fazer: if (key[key_up]) movimenta_pra_cima(); escreve_no_arquivo(key[key_up]); Fazer, por exemplo: t_up = key[key_up]; if (t_up) movimenta_pra_cima(); escreve_no_arquivo(t_up); Desvantagem de ser sempre igual (afinal, foi pré gravado). Solução: gravar vários demos (número ainda limitado, mas aumenta a variedade). Desvantagem de necessitar espaço extra em disco. Análise: 8 teclas possíveis seriam 8 bytes. 40 frames por segundo => 320 bytes/segundo. 300 segundos (5 minutos) de demo => 96 kb (aprox). Consumo baixo. Pode-se dividir o tamanho por 8 usando-se 1 bit por tecla (no exemplo anterior => 12 kb de demo) 1
Exemplo simples de gravação (pseudo-código): estrutura tecla boolean cima,baixo,esq,dir; fim estrutura (...) tecla.cima = estado_tecla_cima; tecla.baixo = estado_tecla_baixo; tecla.esq = estado_tecla_esq; tecla.dir = estado_tecla_dir; escreve_no_arquivo_demo(tecla); if (tecla.cima) movimenta_pra_cima; if (tecla.baixo) movimenta_pra_baixo; if (tecla.esq) movimenta_pra_esq; if (tecla.dir) movimenta_pra_dir; Quando se estiver no modo demo, ao invés de ler a entrada do teclado, preenche-se as variáveis com os valores lidos do arquivo de demo. Programa Principal Leitura da Entrada Lê do Teclado (modo normal) Lê do Arquivo (modo demo) Exemplo simples de leitura (pseudo-código): estrutura tecla boolean cima,baixo,esq,dir; fim estrutura (...) if (modo_normal) tecla.cima = estado_tecla_cima; tecla.baixo = estado_tecla_baixo; tecla.esq = estado_tecla_esq; tecla.dir = estado_tecla_dir; else if (modo_demo) le_do_arquivo_demo(tecla); if (tecla.cima) movimenta_pra_cima; if (tecla.baixo) movimenta_pra_baixo; if (tecla.esq) movimenta_pra_esq; if (tecla.dir) movimenta_pra_dir; Juntando os modos (normal, gravação e demo): Programa Principal Leitura da Entrada Grava arquivo (modo gravação) Lê do Teclado (modo normal) Lê do Arquivo (modo demo) (exemplo simples): estrutura tecla boolean cima,baixo,esq,dir; fim estrutura (...) if (modo_normal ou modo_gravacao) tecla.cima = estado_tecla_cima; tecla.baixo = estado_tecla_baixo; tecla.esq = estado_tecla_esq; tecla.dir = estado_tecla_dir; if (modo_gravacao) escreve_no_arquivo_demo(tecla); else if (modo_demo) le_do_arquivo_demo(tecla); if (tecla.cima) movimenta_pra_cima; if (tecla.baixo) movimenta_pra_baixo; if (tecla.esq) movimenta_pra_esq; if (tecla.dir) movimenta_pra_dir; Demo por IA Ao invés de pré-gravar um jogo, usa-se um algoritmo de IA para simular o jogador. Espécie de bot. Desvantagem de requerer trabalho extra para desenvolver o bot. Em muitos jogos não há trabalho extra, basta aproveitar o que já foi feito para o jogo (por exemplo: FPS - First Person Shooters, luta, esportes em geral). 2
Demo por IA Se o bot não for muito bom (o que é comum), o modo demo pode criar situações constrangedoras (principalmente em exposições). Desvantagem da possibilidade de não mostrar todos os efeitos e atrativos do jogo como se deseja. Solução: criar um bot com uma IA viciada. Ou seja, que tenda a agir de forma a mostrar aquilo que se deseja. Dificuldade em se fazer um modo tutorial de demo usando-se um bot. Demo por IA Ao invés de se ler a entrada do teclado, lê-se a resposta do bot controlado por IA. Programa Principal Leitura da Entrada Lê do Teclado (modo normal) Resposta do Bot (modo demo) Demo por IA x Demo Gravado Para jogos em que o jogador possui as mesmas características dos adversários (por exemplo: luta e esportes em geral) a demo por IA é mais direta (afinal, a IA terá de ser implementada mesmo). Demo gravado é bem mais simples de implementar e confere controle total à demonstração. Por outro lado, é repetitivo e previsível, a não ser que se pré-grave diversas possibilidades de demo. Demo por script Pode ser parecido com o demo gravado, porém mais compacto. Pode ser, também, uma mistura de demo gravado com IA simples. Por exemplo, especifica-se pontos do cenário para onde se deve deslocar, itens a serem pegos em determinados lugares etc. Requer certo trabalho para interpretação do script (por mais simples que seja a sintaxe). O que é? Em determinadas partes do jogo o usuário pode querer que seu jogo seja salvo em disco. No futuro, o usuário pode querer restaurar o jogo salvo, e tudo volta exatamente como estava quando ele foi salvo. É como se o jogo ficasse congelado naquele ponto e voltasse ao normal quando o jogo salvo fosse novamente carregado. Idéia Básica Grava-se todas as estruturas do jogo que, de alguma forma, determinam o estado atual do mesmo, para que esse estado possa ser recuperado no futuro. Depende fortemente de como é o jogo e de suas características, quais suas variáveis, estruturas de dados etc. Deve-se decidir se o jogo pode ser salvo em qualquer ponto ou apenas em pontos específicos. 3
Salvando por senhas Muito usado antigamente, em consoles apenas com memória volátil. Pouco usado atualmente (encontra-se mais em jogos para Internet em Java e Flash, por exemplo). Idéia básica: ao invés de gravar em disco as informações, os valores a serem salvos passam por alguma função de codificação que cria a senha para que o estado possa ser recuperado (decodificado) no futuro. Salvando por senhas Exemplo: cada fase do jogo tem uma senha específica. Ao entrar a senha correta, o jogador vai direto para a fase correspondente. Desvantagem: o jogo não pode ser recomeçado de qualquer parte, mas apenas em pontos específicos. Desvantagem: não guarda estados do jogador (como vidas, pontos, etc) Vantagem: muito fácil de implementar. Salvando por senhas Pode-se melhorar o sistema de senhas para permitir salvar também pontos, vidas e outros estados. Exemplo de codificação: Senha = Str(Vidas) + * + Str(Pontos) + * + Str(Fase) Exemplo: 3*15000*5 Desvantagem: uma codificação tão simples será facilmente deduzida pelo jogador. Solução: criar funções mais complexas, inclusive inserindo caracteres aleatórios. Evitar que seja muito demorada. Salvando por senhas Não resolve o problema de apenas poder salvar ao passar de fase. Solução: permitir que o jogador requeira a senha a qualquer momento do jogo. Isso traz a desvantagem óbvia de necessitar de uma senha proporcionalmente grande à quantidade de variáveis a serem salvas. Conclusão: pode ser inviável (ninguém vai querer anotar uma senha de 10000 caracteres). De uma forma geral é uma boa solução para jogos simples e quando não há possibilidade de gravação. Salvando em disco Parecido com as senhas, mas a informação passa a ser salva em disco e recuperada do mesmo. Senhas muito grandes passam a ser viáveis, pois não serão anotadas pelo jogador e sim salvas em disco. Maior flexibilidade para permitir gravação em qualquer ponto do jogo (pode não ser o desejado). Mais cômodo e conveniente para o jogador. O que salvar Variáveis que controlem estados do jogo (pontos, fase, tempo, itens recolhidos etc.) Estados do ambiente (estados dos sprites, do jogador e dos tiles etc.) Estado de animações (por exemplo, do scroll). Opções de jogo (por exemplo: nível de dificuldade, resolução de tela etc.) Em suma, tudo o que possa ser particular do jogo. 4
O que NÃO salvar Variáveis desnecessárias (para que guardar a posição de um sprite que está morto?) Itens que não mudam (por exemplo, o mapa da fase se este não for dinâmico). Posição de objetos estáticos (em geral basta saber o estado, se estão vivos ou não). Objetos de cenário e composição. Evitar gravar dados redundantes. Observações Pode ser uma boa idéia usar uma biblioteca com algoritmos de compressão de dados para diminuir o tamanho do arquivo em disco. Definir a inclusão de opção de salvar o jogo antes de programar. Fica mais fácil tomar nota do que será necessário gravar ao invés de correr atrás depois. Um mesmo jogo pode ter vários tipos de save game. Jogos de esportes podem salvar times, jogadores, campeonatos etc. de forma separada. Engine ( motor ) O que é? O coração do jogo, como o kernel de um SO. Não se refere especificamente aos gráficos, sons, história etc. mas sim ao código base do jogo. Constitui as estruturas de dados funções básicas (mas não necessariamente simples ). Reutilizada para outros jogos (em geral do mesmo gênero, mas pode ser uma engine mais genérica). Assume, geralmente, o formato de uma biblioteca. Engine ( motor ) É uma Bibliotecas de Jogos ou de Gráficos? Bibliotecas de jogos (como Allegro) ou gráficos (como SDL) costumam ser muito mais genéricas do que as engines. Algumas funções podem aparecer nas três: Leitura de formatos de arquivos (JPG, BMP, WAV, MP3, MPG, AVI, 3DS etc.) Efeitos (fade, alpha-blending, fog etc.) Algoritmos 3D (z-buffer, iluminação, mapeamento de texturas etc.) Engine ( motor ) Exemplos de atribuições de uma engine: Conter o código de Ray Casting (com melhoras, como iluminação, transparência, inimigos). Detecção de colisão (fundamental). Estruturas de dados que descrevem o jogo (sprites, tiles, mapas de fase etc.) Gerenciar eventos do jogo (animação de sprites, leitura da entrada, salvar jogo etc.) Ser o mais reaproveitável possível. Editores Em geral acompanham uma determinada engine pois lêem e escrevem no formato da mesma. Mas podem ser genéricos (ainda assim terão algum tipo de formato próprio) Tipos comuns de editores: Editores de nível / fase Editores de sprites / tile Editores de enredo 5
Fundamentais para o design do jogo. Objetivo de facilitar a criação de fases. Determinado formato de estruturas de dados para representar a fase: Fundos (com scroll) e planos Objetos de cenário (decorativos) Tiles, itens, bônus etc. Sprites, inimigos etc. Deve ser o mais simples possível, mas que permita criar e visualizar com facilidade as fases. O editor é apenas para uso interno ou será distribuído junto com o jogo? Uso interno: não é necessário se preocupar muito com a interface, contanto que os desenvolvedores se sintam à vontade com ela. Distribuída com o jogo: é necessário que seja amigável e acompanhar manual (e talvez até tutorial). Os mapas são construídos através de tiles. Sprites também podem ser adicionados. Estruturas que representam a fase int fase[max_x][max_y]; Cada valor fase[i][j] da matriz representa um determinado tile na posição (i,j) do mapa. O tile ocupa o quadrado (i * W, j * H) - ((i+1) * W - 1,(j+1) * H - 1), onde W é a largura do tile e H sua altura (fixas). Simples e fácil de usar e salvar. Implementação direta. Pouco flexível para descrições mais complexas. Estruturas que representam a fase struct tfase fase[max_x][max_y]; Cada valor fase[i][j] da matriz contém uma estrutura que representa a posição (i,j) do mapa. Essa estrutura pode conter informações como a natureza do bloco na posição (sprite, tile etc.), largura, altura, estado inicial, propriedades especiais etc. Consumo maior de memória, mas ainda simples de implementar e usar. Cada matriz representará uma camada da fase. Diversas camadas podem trazer efeitos interessantes (por exemplo, para fazer um parallax scrolling). Camadas diferentes também podem ser usadas para sprites e tiles (usando, por exemplo, apenas uma matriz de inteiros). A matriz pode ganhar uma dimensão extra Exemplo: int fase[max_cam][max_x][max_y]; Formatos de arquivo O arquivo pode conter apenas a estrutura que representa a fase, gravada diretamente no arquivo. O arquivo pode conter todos os bitmaps usados na fase no mesmo arquivo. Desvantagem: bitmaps que apareçam em mais de uma fase estarão sendo gravados mais de uma vez em disco. Vantagem: todos bitmaps necessários para a fase são carregados de uma só vez. No último caso, a paleta também pode ser gravada. 6
Formatos de arquivo Em geral o arquivo deve ter um cabeçalho (com identificadores do arquivo e variáveis de controle para as estruturas da fase). O arquivo pode ou não ser compactado. Alguma flexibilidade e generalização são desejadas. Afinal, o editor pode vir a ser aproveitado para outros jogos no mesmo estilo (deve ser tão reaproveitável quanto a própria engine). Visualizar a fase. De preferência com alguma forma de rolagem (horizontal e vertical) suave para se visualizar qualquer área da fase. Deve ser possível adicionar novos blocos de arquivos de imagem ao conjunto de blocos disponíveis para a edição da fase. Os blocos disponíveis para usar na edição da fase devem estar bem visíveis e facilmente selecionáveis. De preferência aparecendo lado a lado com a fase. Edição. Obviamente o editor deve permitir que a fase atual possa ser modificada. De preferência de uma forma simples e rápida (mouse, teclas de atalho etc.) Deve ser capaz de trabalhar com múltiplas camadas. Cada camada deve poder ser editada em separado, embora seja também bom que possam ser vistas como um todo, sobrepostas. Em geral as camadas têm a mesma dimensão, mas isso não é obrigatório. Muitas vezes é importante que se permita colocar alguma espécie de grid para facilitar a edição. É importante que se use clipboard, para que se possa cortar e colar blocos, o que facilita o trabalho. O editor deve ser bem flexível. Principalmente se for feito nos estágios iniciais do desenvolvimento do jogo, pois muitas coisas definidas e certas podem mudar e isso evita ter de mexer no código do editor. Exemplo: tamanho de tiles, quantidade de cores etc. Possibilidade de trabalhar com mais de uma fase ao mesmo tempo também é interessante, assim como uma forma simples de se alternar entre uma e outra. Ferramenta de zoom é importante. Como a resolução do editor tende a ser maior que a do jogo, essa é uma característica que pode ser útil (ver partes com detalhes, ou ver a fase mais amplamente). Desfazer / Refazer (sempre importante). O editor deve poder trabalhar com tiles de diferentes dimensões e não apenas uma fixa (mesmo que dentro de uma determinada fase sempre tenham a mesma). Trabalhar com blocos especiais (tiles animados, sprites etc.) É muito importante ter um modo preview da fase (em tela cheia), movimentando-se com as setas e/ou com o mouse. Isso pode dar ao desenvolvedor uma boa sensação de como ficará a fase. 7
As estruturas dos sprites tendem a se complicar quanto mais diverso for o jogo. Diversos sprites diferentes povoam o mundo od jogo. Criar e configurar tudo no código pode ser um trabalho inviável. Editores de sprites facilitam a criação e edição dos sprites, assim como suas animações, características particulares etc. Em geral, editores de sprites são mais simples do que editores de fase. Editores de sprite e de fase podem ser um programa só. Facilita a comunicação entre a criação de fase e dos sprites. Permite que tudo seja feito num mesmo ambiente integrado. Pode ficar bem mais complexo de implementar e tende a ser mais difícil de se utilizar. Normalmente só é usado internamente pelos desenvolvedores. Tipicamente, a estrutura do sprite contém informações como: Posição atual (absoluta, relativa). Estados (pulando, correndo, invencível etc.) Escores (pontos, vidas, dinheiro etc.) Frames (bitmaps usados pelo sprite) Seqüências de animação. O arquivo gravado pode ser apenas de configuração de sprites (com as informações de cada tipo de sprite). O arquivo pode conter todos os bitmaps utilizados pelos frames dos sprites (e, neste caso, também a paleta se for o caso). O arquivo pode ser compactado. Visualização do sprite. Isso significa visualizar todos os frames de uma vez (ou o maior número possível) e poder selecionar um deles como atual para que fique em destaque. Deve ser possível adicionar novos frames ao sprite a partir de bitmaps em disco. Deve ser possível editar o bitmap do sprite (como num programa de edição de imagens). Deve ter algumas das funções básicas de um editor de imagens: Zoom Cortar / copiar / colar Ferramentas de desenho (ponto, retas, círculos, retângulos etc.) Flood fill Seleção de cores Edição de paleta 8
É muito importante que o editor de sprites permita a criação, visualização e edição de seqüências de animação a partir dos frames do sprite. A forma como essas seqüências serão gravadas irá depender do formato de estrutura de sprite utilizado pelo jogo ou pela engine dele. Deve haver um modo de preview das animações definidas para os sprites. Desfazer / refazer. O editor deve permitir que sejam definidos retângulos para uso de detecção de colisão (se o jogo ou sua engine usarem múltiplos retângulos para tal). Mesmo sem usar múltiplos retângulos, é uma boa idéia permitir que se defina o retângulo de colisão, pois pode ser desejado que a colisão não seja detectada em todo o sprite. Se for o caso, o editor de sprites também deve ser capaz de definir flags de colisão (por exemplo, dependendo da região onde acontece). Deve ser possível editar os padrões de comportamento do sprite, quando a estrutura dele assim permitir. Os sprites só são editados em família. Instâncias separadas de sprites do mesmo tipo são configuradas para aparecerem no editor de fases. Definição de cor de transparência (a não ser que seja sempre a mesma, como no Allegro). Outros Editores Editores de Enredo Em geral, para jogos de adventure. Scripts de comportamento (por exemplo, para diálogos) de NPCs (personagens não controlados pelo jogador) de acordo com a situação. Pode ser usado para interlúdios (histórias intermediárias que, em geral, aparecem entre fases). Definição de charadas, quebra-cabeças etc. Outros Editores Editores Específicos de Jogo (em geral para permitir maior interação do jogador) Em geral são editores de determinadas características dos jogos que permitem ao usuário personalizar tanto quanto queira. Exemplos: Editores de times e jogadores (não só características como até mesmo rostos) para jogos de esporte. Editores de personagens para RPGs. 9