Descrição Geral
Este trabalho, que deverá ser feito individualmente
ou em duplas, consiste em modelar e implementar uma simulação
de uma corrida de carros.
A simulação funciona da seguinte forma:
- Ao iniciar, o programa deve desenhar na tela uma pista de
corrida, contendo 4 raias, semelhante ao que está apresentado na figura
abaixo.
- O usuário do programa controla um "carro", representado pelo carro
azul, que pode ser deslocado para esquerda e para a direita na tela
através das teclas de seta.
- O deslocamento do carro do usuário deve ser feito de forma contínua, não sendo permitido que este "pule de raia em raia".
- Além do carro do usuário, devem estar presentes na pista,
pelo menos, mais 4 carros de cores diversas, diferentes da cor do carro
do usuário.
- Estes carros surgem na parte superior da tela e deslocam-se para baixo de forma automática e contínua, sem trocar de raia.
- Ao chegar na parte inferior da tela o carro deve desaparecer do jogo e o usuário ganha um ponto por ter "ultrapassado" um carro.
- O objetivo do jogo é "ultrapassar" o maior número de carros possível.
- Quando um carro desaparecer, outro carro deve reaparecer na
parte superior da pista, mas a raia em que este vai surgir deve ser
definida de forma aleatória.
- A velocidade de deslocamento dos carros nas raias também deve ser aleatória e sorteada quando o carro surge na raia.
- Quando o carro do usuário colidir com algum outro carro, o
jogador deve perder um ponto. Se ficar com pontos negativos, o jogo
deve ser encerrado.
Montagem do Cenário
Para desenhar os carros e a pista, deve ser utilizado o projeto que está disponibilizado na página da disciplina Moodle. Este projeto se baseia em um conjuento de classes criadas pelo prof. Marcelo Cohen.
Conforme pode ser observado no trecho de programa abaixo, o jogo é controlado pelo objeto Jogo, da classe Renderer, instanciado no programa principal.
Este objeto Jogo é capaz de desenhar os objetos que estiverem armazenados no objeto Cenario, da classe ListaDeFiguras.
A classe ListaDeFiguras é capaz de armazenar bjetos da classe Figura, conforme pode ser visto na função MontaCenario. Para a realização presente trabalho esta função deverá ser alterada para desenhar a pista e os carros.
ListaDeFiguras Cenario;
void MontaCenario()
{
Circulo *Jogador;
Jogador = new Circulo(0,0,1);
Figura *F;
F = new Figura(Jogador);
Cenario.add(F);
}
int main(int argc, char *argv[])
{
// Cria um objeto Renderer, responsável por redesenhar a tela
// O construtor de Renderer recebe um ponteiro para o objeto Cenario
MontaCenario();
Renderer Jogo(&Cenario);
// Note que o metodo init deve receber uma string contendo o título da janela
// e um ponteiro para um objeto Renderer já inicializado
GL::init("Lapro Racing 2011 - Trabalho 1", &Jogo);
cout << "Apos GL::init" << endl;
// Inicia processamento de eventos
GL::loop();
}
Controle da Animação
Para controlar a animação deve ser usado o método void Renderer::PassaTempo(), que está no arquivo Renderer.cpp. Este método
e continuamente chamado, permitindo que o programador atualize as posições dos objetos do cenário.
No trecho de código abaixo, por exemplo, a posição do objeto que
está na posição 0(zero) da lista de objetos do cenário, é atualizada
fazendo este objeto subir e descer na tela, continuamente.
No caso do presente trabalho, as funcionalidades de teste de colisão
entre os carros, por exemplo deve ser colocada neste método.
void Renderer::PassaTempo() {
static int direcao = -1;
Figura *F = lista->get(0);
Circulo *c = (Circulo*) F->getObjeto();
if (direcao == 1)
c->move(0,0.1); // sobe
if (direcao == -1)
c->move(0,-0.1); // desce
if (c->getY() < -8)
direcao = 1;
if (c->getY() > 8)
direcao = -1;
}
Criando Novos Objetos
Para criar um objeto, crie uma nova classe a partir do exemplo da classe Circulo. Para o correto funcionamento da lista de objetos, todos os métodos existentes no arquivo Circulo.h devem estar presentes na nova classe. A implementação da nova classe pode ser diferente da implementação da classe Circulo, disponível no arquivo Circulo.cpp.
Se houver necessidade, novos métodos pode ser criados, mas os que já existem devem ser mantidos.
Desenhos de Objetos Gráficos
Para desenhar os objetos do cenário a classe criada a partir da classe Circulo deve possuir um método draw, semelhante ao exemplo abaixo.
void Circulo::draw() {
GL::setColor(0,200,200);
GL::drawCirc(x,y,raio);
}
Para desenhar de fato os objetos, são usados os métodos da classe GL, implementada pelos arquivos GL.cpp e GL.h. Os principais métodos de desenho são apresentados abaixo.
class GL {
public:
// Inicializa a biblioteca gráfica, cria a janela e recebe o objeto Renderer
static void init(std::string titulo, Renderer* render);
// Métodos de desenho: todas as coordenadas são CARTESIANAS
// Desenha um retângulo: informar os pontos dos cantos
// superior esquerdo e inferior direito
static void drawRect(float x1, float y1, float x2, float y2);
// Pinta um retângulo, idem
static void fillRect(float x1, float y1, float x2, float y2);
// Desenha um triângulo: recebe 3 pontos
static void drawTri(float x1, float y1, float x2, float y2, float x3, float y3);
// Pinta um triângulo, idem
static void fillTri(float x1, float y1, float x2, float y2, float x3, float y3);
// Desenha um círculo, recebe o centro e o raio
static void drawCirc(float x1, float y1, float r);
// Pinta um círculo, idem
static void fillCirc(float x1, float y1, float r);
// Desenha uma linha: recebe o ponto inicial e final
static void drawLine(float x1, float y1, float x2, float y2);
// Seta a cor de desenho, especificada em RGB (0-255 para cada componente)
static void setColor(GLubyte r, GLubyte g, GLubyte b);
// Seta a largura das linhas, especificada como um float (1.0 = 1 pixel de largura)
static void setLineWidth(GLfloat w);
...............
Note que este métodos desenham em coordenadas cartesianas, em um sistema de coordenadas que tem a maior dimensão entre -10 e +10. Esta maior dimensão depende da largura e da altura da janela onde roda o programa.
Se a largura é maior que a altura então na esquerda da janela fica a coordenada -10 e na direita fica a coordenada +10. Se a altura é maior que a largura então na parte inferior da tela fica a coordenada -10 e na parte superior fica a coordenada +10.
Estas dimensões podem ser alteradas na funçào GL::reshape, mas isto não é necessário e nem aconselhável para este trabalho.
Além de desenhar objetos através de elementos que são colocados no objeto Cenario, é possível desenhar objetos isolados. Para tanto, os comandos de desenho da classe GL devem ser colocados na função paint da classe Renderer.
No exemplo abaixo, observa-se o desenho de um quadriculado. À direita, pode-se ver o resultado deste código.
No presente trabalho, sugere-se que o desenho da pista seja feito dentro desta função.
Importante: ao final do método GL::paint há a chamada do método lista->drawAll() que
faz o desenho da lista de objetos que foi criada na função
MontaCenario, do programa principal. Esta chamada não deve ser removida
a menso que se deseje eliminar o cenário do programa.
// Este método deve desenhar os objetos, usando os métodos
// estáticos da classe GL
//
// Ele é chamado toda vez que a aplicação precisa redesenhar
// a tela gráfica. NÃO DEVE SER CHAMADO MANUALMENTE!
void Renderer::paint() {
//
// Use os métodos da classe GL para desenhar os objetos da lista
// Ou melhor, cada objeto deve ser capaz de se auto-desenhar
// (implemente o método draw() em cada classe)
//
// Dica: basta pedir que a lista desenhe todos os objetos...
cout << "Renderer::paint" << endl;
// Desenha uma grade
// Cinza bem claro, para outras cores, veja exemplos em /etc/X11/rgb.txt
GL::setColor(200,200,200);
// Ajusta largura da linha (1.0 = 1 pixel de largura)
GL::setLineWidth(1.0);
for(int x=-10; x<=10; x+=1) {
GL::drawLine(x,-10,x,10);
GL::drawLine(-10,x,10,x);
}
// Desenha um sistema de eixos
// Cinza, para outras cores, veja exemplos em /etc/X11/rgb.txt
GL::setColor(60,60,60);
// Linha mais larga
GL::setLineWidth(3.0);
GL::drawLine(-10,0,10,0);
GL::drawLine(0,-10,0,10);
lista->drawAll();
}
|

|
Tratando Teclas
Para permitir a leitura de teclas deve-se utilizar os métodos Renderer::keyboard e Renderer::keyboardSpecial, conforme o exemplo abaixo.
//
// Trata eventos de teclado: se uma tecla qualquer for pressionada
// durante a execução do programa, este método recebe o seu código
// ASCII na variável key, mais as coordenadas atuais do mouse em x,y
void Renderer::keyboard(unsigned char key, int x, int y) {
cout << "Renderer::keyboard: " << key << endl;
if (key == 27) // ESC ?
exit(0);
}
//
// Trata eventos de teclado: se uma tecla ESPECIAL( F1, F2, ..., ALT-A, ALT-B,...) for pressionada
// durante a execução do programa, este método recebe o seu código
// ASCII na variável key, mais as coordenadas atuais do mouse em x,y
void Renderer::keyboardSpecial(int key, int x, int y) {
cout << "Renderer::keyboardSpecial: " << key << endl;
Figura *F = lista->get(0);
Circulo *c = (Circulo*) F->getObjeto();
if (key == 102) { // seta para direita
c->move(0.1,0);
}
if (key == 100) { // seta para esquerda
c->move(-0.1,0);
}
}
Avaliação
O código-fonte deve estar adequadamente comentado (nas suas
partes/algoritmos principais, pelo menos) e no seu início deve haver um
comentário com o(s) nome(s) do(s) aluno(s).
Apresentação
O trabalho será apresentado NA SALA DE AULA, na máquina do professor.
Por esse motivo, TODOS os trabalhos já devem ter sido enviados pelo
Moodle antes do horário da aula. Durante a apresentação, todos os alunos devem estar presentes e aptos a responder quaisquer perguntas. Respostas insatisfatórias ou a ausência do aluno acarretarão em anulação da nota final.
Até ANTES do horário de início da aula do dia da apresentação, cada aluno ou dupla já deverá ter submetido um arquivo .zip (não RAR, nem 7z ou qualquer outro formato) pelo sistema
Moodle, contendo o projeto completo do programa. O programa deverá ser nomeado da seguinte forma:
nome_sobrenome.zip No caso de uma dupla, usar a forma: nome1sobrenome1_nome2sobrenome2.zip.
O programa deverá ser executado no sistema operacional Linux. Não
serão aceitos trabalhos que não compilem corretamente no g++, sob
hipótese nenhuma.
Todos os trabalhos deverão ser entregues até o dia da apresentação, e apresentados na data marcada - não serão aceitos trabalhos fora do prazo.
Trabalhos copiados resultarão em nota zero para todos os alunos envolvidos.
FIM