Como fazer um Space Invaders - Parte 3
Na terceira parte da série nós finalmente vamos começar a programar, infelizmente ainda não vai ser uma programação "pesada", só vamos preparar o programa para que ele possa "suportar" o jogo em si. Ou seja, vamos criar um esqueleto para ele.
Módulo da Lib
Primeiro, vamos criar um programa que inicie a Allegro, entre num loop(que não faz nada) até que se aperte Esc, e depois feche. O importante aqui é ir programando e compilando a medida que se faz o código. Muitas pessoas quando começam a programar tendem a programar várias linhas de código e só depois compilar, mas isso é ruim porquê depois de muitas linhas de código se tiver um erro no seu programa(e provavelmente vai ter) fica díficil saber de one ele veio já que você programou muitas coisas de uma só vez.
Então agora nós só vamos fazer um programa que chama a Allegro e pronto. Mas é importante lembrar que nós vamos separar a Allegro em um outro módulo como eu havia dito. O porquê disso é devido ao fato de organização no código e também pra aumentar a facilidade de portabilidade. Se você depois quiser fazer esse programa em SDL ou outra lib, o jogo vai estar mais isolado da lib utilizando essa organização no código.
Na primeira parte da série eu falei que seria necessário um conhecimento de programação de jogos e como usar a Allegro, então pra quem não conhece existem bons tutoriais por aí sobre ela, mas se quiser continuar lendo sinta-se a vontade
Vamos criar então três arquivos:
- main.c
- libAllegro.h
- libAllegro.c
A main.c é o programa em si, que tera nossa função main() e que controlará todo o resto do jogo. O libAllegro.h e libAllegro.c são os arquivos que vão constituir o módulo da lib da Allegro.
Dentro do libAllegro.h, teremos o seguinte código:
-
-
#ifndef __libAllegro
-
#define __libAllegro
-
-
#include <allegro.h>
-
-
void inicia_allegro();
-
void finaliza_allegro();
-
-
#endif
Nesse arquivo não temos nada de muito especial, somente incluimos a allegro, e declaramos duas funções que serão programas no libAllegro.c, pelo nome das funções(inicia_allegro e finaliza_allegro) podemos supor que elas vão servir para iniciar e fechar a Allegro.
Agora vamos para o libAllegro.c programar cada função. Primeiro vamos programar a initAllegro, que será a função que irá inicializar a Allegro. Para inicializar a Allegro nós vamos utilizar o mesmo código que o Dev-C++ cria caso você use o devkpak da Allegro , segue o código comentado:
-
int depth, res;
-
-
allegro_init();
-
-
//Quantas cores o jogo tera
-
depth = desktop_color_depth();
-
if (depth == 0) depth = 32;
-
set_color_depth(depth);
-
-
//Resolucao da tela
-
res = set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
-
if (res != 0)
-
{
-
allegro_message(allegro_error);
-
exit(-1);
-
}
-
-
//Inicializacao de submodulos da Allegro
-
install_timer();
-
install_keyboard();
Bom, explicando por alto cada parte da função(já que o foco aqui não é Allegro) primeiro nós declaramos duas variáveis que serão utilizadas para armazenar os bits das cores(depth) e outra para verificar se podemos inicializar o programar com a resolução e o modo gráfico definido(res). Depois chamamos a allegro_init() para que a allegro se inicie e para podermos usar todas as outras funções da allegro.
Mais abaixo usamos a variável depth para receber quantas cores utilizamos no desktop, depois disso é feita uma verificação de erro e chamamos a set_color_depth(depth); Mais embaixo inicializamos o modo gráfico com janela na resolução de 640 por 480, mais adiante fazemos uma verificação de erro. E depois chamamos install_timer() e install_keyboard() para podermos usá-los mais tarde. Desculpem não explicar detalhadamente mas como disse acima, o foco não é a Allegro
A função finaliza_allegro() será uma função vazia, já que por hora não tem o que botar nela mesmo. Ela vai servir mais tarde para finalizarmos timers e outras coisas da Allegro.
O arquivo libAllegro.c ficará assim:
-
-
#include "libAllegro.h"
-
-
void inicia_allegro()
-
{
-
-
int depth, res;
-
-
allegro_init();
-
-
//Quantas cores o jogo tera
-
depth = desktop_color_depth();
-
if (depth == 0) depth = 32;
-
set_color_depth(depth);
-
-
//Resolucao da tela
-
res = set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
-
if (res != 0)
-
{
-
allegro_message(allegro_error);
-
exit(-1);
-
}
-
-
//Inicializacao de submodulos da Allegro
-
install_timer();
-
install_keyboard();
-
-
}
-
-
void finaliza_allegro()
-
{
-
-
}
Bom, falta só chamar essas funções no nosso arquivo main. A estrutura do main vai ser bem simples, ele precisa chamar nosso módulo da Allegro, chamar a função de inicializar, entrar num loop(essa parte do loop na verdade não precisa, mas eu deixei ela aqui para que a gente possa ver a tela preta), depois chamar a função deinitAllegro, e fim
-
-
#include "libAllegro.h"
-
-
int main()
-
{
-
inicia_allegro();
-
-
while (!key[KEY_ESC])
-
{
-
-
}
-
-
finaliza_allegro();
-
-
return 0;
-
}
-
END_OF_MAIN()
Se vocês compilarem o programa agora(é bom fazê-lo), ele irá rodar uma tela preta e quando você apertar Esc ele irá sair do programa.
Módulo Jogo
O módulo do jogo vai ser um dos mais importantes porque ele é um módulo central que controla todo o resto do jogo(por isso o nome dele :)). Vai ser um dos módulos onde nós mais vamos voltar e ficar adicionando/mudando código já que ele é o controlador de tudo, então a medida que formos criando as coisas, quem vai botar elas "no palco" vai ser esse módulo.
Primeiro vamos criar o jogo.h:
-
-
#ifndef __jogo
-
#define __jogo
-
-
extern int fimJogo;
-
-
void inicia_variaveis();
-
void processamento_jogo();
-
-
#endif
Ou seja, esse módulo agora não vai ter muita coisa. Só precisamos de uma variável fimJogo que vamos usar na main mais tarde, e duas funções, uma pra inicializar as variáveis relacionadas ao jogo, e a outra é o processamento do jogo em si. Vamos ver elas no jogo.c:
-
-
int fimJogo;
-
-
void inicia_variaveis()
-
{
-
fimJogo = 0;
-
}
-
-
void processamento_jogo()
-
{
-
}
Por enquanto esse módulo não tem nada de especial também, só atribuimos zero a variável fimJogo. Pode parecer inútil programar esses módulos sem nada só pra ter um programa que aparece uma tela preta, mas construir programas grandes é como se fosse qualquer projeto grande como por exemplo uma casa, primeiro se constroem os alicerces para depois ir "moldando" o resto em cima disso. E é assim que um programa grande funciona, embora no nosso caso o Space Invaders não seja necessariamente grande como os jogos de hoje em dia, ele já apresenta características de um jogo que precisa ser programado dessa maneira.
Se nós não programarmos o jogo projetando e tendo em vista o final dele, mais adiante muito do código terá que ser reescrito e isso além de ser trabalhoso faz com que o jogo fique com muitos bugs porque você quase sempre não se lembra algo que você programou no começo do jogo. Então se você está se perguntando o porquê de tudo isso, relaxe que mais adiante você vai entender
Agora o que nos falta é inserir isso na nossa main, o que eu basicamente vou fazer é chamar a função de inicializar as variáveis, mudar o while e inserir o processamento do jogo:
-
#include "jogo.h"
-
-
int main()
-
{
-
inicia_allegro();
-
-
inicia_variaveis();
-
-
while (!fimJogo)
-
{
-
//Input — Apagar depois
-
if (key[KEY_ESC]) fimJogo = 1;
-
-
processamento_jogo();
-
}
-
-
finaliza_allegro();
-
-
return 0;
-
}
-
END_OF_MAIN()
Bom, percebam que eu chamei a inicia_variaveis() logo após a inicia_allegro(), essa ordem é importante porque algumas vezes nós vamos precisar iniciar variáveis que são dependentes da lib que estamos usando, então essa função tem que vir depois da inicialização da lib.
Depois eu mudei o while de "while (!key[KEY_ESC])" pra "while (!fimJogo)". Eu fiz isso devido ao fato de que o jogo nem sempre vai fechar quando apertamos ESC, por enquanto ele só vai fechar nessa condição, mas mais adiante ele vai fechar no menu principal, quando apertamos esc, quando der algum erro, etc. Então por isso o loop principal do jogo depende da variável fimJogo.
Adicionei a linha de "if (key[KEY_ESC]) fimJogo = 1 para podermos fechar o jogo, mais adiante nós vamos deletar essa linha já que no lugar dela vai ser chamada uma função para controlar o input de todo o jogo, mas como nós ainda não criamos o módulo de input essa linha de código vai ficar aí temporiariamente para podermos fechar o jogo.
Logo após o nosso falso input eu chamo a função de processamento do jogo, embora não tenha nada, é essa função que vai mexer os objetos do jogo, detectar colisões, verificar quando a fase acabou, processar a IA dos inimigos, ou seja, tudo relativo ao jogo em si. Por enquanto vamos deixar ela vazia mesmo.
Finalizando
Bom, até agora você tem uma bela de uma tela preta! Mas isso é importante já que existem milhares de maneiras de fazer um programa que imprima uma tela preta, e principalmente milhares de maneiras de começar a programar um jogo. Mas eu estou fazendo isso pra que vocês vejam que é importante preparar tudo antes mesmo que o programa não faça nada. E não é só porque o programa não está fazendo nada, que o código por debaixo dele tem que ser ruim. O jogo tem que ser bom tanto para o usuário quanto para o programador.
Por enquanto vamos ficar só com esses dois módulos, no próximo será explicado o módulo de output o de objeto e canhão e input! Então se prepare!
Mas como vocês viram, montar um jogo é saber se preparar, entender os requerimentos dele e saber como programar isso efetivamente. Por isso eu frizei bastante nas 2 primeiras partes da série o planejamento, e como eu disse, é prática! Até a próxima ![]()
Padrões de Projetos para Jogos - Máquina de Estado
Primeiramente, gostaria de me desculpar pelo atraso, nós estavamos com sérios problemas com o sistema do WordPress pra publicar esse post(que era pra ter saído a umas 2 semanas!) Mas uma simples tag que tinha nele impedia a gente de postar, mas agora ajeitamos e aqui está mais um tutorial escrito por um convidado, Bruno Schifer, que mandou diretamente do fórum e está sendo publicado aqui.
Só lembrando que quem quiser participar também é só postar aqui.
E fiquem com o tutorial:
Nesse tutorial eu vou ensinar uma técnica de programação muito útil para o desenvolvimento de jogos.
Trata-se da máquina de estado que pode ser usada para controle da execução do seu jogo entre outras coisas.
Antes de mais nada, vamos chamar o objeto de estudo deste tutorial corretamente, pois o nome está incompleto.
As máquinas de estado são mais corretamente denominadas Máquina de Estado Finito ou em inglês “Finite State Machine” ou ainda melhor, FSM.
A utilização das FSMs no mundo da programação já é muito difundida e qualquer projeto mais complexo pode se utilizar desta técnica para resolver problemas comuns de lógica.
Com a utilização, foi-se criando um padrão de implementação para esse objeto e não é à toa que os autores Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, incluíram um padrão que pode ser usado para implementar a FSM em C++ no livro “Design Patterns: Elements of Reusable Object-Oriented Software”.
Eu já encontrei FSMs em implementações de jogos, algoritmos de inteligência artificial, algoritmos de parsing (compiladores) e em muitos outros lugares, ou seja, é muito importante para qualquer desenvolvedor de jogos entender o funcionamento das Máquinas de Estado Finito.
O estudo das FSMs é normalmente conhecido como a Teoria dos Autômatos ou ainda Teoria da Computação e ela é ensinada nas universidades para a implementação de compiladores e algoritmos de parsing, apesar de ter muitas outras aplicações.
Vamos então ao significado do termo: FSM é um modelo de comportamento composto por um número finito de estados, transições e eventos que geram essas transições.
Um modelo é uma interpretação ou abstração de uma realidade para que possamos compreender e utilizar o objeto com maior facilidade.
Portanto, a máquina de estados cria um modelo do comportamento de um objeto para que possamos utilizá-lo em nossas aplicações.
Por número finito de estados queremos dizer que nós podemos contar quantos estados a máquina possui.
Logo, esse modelo de comportamento pode assumir um número limitado de estados.
Com base nisso, nós podemos representar uma FSM através de um “diagrama de transição de estados” ou através de um “diagrama de estados” definido na UML.
Não vou me aprofundar nos diagramas citados acima, pois eles não entram no escopo deste tutorial.
O que eu vim aqui mostrar é uma implementação de uma FSM utilizando o State Pattern definido no livro de Padrões de Projetos do Erich Gamma.
Vamos ao exemplo…
Simplificando o modelo, vamos imaginar um jogo qualquer bem simples. A minha idéia é criar um jogo que possua uma tela de apresentação, uma tela de menu e uma tela com o jogo em si. Dessa forma, concluímos que o número de estados dessa aplicação é 3 (três).
As transições entre os estados são acionadas a partir de certos eventos. Vamos tentar relacioná-los aqui:
Quando o jogo entra na tela de apresentação não é possível que o jogador saia da tela. Ele terá que aguardar 5 segundos para que ocorra a primeira transição de estado. Após os 5 segundos, um evento ocorrerá e a transição para o estado de menu será acionada. Portanto, o primeiro evento de transição é o tempo alcançar 5 segundos após a entrada na máquina.
Dentro do estado de menu, o jogador pode sair da aplicação pressionando o botão saída ou pode entrar no estado jogo, pressionando o botão jogar. As transições nesse estado ocorrem somente com o pressionamento de dois botões.
Por último, o jogador estando dentro da tela de jogo, pode sair da mesma pressionando o botão ESC no teclado. Ao sair desse estado, a máquina volta para o estado menu.
Vamos visualizar em um diagrama de estados da UML o modelo de comportamento desse jogo:

Com esse diagrama, conseguimos ver exatamento o comportamento do jogo mapeado em um modelo de FSM.
Após ter definido nosso modelo, podemos começar a implementar.
Como meu objetivo é mostrar a implementação do State Pattern, não vou apresentar uma aplicação gráfica aqui. Tudo será implementado no console do DOS.
Ao código então…
Crie um projeto vazio com o nome de MaquinaEstado no Dev-C++.
Acrescente os seguintes arquivos no projeto:
- Main.cpp
- Global.h
- Maquina.cpp
- Maquina.h
- Estado.cpp
- Estado.h
- Estados.cpp
- Estados.h
Pronto, agora pegue o arquivo Main.cpp e inclua o seguinte código nele:
| #include “Global.h”int main() { // Loop principal do jogo while(1) { // Implementação dos if(kbhit()) { int tecla = getch();if(tecla == 27) // tecla { break; } } //Limpa a tela // Desenha na tela printf(”Loop”); } return 0; } |
| #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h> |
Como eu disse, não estaremos usando nada gráfico aqui.Compile o programa e execute. Esse programa é um modelo simplificado de jogo. Você pode observar no código de Main.cpp o loop principal da aplicação que espera entradas no teclado, comando kbhit() e getch(). Caso ocorra algum pressionar de botão, o programa armazena a tecla pressionada na variável específica e caso a tecla pressionada seja um ESC, o loop é interrompido com o comando break.Mais a frente no código, podemos encontrar um comentario simulando a limpeza no buffer da tela, que poderia ser feito com uma chamada ao sistema operacional, usando system mais um cls, e podemos encontrar o comando printf() que simula o desenhar da tela. Tudo isso dentro de um modelo simplificado que tem como objetivo excluir a complexidade do desenvolvimento de um jogo para que possamos focar o problema na implementação de uma FSM.No código de Global.h você pode encontrar as bibliotecas padrão do C que estamos usando nessa aplicação.Com essa aplicação, nós já podemos receber 3 dos eventos que acionam as transições de estado da máquina:- Pressionar da tecla “j” para jogar - Pressionar da tecla “q” para sair - Pressionar da tecla “ESC”Está faltando somente um dos eventos relacionados anteriormente no tutorial:- limite de tempo de 5 segundos alcançado após a entrada da aplicaçãoAcrescente o código em vermelho na função main para podermos monitorar o tempo:
| #include “Global.h”int main() { // Tempo time_t timer_start; time_t timer_current;timer_start = time(NULL);double diff = 0; // Loop principal do jogo while(1) { timer_current = time(NULL); diff = difftime(timer_current, timer_start); if(kbhit()) { int tecla = getch(); if(tecla == 27) { break; } } // Desenha na tela printf(”Loop: %4.2f\n”, diff); } return 0; } |
Nesse modelo, a classe Maquina mantém uma instância da classe Estado e a utiliza para executar operações específicas ao estado do jogo.Toda vez que um estado muda, ou melhor, quando ocorre uma transição de estados, a instância de Estado da classe Maquina muda.Primeiramente, vamos observar o código da classe Estado, pai de todos os estados do jogo. Abra o arquivo Estado.h e acrescente o código abaixo: | #ifndef ESTADO_H #define ESTADO_Hclass Estado { public: // Eventos de mudança de virtual void AoPressionarJo virtual void AoPressionarSa virtual void AoPressionarES virtual void AoTerminarTemp virtual void AoEntrar() {};// Executa um frame de virtual void ExecutaFrame( virtual void AoSair() {}; protected: void ExecutaTransicao };#endif |
| #include “Global.h”void Estado::ExecutaTransicao { maquina->ExecutaTransicao } |
| #ifndef MAQUINA_H #define MAQUINA_Hclass Estado;class Maquina { private: // Instância única da static Maquina* m_pInstanci class Estado* m_pEstadoAtua bool m_Finalizar; public: // Construtor da classe Maquina();// Cria a instância única static Maquina* CriaInstanc void ExecutaFrame();// Executa transição de void ExecutaTransicao void Finalizar();// Pergunta se pode bool PodeFinalizar();// Eventos de mudança de void AoPressionarJogar(); void AoPressionarSair(); void AoPressionarESC(); void AoTerminarTempo(); };#endif |
| #include “Global.h”Maquina::Maquina() { m_pEstadoAtual = 0; m_Finalizar = false; }// Definição do atributo Maquina* Maquina::m_pInstancia Maquina* Maquina::CriaInstancia { if(m_pInstancia == 0) { m_pInstancia = new Maquina }return m_pInstancia; }// Executa uma transição de void Maquina::ExecutaTransicao { // Executa o evento AoSair() if(m_pEstadoAtual != 0) { m_pEstadoAtual->AoSair(); }m_pEstadoAtual = estado;// Executa o evento AoEntrar m_pEstadoAtual->AoEntrar(); }// Executa um frame de animação void Maquina::ExecutaFrame() { m_pEstadoAtual->ExecutaFrame }void Maquina::Finalizar() { m_Finalizar = true; }bool Maquina::PodeFinalizar() { return m_Finalizar; }// Eventos de mudança de estado void Maquina::AoPressionarJogar { m_pEstadoAtual->AoPressionarJ } void Maquina::AoPressionarSair void Maquina::AoPressionarESC() void Maquina::AoTerminarTempo() |
| #ifndef ESTADOS_H #define ESTADOS_Hclass EstadoApresentacao : { private: // Instância única da static EstadoApresentacao* public: // Cria a instância única static EstadoApresentacao* void AoTerminarTempo };class EstadoMenu : public { private: // Instância única da static EstadoMenu* m public: // Cria a instância única static EstadoMenu* CriaInst void AoPressionarJogar void AoPressionarSair };class EstadoJogo : public { private: // Instância única da static EstadoJogo* m public: // Cria a instância única static EstadoJogo* CriaInst void AoEntrar(); void ExecutaFrame(); void AoSair(); // Eventos de mudança de #endif |
| #include “Global.h”EstadoApresentacao* EstadoApres { if(m_pInstancia == 0) { m_pInstancia = new EstadoAp }return m_pInstancia; }void EstadoApresentacao: { }void EstadoApresentacao: { printf(”\nApresentacao\n”); }void EstadoApresentacao: { }void EstadoApresentacao: { ExecutaTransicao(maquina, }EstadoMenu* EstadoMenu::m { if(m_pInstancia == 0) { m_pInstancia = new EstadoMe }return m_pInstancia; }void EstadoMenu::AoEntrar() { } void EstadoMenu::ExecutaFrame() void EstadoMenu::AoSair() void EstadoMenu::AoPressionarSa void EstadoMenu::AoPressionarJo EstadoJogo* EstadoJogo::m EstadoJogo* EstadoJogo: return m_pInstancia; void EstadoJogo::AoEntrar() void EstadoJogo::ExecutaFrame() void EstadoJogo::AoSair() void EstadoJogo::AoPressionarES |
| #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h>#include “Maquina.h” #include “Estado.h” #include “Estados.h” |
Como fazer um Space Invaders - Parte 2
Desculpem o atraso pessoal, é que estamos tendo problema com o sistema do WordPress e o post que era pra ter sido postado (do Bruno Schifer) teve que ser adiado, então esse imprevisto com o sistema acabou me pegando de surpresa pra escrever esse texto(ainda faltava preparar umas coisas!). Mas aqui está:
Parte 2
Bom, nessa segunda parte do artigo vamos fazer as preparações para podermos começar a programar baseado com a descrição do jogo que foi feita na primeira parte da série.
Módulos
A maneira que irei usar para programar aqui vai ser dividir em módulos o que tinhamos definido na primeira parte da série e algumas coisas especiais que vamos usar pra programar. Na primeira parte nós definimos que iriamos ter os seguintes atores no jogo:
- Canhão
- Naves
- Base(barreira)
- Tiros
Definimos também as variáveis que eles irão usar, se vocês prestaram atenção eles dividem variáveis em comum, como x e y, flag de vivo ou morto, e frame atual de animação. No caso o único que é diferente é a barreira. Então nós iremos criar uma estrutura comum para o Canhão, Naves e Tiros. Vamos chamar ela de estrutura objeto. Nós iremos definir as variáveis dela na próxima parte da série.
Vejam que mesmo definindo que iremos criar uma estrutura em comum para eles, cada um tem um movimento diferente, tem uma imagem diferente, atira de maneira diferente e é representado com uma estrutura de dados diferente(o Canhão é uma estrutura normal, as naves são uma matriz de uma estrutura, e os tiros uma lista, ou vetor de uma estrutura de tiros). Então apesar de cada um dos três dividir a estrutura de objeto, cada um tem uma peculiaridade, ou seja, vamos precisar de um módulo para cada um também.
Quando eu falo a palavra módulo, eu me refiro nada mais que um .h e um .c que contém somente coisas relacionadas ao ator que estamos programando. É quase igual a um objeto de POO(só que sem as vantagens de POO). Com o que definimos até agora vamos precisar de um módulo para o Canhão, outro para as Naves outro para a Barreira outro para os Tiros e mais um para a estrutura de objeto. A idéia desses módulos é que (como objetos de POO) eles são “isolados” o máximo possível para que qualquer alteração no jogo, não precise alterar muitos módulos, mas sim o módulo em questão. Ou seja, vamos tentar fazer com que eles sejam independentes um do outro o máximo possível. Não é 100% possível de fazer isso porque geralmente um utiliza variáveis e estruturas dos outros e alguns vão ser por natureza totalmente dependentes de um outro, como por exemplo a estrutura objeto que é uma estrutura “mãe” da estrutura de canhão nave e tiros. Se vocês não estão entendendo muito bem isso tudo, fiquem mais calmos que quando vocês começarem a programar (na próxima parte da série) vai tudo ficar mais claro.
Outros módulos que vamos precisar vão ser os módulos de output, input, o módulo jogo e mais um módulo para a lib que vamos usar. Esse módulo da lib vai servir pra que possamos tentar separar algumas funções da lib que vamos usar no jogo, pra ficar mais fácil pra quem usa SDL ao invés de Allegro.
Resumindo nós temos os seguintes módulos:
Lib, módulo que terá funções para inicializar/finalizar a lib utilizada.
Objeto, módulo que irá conter estrutura variáveis e funções para manipular objetos(qualquer coisa que se mova) no jogo.(É nesse módulo que vamos criar a função da colisão)
Tiros, módulo que será dependente do módulo objeto e ira conter estruturas de dados, variavéis e funções para manipular os tiros das naves inimigas e do jogador.
Nave, módulo que será dependente do módulo objeto e do modulo tiro e irá conter estrutura variáveis e funções para manipular as naves inimigas do jogo(com base no que tinhamos planejado na série passada, é aqui que vai ter o algoritmo de movimento das naves e o algoritmo de “IA” para atirar)
Canhao, módulo que será dependente do módulo objeto e módulo de tiros, irá conter estrutura variáveis e funções para manipular o canhão do jogo
Barreira, módulo que irá conter estruturas e variáveis para manipular a barreira.
Input, módulo que irá controlar as reações do teclado.
Output, módulo que irá conter funções para controlar audio e video.
Jogo, módulo chave que irá utilizar todos os outros módulos para fazer o jogo funcionar.
Ordem de desenvolvimento
Bom, com base nisso iremos criar a ordem de como iremos programar tudo. De acordo com o que eu tinha dito na parte passada da série, a ordem com que vamos programar tudo depende das dependências(!). Mas nesse caso não são necessariamente as dependências dos módulos. Vejam que o módulo de tiro depende do de objeto, mas ele vai ser um dos ultimos a ser programado já que (como eu tinha dito) se criarmos ele primeiro não iria adiantar muito já que pra um tiro ser disparado precisa de algo que o dispare!
Então a ordem vai ser algo mais ou menos assim:
1º Criar o módulo da Lib
2º Criar o módulo do Jogo
3º Criar o módulo de Output
4º Criar o módulo Objeto e Canhao
5º Criar o módulo de Input
6º Criar o módulo das Naves
7º Criar o módulo dos Tiros
8º Criar o módulo da Barreira
Acredito que a ordem será essa, mas notem que criar um módulo não significa necessariamente que vamos criar ele e nunca mais iremos programar algo nele de novo. Muito pelo contrário, todo módulo que vamos criar vai ser um “esqueleto”, ou seja, no começo não vai ter muita coisa, só o suficiente pra criar as dependências e não precisar ficar reprogramando tudo utilizando Ctrl+C e Ctrl+V ou algo que o valha.
Finalizando
Com isso concluímos essa segunda parte da série. A partir da próxima se preparem que vamos começar a programar ![]()
Tutorial Básico de Iluminação
Estamos inaugurando o espaço do convidado aqui no PDJBlog. Esse espaço nada mais é do que um espaço onde alguém que não faz parte do time de escritores do Blog escreve um artigo ou tutorial e nós decidimos postar aqui. Em torno de uns 2 ou 3 dias iremos explicar no fórum como proceder.
O escritor de hoje é Marcelo Prado, ou akigames como é conhecido na PDJ. Ele é um dos usuários “das antigas” que com bastante esforço chegou longe na área onde ele queria. É só visitar o site dele: http://marcelo3d.vilabol.uol.com.br/ pra conferir.
Espero que o pessoal se motive vendo como dá pra ir longe com esforço e foco
Light Dome com Scanline Render do 3Ds MAX
Temos aqui a nossa cena renderizada sem qualquer configuração de Iluminação. Uma cena simples com três esferas com Raytrace Material e um plano com um material Standard.
Primeiro criamos uma Spot na TOP View, de forma que o target esteja no centro de nossa cena. Evite deixar a Spot muito próxima da cena. Em seguida desligue o target da Spot e e re posicione o seu PIVOT para o centro da cena. Isso ajudará na hora de copiar a Spot.
![]() |
![]() |
Iremos criar um total de 11 instâncias dessa Spot ao redor da cena a cada 30 graus, formando um circulo de Luzes em volta da cena.
![]() |
![]() |
![]() |
Iremos Criar mais 3 instâncias da Spot na vista Front no eixo Z. E depois criar mais 11 instâncias de cada Spot Selecionado na Imagem acima no eixo X. Dessa forma teremos nosso Light Dome Pronto. Só faltando assim um ajuste na fileira central de luzes para interpola-las e ter um sombreamento mais suave.
Teremos assim o seguinte resultado ao renderizar.
Precisamos agora configurar as Spots para obtermos o uma Iluminação adequada e sombras difusas. Para esse caso usei a seguinte configuração:
Shadow “on”: Shadow Map
Multipler: 0,05
Specular: Desligado
Shadow Dens.: 1,55
Bias: 0,1
Size: 120
Sample Range: 6
Aqui vai uma pequena explicação do que cada um desses parâmetros.
Shadow Type “Shadow Map”: O Max irá gerar as sombras na cena a partir de um BITMAP criado pelo próprio Max com a silhueta do Objeto. Muito rápido e leve, porém não detecta transparência.
Multipler: Controla a intensidade da Luz.
Specular e Difuse: Controla quais propriedades do Shader a luz irá afetar. Por padrão a luz afeta o Difuse e o Specular do Shader. No nosso caso para o Light Dome desligamos o Specular para que a Luz afete somente o Difuse do Shader iluminando a cena. Caso contrário teríamos várias bolinhas rodeando todos os Objetos com Shaders que possuam Specular.
Shadow Density: Controla a densidade da sombra. Quanto maior a densidade mais escura a sombra tende a ser.
Bias: Controla a distancia da sombra do Objeto que está emitindo-a.
Size: Já que o Shadow Map se basea de um BITMAP calculado pelo Max com a silhueta do Objeto, nesse campo você controla a resolução desse Bitmap. Com um Size de 1024 você terá um BITMAP de 1024×1024 para gerar a sombra. Quanto maior a resolução maior a qualidade e nitidez da sombra.
Sample Range: Aqui você controla a quantidade de “Blur” que você quer no seu BITMAP. Quanto maior o valor do Sample Range mais borrada será a sombra.
Teremos o seguinte resultado. E cadê o Specular da cena? Já que não temos nenhuma Luz gerando Specular na cena vamos criar uma.
Iremos criar uma Omni na cena seguindo as configurações da imagem acima. Logo em seguida com a Omni selecionado clicaremos em Align > Place Highlight. Ao passar o Mouse em cima de umas das esferas aparecerá uma setinha Azul. Essa setinha indica onde a Omni ira gerar o Specular. Com essa ferramenta o Max irá posicionar a Omni no lugar certo para gerar o Specular onde a setinha azul indica. Dessa forma você pode posicionar o Specular da forma que achar melhor para a sua cena.
Agora nossa cena possui Specular.
![]() |
![]() |
Para finalizar iremos criar a radiosidade vinda do piso. Selecione as Spots que se encontram no topo do Light Dome e crie uma cópia (Cópia, não Instância) e posicione as como na imagem acima. Não iremos mexer muito nessas luzes, apenas desligar a sombra e mudar a cor para uma cor próxima com a cor do piso. Para isso você pode pegar o render e clicar com o botão direito do Mouse. Dessa forma o Max irá criar uma cópia do pixel no qual você clicou em um quadrado de cor que encontrasse no topo da janela do renderizador. Assim você pode copiar essa cor para as luzes que irão gerar a radiosidade.

Render Final com Scanline Render e Light Dome.
Sky Light com Mentalray 3.5 no 3Ds MAX
Basicamente iremos manter tudo que fizemos na cena anterior. Iremos apenas substituir as Spots do Light Dome por uma simples Sky Light. Você pode posicionar a Sky Ligth em qualquer lugar da cena que o resultado será o mesmo. Após criar a Skylight va na janela do Renderizador e na guia “Indirect Illumination” ligue o Final Gather. Para testar você pode usar o preset DRAFT. E para render final você pode usar os presets Medium e High.

Render Final com Skylight no Mentalray
Como fazer um Space Invaders - Parte 1
“Rather than the programmer saying how about like this, he keeps making the kind of environment which the designer can adjust to whatever their heart’s desire.” - Takuya Seki, Shadow of the Colossus
Essa série é um conjunto de textos com instruções para se programar do zero um Space Invaders. Notem que eu estou usando o termo programar, mas eu vou mencionar uma pequena parte de Game Design, especificamente a parte onde o design se transforma em programação. Isso parece ser bobo mas é um dos (vários) motivos pra um projeto de game design que é bom não dar certo. Os gráficos vão ser uma cópia do original com umas mudanças, o bom de fazer clones de jogos antigos é que os gráficos são simples ;). Os sons vão ser feitos usando um programa free que eu vou mencionar mais adiante nas próximas partes da série.
Para quem essa série é indicada
É essencial que você saiba programar e já tenha tido alguma experiência com programação de jogos antes, no caso, saber usar/criar headers, ponteiros, conhecer algumas estruturas de dados simples, saber o que é game loop, double buffering e etc. Ou seja, se você sabe programar e já programou algum jogo antes mas não acabou, ou teve dificuldades, ou pelo menos sabe usar alguma lib tipo Allegro ou SDL essa série será útil. Se você está aprendendo a programar pode tentar acompanhar já que eu vou dar umas dicas de práticas de programação que não precisam de muito conhecimento, são métodos de programar que eu considero importantes. Eu irei usar C(sem POO) com Allegro, mas o jogo será programado de uma maneira tal que será fácil portar o jogo para outras libs.
Primeiros Passos
No desenvolvimento de um jogo tradicional existem muitos processos a serem feitos até que efetivamente se comece a programar. Quando o motivo incial de fazer um jogo não é a tecnologia dentro dele e sim alguma idéia, se começa pelo design do jogo a partir dessa idéia.
Resumindo um pouco, a partir de uma (ou mais) idéia(s) o game designer vai moldando o jogo na cabeça dele, pensando em alguns detalhes do jogo, mas não nele todo, a partir disso são criados alguns protótipos da funcionalidade do que ele tinha pensado, e a partir disso é definido como o jogo será, ou seja, o design doc. Notem que alguns design docs ainda deixam espaço para experimentação. Nesses casos ele não é um documento que contém TUDO do jogo, já que seria complicado mudar alguma coisa no código caso essa coisa não tenha ficado boa como ele tinha pensado(isso acontece muito).
Mas o importante do design doc é que ele seja um guia para as outras pessoas da equipe(no nosso caso, o programador) para elas saberem o que elas tem que fazer. É importante existir uma comunicação entre o designer e o programador sobre essas partes que podem mudar, já que as vezes é complicado deletar tudo que já tinha sido feito e reprogramar do zero. Quando o programador sabe de antemão quais coisas podem vir a mudar no futuro, ele prepara o código para que ele seja facilmente modificavel. Sabendo disso vamos criar nosso design doc simplificado!
“Design Doc” simplificado
Como nós não estamos criando um jogo do zero e sim um clone, o design doc está praticamente pronto, ou seja, a gente já sabe tudo que o jogo tem, seria só passar pro papel e em teoria começar a programar… mas como a vida nunca é facil, não é assim que nós vamos começar.
Isso acontece porque mesmo se a gente jogasse o jogo todo dia e conhecesse tudo a respeito dele, nós estariamos pensando como jogadores e não como programadores. É muito fácil achar que um jogo é fácil de programar só porque ele parece simples, mas a verdade é outra… SEMPRE tem mais do que parece ter por trás de um jogo, e é isso que eu vou mostrar aqui.
Bom, supondo que todos que estão lendo sabem o que é o space invaders a gente pode fazer um design doc do jogo:
Obs: Eu estou chamando de Design Doc, mas é só uma descrição do jogo pra gente ter idéia de como ele funciona, o que existe dentro dele, quais as regras e etc, ao longo do texto vou usar design doc e descrição mas me refiro a mesma coisa.

Em Space Invaders você controla um canhão que defende a terra de invasores do espaço. O canhão fica na parte inferior da tela, ele pode se mover para esquerda e para a direita. Ele se defende atirando contra os invasores.
Os invasores se movimentam para direita ou para a esquerda. Quando um dos flancos toca um lado da tela, eles descem uma linha e movem para o lado oposto:

Os invasores também se defendem atirando contra o canhão. Eles se movem mais rápido a medida que vão sendo destruidos(mais adiante vai ser explicado como eles são destruidos). Existem 3 tipos de invasores(
), e eles ficam organizados como uma matriz.
Eventualmente aparece um nave em cima(
) que se move para o lado oposto da tela e dá pontos especiais para o jogador.
Os tiros do jogador, se acertam a base, destroem ela parcialmente, se acertam uma nave inimiga destroem ela imediatamente.
Os tiros das naves não acertam naves, mas acertam a base, destruindo ela parcialmente, e quando acertam o canhão o jogador é destruido e ele perde uma vida.
O jogador passa de fase quando todos os invasores da matriz morrem, a cada fase que ele passa os invasores começam uma linha abaixo.
O jogador perde o jogo quando ele perde todas as vidas dele(ele começa com 3).
As fases são infinitas.
Bom, tá meio bagunçado mas descrevemos o jogo, certo? Em partes
Isso é o que passa pela cabeça de um jogador quando ele descreve o jogo(e na de Game Designers que ainda estão aprendendo), mas falta algumas informações cruciais aí. Por exemplo, eu falei que “Eventualmente aparece uma nave em cima que se move para o lado oposto da tela e dá pontos especiais para o jogador“. Ok, mas, quais são os critérios para a nave aparecer? Ela aparece sempre pela esquerda? Ela aparece sempre pela direita? Se o jogador estiver mais pra direita ela aparece na direita e vice-versa? Ela aparece depois de um determinado tempo? Ela aparece depois de um número de tiros? Só jogando o jogo pra saber, e mesmo assim você ficaria na dúvida.
Mas vamos supor que Space Invaders é um jogo que ainda está sendo criado, que o game designer dele não sabe disso também, ou seja, ele simplesmente teve uma idéia de que seria legal uma nave aparecer em cima dando pontos especiais(geralmente quando a gente tem essas idéias legais, elas sempre são vagas em termos de programação). O game designer vai querer experimentar varias opções durante o jogo, ele vai querer experimentar todas aquelas opções que eu disse acima e mais o que a cabeça dele puder imaginar. você, como programador, na hora que estiver modelando o programa tem que identificar essas partes vagas de um documento de design e conversar com o designer do jogo sobre isso, pra que você não comece a programar algo que seja da sua cabeça, e quando o game designer ver que aquilo não era o que ele tava pensando você vai ter que deletar tudo porque não preparou o código pra mudanças.
Com base nisso vou mostrar aqui algumas coisas que ficaram faltando e outras que o game designer vai querer experimentar:
Canhão:
- Qual a posição inicial do canhão?
- O que acontece quando o jogador leva um tiro? Ele perde uma vida e fica quanto tempo sem se mexer? Ele fica invencível por um tempo? Por quanto tempo?
- Quando ele leva um tiro ele volta pra posição inicial?
- O que acontece quando ele toca um extremo da tela? Ele aparece no outro extremo? Ele para de se mexer?
- Ele tem tiros infinitos? Se tem, ele pode atirar uma vez até acabar o tiro ou quantas vezes ele quiser?
Invasores:
- Como eles ficam organizados? Qual é o tamanho da matriz?
- Qual a velocidade deles? Como é a progressão da velocidade a medida que eles vão morrendo?
- Qual é o critério pra eles atirarem?
Barreira:
- Como que a barreira vai sendo destruida? Ela tem seções que aguentam por exemplo 3 tiros? Ou ela é destruida por terreno(tipo worms)?
Pois é, quando a gente descreve o jogo, ou tem idéias pra um jogo, esse tipo de coisa passa batido, mas quando a gente para pra pensar nesses detalhes que ficam faltando acaba aparecendo um monte de coisas. Mas o importante é que isso apareça AGORA e não quando nós começarmos a programar. Eu vou responder todas essas perguntas agora já que nós estamos programando um clone, mas eu fiz isso porque eu quero que vocês vejam que quando nós estamos criando um jogo do zero, esse tipo de pergunta aparece, e as vezes a gente não sabe como responder e tem que experimentar. E no final das contas é o programador que tem que programar o jogo sem essas respostas, por isso ele tem que saber aonde que o designer quer experimentar pra que ele possa fazer um código flexivel o suficiente pra que possa haver espaço para essas experimentações.
Mas voltando as perguntas:
O canhão começa na esquerda mais ou menos em uns 20% da tela. Quando ele leva um tiro o jogo para e espera até o jogador apertar o botão de atirar e ele volta a posição inicial dele, mas os invasores continuam na posição aonde eles estavam. Quando o canhão se move até um extremo da tela ele para e não se move mais. Ele tem tiros infinitos e só pode dar um tiro por vez.
Os invasores se organizam em uma matriz com 11 colunas e 5 linhas. A velocidade inicial e a progressão é algo que nós vamos ter que experimentar
Mas no jogo, quando sobra o ultimo invasor aparentemente a velocidade dele dobra quando ele se move pra direita, quando ele se move pra esquerda ela diminui um pouco(acho que é pro jogador ter mais chance, fica realmente muito rápido). O critério pra eles atirarem me parece ser algo assim: nas primeiras fases um deles atira aleatoriamente, enquanto o outro tiro é sempre da nave que está na coluna em que o jogador está, a medida que as fases vão passando vai aumentando o número de tiros aleatórios.
A barreira é destuida por terreno, de acordo com a animação. No jogo original, a barreira era “deletada” de acordo com a animação da explosão, por exemplo: Um tiro acertava a barreira(a colisão aparentemente é pixelperfect), o tiro virava uma explosão, o desenho dessa explosão simplesmente sobrepõe o que tinha sido desenhado na barreira(isso será explicado em detalhes nas próximas partes da série).
Bom, adicionando isso tudo ao “design doc” inicial, já temos coisa suficiente pra planejar algumas coisas de como iremos programar e a ordem de como iremos programar o jogo.
Pensando como programador
Primeiro, nós temos que identificar quantos objetos nós temos. Pela imagem(e pelo que a gente conhece do jogo) a gente sabe que tem o canhão, os inimigos, as 4 bases que defendem o jogador, tem também aquela nave que aparece em cima para dar pontos extras. Com isso nós já detectamos que vamos criar uma estrutura para o jogador, outra para as naves, e mais uma para as barreiras que ficam embaixo. Mas nós temos que lembrar que o jogador atira e as naves de cima também! Ou seja, mais uma estrutura para o tiro. Resumindo nós temos:
- Canhão(jogador)
- Naves
- Base
- Tiros
Note que poderiam ser 4 classes, mas não importa muito já que nós vamos separar por módulos cada estrutura, então mesmo não usando OOP vai ficar muito parecido já que estamos modelando o programa da mesma maneira, a diferença vai ser na hora de representar no código.
Depois nós temos que identificar os comportamentos, ou seja, o que cada um pode fazer e pensar em como isso pode ser programado:
- O canhão atira, leva tiros e se move.
- As naves atiram, levam tiros, se movem.
- A barreira somente leva tiros(coitada hehehe).
- Os tiros se colidem com outros tiros, com o canhão, com as naves e com a barreira.
Aparentemente é simples, mas agora é que vai ficar complicado pois nós vamos refinar isso. Não se espante se você não entender porquê vamos usar tal variável ou tal algoritmo, isso tudo será explicado nas seções mais a frente destinadas a programar cada parte do jogo, mas é importante fazer esse tipo de verificação agora para que não aconteçam imprevistos no futuro. E como que você vai saber qual algoritmo usar em determinada hora? Bom, isso é experiência, e é pra isso que você está lendo esse artigo
Canhão:
Nós sabemos que ele dá um tiro até atingir o final da tela ou algum outro objeto. Pra isso vamos precisar de alguma variável que indique se o jogador está atirando ou não.
O canhão se move para esquerda e para direita de acordo com o que o jogador aperta no teclado, isso é bem simples, vamos criar só uma váriavel X e Y e uma Velocidade pra podermos fazer isso.
Quando o jogador leva tiros ele simplesmente é destruido, então uma colisão de bounding boxes nesse caso será útil.
Naves Invasoras:
As naves atiram de acordo com aquele critério mencionado acima, teremos que criar uma função que terá um algoritmo que decidirá quais naves irão atirar.
A colisão delas é parecida com a do canhão, bounding box já que elas são destruidas na hora.
O movimento dos invasores é de acordo com o visto na imagem acima, iremos criar uma função pra mover também de acordo com o que sabemos.
Notem que nós pensamos em criar uma estrutura nave, e de fato nós vamos criar, mas o que importa para nós é o conjunto das naves, ou seja, a matriz! No caso os inimigos vão ser uma matriz da estrutura nave.
Base:
A base só leva tiro, e a colisão é como explicamos acima, o algoritmo dela é meio complicado e será explicado mais adiante, mas o que nós precisamos saber agora é como representar essa base, a base nada mais vai ser do que um bitmap, sem energia sem nada, só vamos precisar do X e Y dela pra poder calcular a colisão.
Tiros:
Os tiros se movem de acordo com quem atirou, por exemplo, se o tiro foi do canhão, o tiro sempre se move pra cima. Se o tiro foi de alguma das naves ele vai se mover sempre pra baixo. A função de movimento deles é bem simples. A colisão também é feita com bounding box(com excessão da colisão de tiro com a barreira).
Uma regra geral pra caso você não saiba que algoritmo usar ou como representar tal objeto é, nessa hora do planejamento, fazer experimentos com partes do programa. Por exemplo, digamos que você ficou em dúvida sobre como fazer o movimento das naves, então crie um programa que SOMENTE tenha essas naves se movendo. Parece uma coisa simples mas isso envolve pesquisa e experimentação, até porquê nem todo programador sabe tudo, então quando um programador começa um projeto grande que ele não sabe como programar determinadas coisas, é nessa hora de planejamento que ele faz essa experiências, demos e pesquisas. Isso é super importante, e é um dos motivos que diferencia um programador preparado de um programador amador que programa “na doida”.
Bom, podemos já ter uma idéia do que vamos programar e como vamos programar. Agora nós precisamos decidir o que vamos programar primeiro. Isso muda de pessoa pra pessoa e de jogo pra jogo, acreditem. Mas como nós estamos programando jogos pequenos e já sabemos o que nós queremos, nós podemos usar uma regra bem simples que se aplica ao nosso caso: Comece programando o que não tem dependências. Ou seja, não dá pra começar programando a colisão se nós não temos ainda as estruturas que vão fazer essa colisão, não dá pra programar o algoritmo de mover as naves se nós ainda não fizemos ela nem desenhamos elas na tela! A idéia é simples.
No nosso caso, eu vou começar programando o canhão e a função de movimento dele. Depois nós poderiamos fazer a função de atirar, mas ia ser ruim porque depois nós iamos ter que programar as barreiras, as naves e depois voltar pra acabar os tiros! Porquê se nós fossemos programar os tiros do canhão logo após termos criado ele e sem nada no jogo, não teria com o quê colidir, então não faria muito sentido. Ou seja, depois de programar o canhão e o movimento dele, vamos criar as naves e o movimento delas, depois a barreira, depois os tiros e por aí vai. Vocês verão mais adiante na proxima parte da série, como isso será feito, por enquanto fiquem com isso que já é até bastante informação =)
Considerações Finais
A primeira parte acaba por aqui, ná próxima nós vamos montar o esqueleto do jogo e programar o canhão. Espero que não tenha ficado tão confuso! Eu tenho tendência a confundir o simples hehehe, mas creio que a partir das próximas partes fique mais fácil de acompanhar já que é uma parte mais prática e sem tanta teoria. Obrigado e espero ver os comentários pra saber as dúvidas e também poder melhorar nás próximas partes da série. ![]()
Vaga de estágio em programação Flash - RJ
A vaga é para ocupar o meu lugar, pois no meio de abril vence meu contrato e estarei assumindo tempo integral em outra empresa. O estágio é no Centro da cidade, 20 horas semanais com horário bem flexível, sendo oferecido bolsa + benefícios. Quem quizer saber mais sobre com o que irá trabalhar, pode visitar o site Mundo da Criança (www.mundodacrianca.com).
A programação é em flash com ActionScript 2.0.
Quem estiver apto para a vaga, envie currículo para anderson [arroba] mundodacrianca [ponto] com.
Abraços!
No commentsDeveloper Begins! - Curso de games na área…
update #3: segundo me informado hoje, a primeira turma LOTOU e quem se mostrou interessado mas não concretizou a matrícula está com reserva para uma segunda turma, que supostamente começará no dia 14 de abril e terminará no dia 30 (mesmo horário… tem tanto laboratório assim disponível na faculdade e eu não sabia?!).
update #2: a data do curso foi alterada para 7 de abril e irá até o dia 29 do mesmo mês.
Algumas vezes acontecem coisas embaixo de seu nariz, mas que pela capacidade limitada de percepção do sujeito em questão, passam completamente despercebidas. Me senti assim neste fim de semana, quando li uma notícia na Unidev sobre um novo curso de games no Rio de Janeiro. Até aí tudo bem, pois moro no Rio de Janeiro e isso fez com que aguçasse a curiosidade para saber mais sobre o tal curso, só que a surpresa ainda estava por vir.
Minha primeira surpresa foi quando li: “trás em parceria com a Tecnohall, gestora do Centro de Treinamento Uniriotec”. Na mesma hora soltei uma expressão linda que não posso transcrever, afinal ela possui convênio com a faculdade onde estou me graduando e isto soava-me como notícia boa. O golpe final veio logo abaixo: “O curso será ministrado no Centro de Treinamento Uniriotec, localizado na Universidade Federal do Estado do Rio de Janeiro (Unirio) - Av. Pasteur, 458 - térreo - Prédio da Escola de Informática Aplicada - Urca.”
Pára tudo! A empresa que possui convênio com a faculdade onde estudo está em parceria na oferta de um curso de games que será oferecido nos laboratórios de lá? Como é que eu só descobri isso agora?
Fiquei um tempo parado até cair a ficha, mas de qualquer maneira era uma ótima notícia! Logo tratei de entrar no site da Magus Ludens e descubro que a notícia saiu dia 25 de fevereiro. Como o curso já inicia na próxima segunda-feira (17 de março), fiquei um pouco apreensivo quando à disponibilidade de vagas, mas e-mail que recebi, ainda está em tempo de se matricular. Confesso que desconheço os professores e o nível de qualidade do curso, mas como estarei praticamente “em casa”, resolvi arriscar.
O curso Developer Begins! tem duração de 40 horas, com aulas de segunda à sexta de 19h às 22h (17 de março a 4 de abril de 2008). O valor do curso é de R$699,00 à vista ou 2x de R$349,00. O contato pode ser feito pelo e-mail treinamento@uniriotec.br ou pelo telefone (21) 3209-1412.
update #1:
Agora vocês podem conferir a ementa do curso:
· História dos Games
o De 1889 a 2008
o Perspectivas para o futuro
o Academia de Artes e Ciências Interativas
· Game Design
o Entretenimento
o Significado e Experiência
o Semiologia
o Sistema
o Interatividade
o Espaço das Possibilidades
o Sistema de Incerteza
o Definição de Game
o Diferenças de um Jogo Digital
o Círculo Mágico
o Regras em 3 níveis
o Flow
o Sistema Cibernético
o Games como Sistema Emergente
o Teoria da Informação
o Sistema de Conflito
o Desenhando Regras para serem quebradas
· Game Design e Roteiro
o Interface
o Game Bible
o Design Iterativo
o Balanceamento
o Prototipagem
o Metaversos
o Liderança
o Básico de Roteiro para Game
o Outras discursões
· Direção de Arte + Concept Art
o Pesquisa de Referência
o Dissonância
o Storyboard
o GUI
o Charts
o Concept Art
· Desenho Técnico
o Aula Prática
· Apresentação do ambiente 3D e Interface do 3ds Max
o Conceitos gerais de um ambiente 3D
o Básico da Interface do 3ds Max
· Modelagem
o Modelagem em lowpoly
· Mapeamento e Textura
o UVW Mapping, Unwrap e Material Editor
· Shaders, Câmera e Iluminação
o Render to Texture
o Câmera
o Iluminação
o Shaders
· Game Engine, Linguagens de programação e Pipeline Gráfico
· Pseudolinguagem e apresentação do XNA
· XNA
o Aula Prática
· Marketing
o Conceito gerais de Marketing
o Análise do mercado e indústria de games no Brasil e no Mundo
Uma Introdução a Sistemas de Partículas

Um grande “boa noite” a todos!
Gostaria de pedir desculpas por meu atraso em postar aqui a minha contribuição: somos um grupo de poucos escritores, então, qualquer atraso de um de nós leva a grandes problemas na manutenção da atualização deste blog.
Hoje venho aqui trazer uma breve explanação sobre uma das coisas que estudei durante certo tempo com meu amigo Edmar Souza Jr. (the great SuperSayajin! ) e que me parece ser sempre um assunto bastante atualizado e capaz de oferecer muitos resultados: os sistemas de partículas.
Sistemas de partículas são sistemas criados para gerenciar a criação, movimentação (às vezes também a colisão) e destruição de pequenos elementos que podem compor um cenário do jogo. Você pode escrever sistemas de partículas para simular:
- Chuva;
- Neve;
- Furacões; (este aqui eu não consegui fazer… ainda!
)
- Espirais; (semelhante àquelas galáxias vistas nos filmes)
- Explosões;
- Fontes d’água;
- Fogo;
- E muuuuuuitas outras coisas!
Ou seja, um sistema de partículas pode tornar o seu jogo muito mais atraente, já que a posição e movimentação das partículas não é pré-programada (entretanto, quanto mais partículas a serem renderizadas, mais pesada fica a aplicação!).
Certo, certo… Já falamos sobre vantagens e cuidados, agora vamos ver como deve ser criado um sistema… O meu sistema eu criei da seguinte forma:
- A classe (sim, só gosto de trabalhar com orientação a objetos, nem me venham com história de programação estruturada!
) controladora do sistema, que mantém uma lista de todas as partículas e as funções para criar partículas, inicializá-las, movimentá-las e destruí-las;
- A classe a que deve pertencer cada partícula, armazenando nela somente informações da partícula especificamente, como: coordenadas retangulares ou polares no espaço (não sabe trabalhar com coordenadas polares? Então está na hora de aprender, pois eu precisei delas para fazer os espirais! ), tempo de vida, contador que auxilie a animação, etc.
Agora, lembre-se que forças externas podem interferir no movimento das partículas, forças como:
- Gravidade (influencia na posição vertical);
- Vento (influencia na posição horizontal);
- Campo elétrico;
- Campo magnético (provido ou não pelas outras partículas).
No meu sistema implementei levando em conta somente a gravidade e o vento.
Os segredos para se desenvolver um bom sistema são:
- Saber quais propriedades a serem atribuídas a uma partícula;
- Saber como inicializar corretamente (criar e posicionar uma partícula);
- Saber como deve ser feita a movimentação (não é muito difícil - estude exatamente como as coisas se comportam na vida real e aplique esses conceitos);
- Saber o momento para destruir uma partícula (geralmente as partículas são destruídas após um determinado tempo transcorrido, ao colidir com algo ou ao atingir determinada distância do ponto de criação).
Uma curiosidade: gotas de chuva caindo em sua simulação não devem possuir uma aceleração devido à gravidade! Isso é uma regra da Física - um corpo caindo chega a uma velocidade máxima e dela não ultrapassa, devido à resistência que o ar oferece e isso ocorre com as gotas de chuva. Os flocos de neve também não sofrem aceleração gravitacional, estes devido à sua aerodinâmica.
As partículas de explosão e da fonte d’água podem ser criadas da mesma forma: por meio de dispersão, ou seja, as partículas possuem uma velocidade inicial que as levam a se afastar de um ponto origem e pode-se aplicar ou não as leis da gravidade e do vento (é óbvio que a fonte sofre essa ação, não é?). Mais uma curiosidade: quando coloco um tempo de vida razoável nas partículas, uma potência (velocidade inicial com que se afastam as partículas) baixa, sem sofrer a ação da gravidade, tenho um efeito semelhante ao da chama de uma vela!
Bem, com isso que foi dito já há um pontapé inicial para compreendermos como os sistemas de partículas funcionam, não?
Seguindo estas orientações, vocês podem desenvolver quaisquer sistemas de partículas (é só colocar a cabeça para funcionar!).
Bem, agora vou mostrar aqui como criar um sistema de partículas que simule uma chuva em Delphi, ok?
Apresentarei aqui um sistema muito simplificado, não estarão envolvidos aqui tantos parâmetros quanto foram necessários no sistema que desenvolvi, mas já dará um resultado razoável em seus jogos.
Obs: No período em que estava estudando, desenvolvi cada tipo de sistema de partícula como um componente para o DelphiX e, posteriormente, comecei a converter todos os componentes de sistemas de partículas para classes e “encapsulei-as” em um componente principal chamado DXParticleSystemManager, mas infelizmente interrompi a conversão há algum tempo
.
Como já disse, uma classe (ou tipo, que é o que vou usar agora) deve designar a construção das partículas:
TGota = record x: integer; y: integer; end;
E uma outra classe (mais uma vez, vou preferir criar um tipo aqui, só para tornar o processo mais simples) para controlar tudo, ou seja, nosso sistema:
TChuvaSprite = record intensidade: integer; gotas: TList; vento: integer; gravidade: integer; superficie: TCanvas; end;
Bem, defina também um método que inicialize o sistema (CriarChuva) e um método que, a cada vez que for chamado, ele irá desenhar sobre a superfície e recalcular a posição das partículas (MoveGotas).
Pronto! Agora vamos dizer mais ou menos o que deve ser feito na inicialização:
procedure CriarChuva;
var i: integer;
pgota: ^TGota;
begin
if intensidade > 0 then
for i := 0 to intensidade - 1 do
begin
new(pgota);
pgota.x := random(superficie.width);
pgota.y := random(superficie.height);
gotas.Add(pgota);
end;
end;
E agora, vamos ver como deve ser feita a movimentação e desenho de cada ponto. Só lembrando: as partículas de chuva não sofrem aceleração gravitacional, entretanto, em nosso sistema, quanto maior a gravidade, maior a velocidade de queda vertical da gota (só que essa velocidade é constante!!!) e só para facilitar, as gotas de chuva terão a cor ClAqua…
procedure MoveGotas; var i: integer; pgota: ^TGota; begin superficie.Pen.Color := ClAqua; for i := 0 to gotas.Count - 1 do begin pgota := gotas.Items[i]; superficie.MoveTo(pgota.x, pgota.y); superficie.LineTo(pgota.x + vento, pgota.y + gravidade); pgota.x := pgota.x + 2*vento; pgota.y := pgota.y + 2*gravidade; if pgota.x <> superficie.width then pgota.x := 0; if pgota.y <> superficie.height then pgota.y := 0; end; superficie.release; end;
Certo, agora, alguns detalhes: se você colocar o código assim, desse jeito, pode dar algum problema na hora de redesenhar a superfície. Eu trabalho com os valores x e y como reais, mas daí tem que tomar cuidado nas validações e conversões, senão acaba ficando tudo uma porcaria e idem quanto a gravidade negativa, pode trabalhar com ela, mas talvez tenha que fazer ajustes no algoritmo, de acordo com componentes e bibliotecas que estiver usando.
Antes que me perguntem: e por que não já colocou um código perfeito para usarmos, sem falhas e com muitos efeitos?
Simples: meu objetivo aqui é MOSTRAR COMO FAZER e não só chegar e já passar o código-fonte, senão ninguém estaria aprendendo, não é? E já que o objetivo é aprender, temos que fazer restrições nas funcionalidades e tentarmos criar algo bem genérico e de baixa complexidade, como isto!
Em outro momento eu mostro algo aqui um pouco mais avançado, mas quem quiser implementar algo um pouco melhor, uma dica: mesmo em 2D, podemos inserir mais uma coordenada às gotas, uma coordenada z, que quanto maior esse z, mais próxima a gota está da tela, portanto maior ela ficaria, mais rápido ela cairia, ok?
Pronto, por enquanto é isso! Agora é com vocês: recriem o sistema de partículas para chuvas e implementem, por exemplo, o sistema para neve, pois é também simples, mas introduz uma nova característica: o movimento das partículas não é retilíneo, mas sim, algo similar a uma senóide.
Ah, para que eu não esqueça, estou subindo também o executável que implementei no período em questão, com vários sistemas de partículas diferentes.
Para baixar, é só clicar aqui: Uma Introdução a Sistemas de Partículas
Agora sim: até mais!
No commentsA Equipe de Desenvolvimento – Parte III: Programador
Fala galera,
Após algum tempo de ócio pós-final-de-ano, fui “convidado” a dar continuidade à série. Entre os projetos de uma aventura-solo, um jogo da cobrinha (também quero) e outros projetos de jogos casuais (tive tempo até pra ser assaltado), aqui estamos.
Programador
Programador é o profissional responsável pelas implementações técnicas dos jogos. Em algumas empresas, o título de Programador é subdividido em vários cargos, como os exemplos a seguir:
• Tools Programmer: desenvolve ferramentas como editores de mapas, editores de chars, automação de tarefas, organização de projeto, pesquisa, editor de fluxogramas, comunicação da equipe, randomizadores, thesaurus, painéis eletrônicos, plugins e addons, dentre outras variadas possibilidades que se façam necessárias;
• Engine Programmer: produz as estruturas de software que rodam por trás do jogo em si, utilizando linguagens poderosas e interação direta entre softwares, drives e hardwares de entrada, processamento e saída de dados. Comumente, engines utilizam tecnologias externas ou específicas para o tipo e plataforma do jogo a ser produzido.
• Graphics Programmer: tendo uma certa relação com a parte de programação de engine, para o caso da engine gráfica, a função deste programador não se limta apenas aos recursos básicos de renderização, mas à programação de diversos elementos necessários aos projeto, como programação de efeitos de tela, shadders, materiais, smooths, shakes, câmeras e outros recursos e elementos vinculados;
• Database Programmer: esta é uma função mais específica, onde as principais tarefas são organizações de tabelas e gráficos relacionados aos sistemas internos do jogo, geralmente envolvendo recursos de customização e configuração de contas de usuários e suporte para jogos on-line.
• Gameplay Programmer: esta função é a programação do jogo propriamente dita, em seu formato final. Este programador é responsável pela implementação técnica dos sistemas de jogabilidade, coordenação de animações, interação entre levels, elementos de cenário, movimentação, física e matemática dos jogos.
As tarefas mais comuns exercidas pelos Programadores são:
Prototipação de Software de Jogo;
Desenvolvimento de Softwares, Tools, Plugins;
Desenvolvimento de Engines e Core;
Estruturação de Dados;
Correção de Bugs, Otimização de Códigos;
O caminho mais comum para se tornar um Programador é obtendo uma graduação em Ciência da Computação, Engenharia ou Tecnologia da Informação, mas é possível adquirir também conhecimentos e experiência por conta própria. Conhecimentos de lógica de programação, contrução de algorítmos e matemática são fundamentais.
1 commentBurning Down: Fire Effect
Olá galera. Acho que já falei bastante sobre esse efeito aqui. Mesmo assim, vale a pena olhar novamente!!
O efeito de Fogo!!!!
O efeito de fogo é um efeito bem simples de ser implementado, e que tem um resultado bem atraente. Basicamente o que fazemos com ele é pegar um array colocar nele valores aleatórios e ficarmos fazendo cálculos com esses valores.
Como queimar Roma:
Para calcular a cor de um pixel, o que prescisamos fazer é pegar os valores dos 4 pixels vizinhos ( (x+1,y), (x-1,y), (x,y+1) e (x,y-1) ) , somá-los, tirar a média e subtrairmos algum valor para diminuir a vida do pixel.
A idéia é usar um array de bytes (Mapa: array of array of Byte), e trabalharmos com os seus valores de 0..255. Qdo um valor for igual a 0 esse pixel não seria desenhado, e do contrário ele seria plotado na tela.
E se o valor da divisão dos pixels for 0?? Bom, nós teremos então um valor de pixel negativo. Por estranho que pareca, o delphi faz com que os valores retornem para o final do índice, assim, um valor de pixel - 15 será convertido para o valor 240, assim, acabamos tendo a impressão que se forma um rio de mapa na tela. Então, devemos tratar isso manualmente para que valores menores que zero, continuem zero
E a animação? Como o fogo sobe? Bom, isso eh simples, na hora que formos calcular o valor do pixel, vamos devolvê-lo uma linha para cima no array.
Dois arrays ou um só?
A recomendação desse código é usar dois arrays para calcular o fogo, alternando entre eles enquanto se plota os pixels, (faz-se um cálculo em um array, e plota os valores em outro array que será desenhado, depois inverte), isso é desnecessário, pois pode ser feito apenas com um array, dizem, que fazendo assim o fogo fica estranho, mas eu gostei do resultado, e vc vai poder curtir ele com os meus exemplos que vc vai baixar no final desse post ^^.
Calculando as Cores:
Para calcularmos as cores usamos uma paleta indexada de 0..255 (mesmo comprimento do tipo byte
), sendo que nela configuraremos os valores da seguinte maneira:
0: Valor frio ou preto
1: Valor quase totalmente preto
…
255: Valor máximo de calor
Sendo assim, qdo mais alto o valor, mas quente será o nosso fogo, e mais ele demorará para morrer tb.
Não vou esplicar aki como calcular a diferença de cores, pois vc pode achar isso na net, e com o exemplo vem duas boa paletas tb.
A estrutuda das peletas é bem simples:
Type TPalete = Array[0..255] of TColor;
Bom, vc já deve estar imaginando que os valores de cor são em RGB. logo não será mto difícil trabalhar com essas paletas.
Plotando a cor no Array:
Nada difícil de se fazer, simplesmente use:
NovaCor:= ( Mapa[x+1,y] + Mapa[x-1,y] + Mapa[x,y+1] + Mapa[x,y-1] ) / div 4 - Cooling;
if NovaCor < 0 then NovaCor := 0;
Mapa[x,y + 1]:=NovaCor;
O Mapa[x,y + 1]:=NovaCor, é o que fará a animação do nosso fogo subindo.
O Cooling é um valor inteiro que decrementa o valor do pixel, fazendo assim com que a vida do pixel diminua.
Plotando o Pixel na Tela:
Plotar o pixel na tela vai depender da biblioteca que vc está usando. No meu caso eu estou usando o DelphiX, que tem métodos de desenho mto semelhantes ao Canvas padrão do Delphi, então eu faço assim:
DXDraw.Surface.Canvas.Pixels[x,y] := Paleta[Mapa[x,y]];
Recomendo que faça o desenho dos pixels em uma procedure separada da proedure em que se calcula os valores, isso por questão de organização do código ok?
Bom, creio que não precisa dizer que Paleta é um objeto do tipo TPalete né?
Carregando a Paleta:
Já que tocamos no assunto, devemos criar tb. um tipo:
Type TPaleteFile = File of TPalete;
Com ele podemos tanto salvar e carregar uma paleta com poucas linhas de código:
var MeuArquivo: TPaleteFile; MinhaPaleta:Tpalete;
…
If FileExists(Nome_do_arquivo) then begin
AssignFile(MeuArquivo, Nome_do_arquivo);
Reset(MeuArquivo); // Para ler;














