terça-feira, 29 de maio de 2012

paOpenGLDraw finalizada!

Olá a todos!

Consegui finalizar por hora a parte de desenhos de primitivas da engine. Alguns desenhos suportam texturas com GL_REPEAT. Outros, somente texturas com GL_CLAMP_TO_EDGE. Algumas primitivas também suportam smooth de cor para cada vértice.

Algumas coisas ainda estão por ser feitas, como o desenho de qualquer poliedro desejado de modo fácil, desenho usando smooth para todas as formas, e suporte para GL_REPEAT para qualquer tipo de forma geométrica também, o que deixarei para mais tarde devido ao tempo para a entrega do projeto estar se esgotando.

É importante ressaltar que o comando  "setPolygonDrawMode" define o modo como a forma será desenhada (de modo normal, por linhas - wireframe - , ou somente os vértices da mesma).

A seguir, todas as forma geométricas 2D e 3D suportadas e suas respectivas características:
  • Pontos;
  • Linhas (2D e 3D);
  • Linhas tracejadas (2D e 3D);
  • Grid;
  • Triângulo (suporta texturas com GL_REPEAT);
  • Quadrado/Retângulo (suporta texturas com GL_REPEAT);
  • Círculo (suporta texturas);
  • Elipse;
  • Poligonos com textura;
  • Polígonos com smooth de cores para cada vértice;
  • Polígonos com dimensões internas dinâmicas e ângulo inicial e final variados (Ex: podemos desenhar um polígono com um buraco no centro e indo de 0 a 90 graus, formando assim um leque);
  • Skybox;
  • Cubo/Paralelepípedo (suporta texturas com GL_REPEAT);
  • Pirâmide (suporta texturas com GL_REPEAT);
  • Esfera (suporta texturas);
  • Poliedros esféricos (que nada mais são que esferas com número de linhas de definição dinâmicas). Suporta texturas;
  • Cilindros parciais (somente o envolto) e completos (o envolto e os círculos laterais). Suporta texturas;
  • Cones parciais (somente o envolto) e completos (o envolto e o círculo da base). Suporta texturas;
  • Polígono/Poliedro com posicionamento dos pontos a ser definido pelo usuário (suporta smooth de cores).
As implementações futuras envolverão:

 * Suporte de textura para todas as fomas

 * Suporte de GL_REPEAT para todas as texturas

 * Desenho com smooth para todas as formas

 * Desenho de qualquer tipo de poliedro de modo fácil

 * Desenho de mais formas cilindricas

 * Melhora das texturas esféricas

 * Suporte a curvas

 * Suporte a texturas 1D (para linhas) e 3D (para volumes)

domingo, 27 de maio de 2012

Arte + Game Design - Elementos gráficos "in Game"

Oie!!!!

Como prometido já estão prontos os novos elementos gráficos, desde umas duas semanas na verdade, mas como estou com várias correções e  atualizações na documentação acabei não fazendo este post antes.
Então, vamos lá!

A criação destes novos elementos é produto das sprints Produção dos elementos da arte – HUD PadrãoProdução dos elementos da arte – HUD (Personagens) inclusas como tarefas na estória Desenvolvimento Intermediário nas Estimativas de Desenvolvimento no Documento de Arquitetura  e como estórias individuais na ferramenta Pivotal Tracker.

Esses Elementos são os utilizados "in Game"  na tela em que o jogo estará em andamento, ou seja, a partida em si e os turnos de cada jogador.
Como já comentei optamos em utilizar como base o conceito do jogo original em sua versão digital, para mais detalhes confira nos posts anteriores: Arte Inicial e Game Design.

Agora vamos ver a personalização dos elementos gráficos para a nossa própria versão do jogo "Ticket to Ride"!!!

Produção dos elementos da arte – HUD Padrão


As dimensões de tela foram definidas como 1024 x 768, além do modo Full Screen e a área do mapa 842 x 512, assim desenvolvi o background  com estas dimensões.
Já no conceito resolvi criar um fundo com textura de madeira para proporcionar um ar rústico, também coloquei um trilho na parte inferior da imagem onde ficam as cartas que o jogador atual possui na "mão", levando em consideração que serão estas que ele utilizará para construir suas rotas no mapa, criando assim uma imagem de referencia entre cartas e trilhos. Por fim, fiz uma moldura com tons amarelos e alaranjados.

Primeiro estudo do Background
(Figura 1 - Primeiro estudo do Background)

Para finalizar utilizei efeitos para deixar o conjunto com uma perspectiva melhor com sombra na moldura e 3D no trilho dando uma melhorada na posição deste.

(Figura 2 -  Background com Textura Simples)
(Figura 2 -  Background com Textura Simples)

Ai surgiu a ideia de estilizar mais a textura de madeira do fundo, confira o resultado:

(Figura 3 -  Background com Textura Estilizada)
(Figura 3 -  Background com Textura Estilizada)

Ainda não decidimos qual das texturas de fundo utilizaremos...

Também fazem parte desta as cartas anteriormente estilizadas com base nas originais, porém agora elas estão com as dimensões definitivas, aoalterar as dimensões consequentemente precisei otimizar as imagens.

(Figura 4 - Cartas de Construção(Trens Coloridos))
(Figura 4 - Cartas de Construção (Trens Coloridos))

Quanto as cartas de destino faltam algumas definições, assim que acerta-las disponibilizo as cartas, de qualquer forma seguirão o padrão das originais.

Também entra na hud padrão as peças, que na verdade é uma peça branca que terá a cor modificada de acordo com o jogador que a utilizar.

(Figura 5 - Peça vagão)
(Figura 5 - Peça vagão)


O ultimo detalhe da hud padrão é o botão "?" que conterá um help sobre os comandos e esta localizado no canto superior esquerdo da tela.

(Figura 6 - Botão de help "?")
(Figura 6 - Botão de help "?")

(Figura 7 - Botão de help "?" ativo)
(Figura 7 - Botão de help "?" ativo)




- Produção dos elementos da arte – HUD (Personagens) 

Os personagens são baseados nas 5 cores disponíveis para escolha dos jogadores: Preto, Vermelho, Azul, Verde e Amarelo. Cada um tem uma personalidade distinta como pode ser observado no próprio desenho deles, tentei abranger o máximo de estilos de pessoas que jogam Ticket to Ride apesar da limitação de cinco possibilidades.
A hud é formada pela 'foto' do personagem caracterizado com acessórios da sua cor, a moldura envolve esta e também os locais onde aparecerão a quantidade de trens (abaixo da imagem de locomotiva) e pontos atuais, estendendo-se em uma linha personalizada até a outra extremidade da tela servindo como apoio das cartas do jogador.

Abaixo as huds dos personagens no modo jogador atual, esta fica na parte inferior da tela:

(Figura 8 - Lady)
(Figura 8 - Lady a Charmosa)

(Figura 9 - Shadow)
(Figura 9 - Shadow Mistério...)

(Figura 10 - Scot)
(Figura 10 - Scot o Aventureiro)

(Figura 11 - Sarah)
(Figura 11 - Sarah a Estrategista)

(Figura 12 - John)
 (Figura 12 - John o Divertido)


E na parte superior da tela ao lado do botão de help ficam as huds dos outros personagens que fazem parte da partida e aguardam seu turno. Se trata de uma hud diminuída da hud de personagem atual sem a linha inferior desta, confira:

                               (Figuras 13 - Personagens aguardando seu turno) Shadow(Figuras 13 - Personagens aguardando seu turno) Lad(Figuras 13 - Personagens aguardando seu turno) Scot

                                            (Figuras 13 - Personagens aguardando seu turno) Sarah(Figuras 13 - Personagens aguardando seu turno) John
(Figuras 13 - Personagens aguardando seu turno)

Enfim, nas próximas imagens pode-se visualizar o resultado final com a textura normal e a estilizada. Se alguém quiser opinar sobre qual prefere ou fazer sugestões sinta-se a vontade, espero que gostem!

(Figura 14 - Resultado Final)
(Figura 14 - Resultado Final)

(Figura 15 - Resultado Final 2)
(Figura 15 - Resultado Final 2)

Novas Implementações na paOpenGLDraw

Olá!

Devido a alguns problemas, não acabei implementando muitas coisas nesses ultimos dias. Também perdi muito tempo corrigindo um bug no desenho (as texturas não estavam mais funcionando) e na câmera (as transformações passaram a acontecer invertidas).

Pra compensar, diversas alterações se fizeram necessárias na classe draw para chegar à formula perfeita, e desconfio que esta formula ainda não chegou ao seu ápice.

A maior decisão de projeto envolveu remover o modo de posicionamento do desenho. Agora para movimentar os objetos no cenário só é possível utilizando-se a classe de transformações.

Também inseri desenho de textura com e sem glRepeat, de forma que o usuário deve informar apenas o tamanho da primitiva gráfica, quais serão as texturas utilizadas, qual será a ordem para cada face, e qual será o fator de repetição para cada face.

Para contextualizar, segue o cabeçalho do método drawParallelepiped:

//Desenha um paralelepípedo ou cubo dependendo das dimensões desejadas
//Recebe o tamanho em largura, altura e profundidade no vetor size
//Recebe um ponteiro para um array contendo todas as texturas usadas na rederização de cada face
//Recebe a sequencia de aplicação de textura (ex: se houver apenas uma textura em textures,
//  podemos setar o valor de textureSequence como: {0,0,0,0,0,0}, ou seja, a mesma textura para todas as faces)
//Em caso de uso de TEXTURE_REPEAT, passar o fator de repetição para cada face em X e Y (factor)
//(usar DEFAULT_FACTOR para GL_CLAMP)
//A ordem das faces desenhadas é: TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT
void drawParallelepiped(const math::Vector3D size,const GLuint *textures,
                                        const GLuint textureSequence[6],const GLuint factor[6][2]);


Também implementei Culling para todo tipo de forma desenhada para otimizar a engine. Apesar de quase, a parte de desenho ainda não está finalizada, pois estou dando uma melhor estudada em texturas a fim de criar formas geométricas arredondadas com textura aplicada.

quinta-feira, 24 de maio de 2012

Melhorando o desenho

Olá a todos!

De acordo com o cronograma de desenvolvimento, a próxima etapa é implementar a colisão.
Pensando nisso, fui analisar como estava a classe paOpenGLDraw para poder criar maneiras de integrar o desenho à colisão em uma classe de sprites.

Quando observei os aspéctos da classe, reencontrei o caos!

Para contextualizar, é importante lembrar da época da refatoração do código e das implementações a nível de prototipagem. Nesse contexto, todas as demais classes foram modificadas e melhoras, exceto a pobre paOpenGLDraw.

Então, antes de iniciar a implementação das colisões, decidi por buscar finalizar os métodos restantes de desenho. Acabei também corrigindo um bug da câmera e da paOpenGLWindow.

Dessa forma, as seguintes funcionalidades foram implementadas:

  • Melhora do método já existente "setSimulationStuff" para setar, de acordo com a qualidade desejada, qual será a qualidade do mipmap das texturas.
  • Método setColor.
  • Método para deletar texturas carregadas.
  • Método drawLine.
  • Método drawGrid.
  • Método drawSkybox.
  • Método para desenho de cubos e paralelepípedos.
  • Método drawPyramid.
  • Método drawSphere (EM CONSTRUÇÃO).

É importante ressaltar que todos as implementações envolvem aplicação de textura e culling.
Ainda faltam implementar os métodos:
  • drawTriangle
  • drawRect
  • drawCircle
Finalizadas essas implementações, planejarei como será a integração da colisão às classes de desenho, porém já dando inicio ao mesmo tempo ao gerador de mapas que precisa ser implementado o quanto antes!

domingo, 20 de maio de 2012

Ultimos Ajustes

Olá...

Depois de uma semana trabalhando com a parte de som, finalmente cheguei ao resultado desejado.

A ultima funcionalidade adicionada foi a de limitar os tipos de arquivos de sons carregáveis, disparando uma exceção em caso de formato não suportado.

Se fosse continuar a programação dessa parte da engine, ainda teria que inserir controladores de paning e de distância do som de acordo com a posição do jogador. Infelizmente não tenho o tempo necessário para isso e por hora a parte de som fica por aqui.

Restando somente a parte de colisão, agora vou buscar inserir na engine o método de colisão por picking para poder iniciar a construção do gerador de mapas.

paSDLChannelPlayer

Olá!

Levando em consideração os moldes criados para paSDLMusicPlayer, criei paSDLChannelPlayer para gerenciar os sfx.

Apesar da maior complexidade, foi muito mais fácil criar essa classe, já que a maioria da programação de dependência com um gerenciador já estava feita entre paSDLSoundEffect e paSDLAudio, bastando apenas colocar as coisas no lugar certo.

Diferente das músicas como todo mundo sabe, sfx podem tocar ao mesmo tempo. Então não foi necessário remover do escopo da paSDLSoundEffect métodos de reprodução da música. Em geral deu tudo rapidamente certo.

As maiores dificuldades foram a alocação de canais, uma vez que tive que fazer uns testes para estabelecer o melhor padrão para o usuário inserir o número de canais desejados para a aplicação; e um método que pausasse todas as músicas e voltasse a reproduzir apenas as que estavam tocando no momento do pause. Além da própria implementação, ainda tive que considerar problemas com relação ao estado do pauseAll. Por exemplo, se todos os sfx estiverem parado, nao faz sentido aplicar pauseAll. E se todas as músicas estiverem reproduzindo novamente, não faz sentido aplicar o resumo para cada uma delas, sendo necessario novamente pausar as musicas ao inves de voltar a reproduzí-las.

Outra grande dificuldade foi estabelecer um padrão entre pauseAll e pauses de escopo. Testando as melhores maneiras, cheguei ao seguinte algoritmo:

1- Musicas pausadas com pauseAll e reiniciadas ou despausadas com os métodos de escopo para tais funcionalidades não são mais gerenciadas pelo pauseAll

2- Se o usuário chamar o método pauseAll e depois mandar tocar mais sfx sem reaplicar o pauseAll para retomar os sfx com pauseAll, somente os sfx pausados com esse metodo voltarão a tocar, sendo os demais sons não atingidos por ele.


É aquela velha história. Uma engine deve ser flexível, mas cobrir todas as burradas do usuário é impossível. Se eu fosse levar em conta novas músicas reproduzidas ou músicas despausadas independente do pauseAll, acabaria desvirtuando o motivo real da criação desse método.


Juro que o próximo post será o último sobre audio! Ainda preciso verificar algumas coisas nas classes antes de fechar uma nova versão.

Até la!

paSDLMusicPlayer

Olá!


Lembrando dos assuntos referentes ao último post, a parte de som da engine estava praticamente concluída, exceto por alguns problemas de encapsulamento envolvendo funções friend e variáveis estáticas.


Este problema se agravou quando fui implementar as ultimas funcionalidades, as quais envolviam dar stop em todos os sons, pausar todos os sons ou retomá-lo a partir do ponto de onde pararam. O grande problema envolvido nessas funcionalidades estava na ausência de um mecanismo ágil de conversação entre as classes. além da perigosa declaração das mesmas como friend. Analisando a situação, percebi que era inviável continuar e resolvi aumentar a complexidade das classes em virtude da iminente necessidade de um gerenciador decente!


Como diria Carlo Ginzburg em "O Queijo e os Vermes": VOLTAMOS À ESTACA ZERO!


Buscando maneiras de melhorar a conversa entre uma possível classe gerenciadora dos atributos de todas as músicas, me decidi pela velha companheira herança que nunca cessa em ajudar os desesperados por uma relação de hierarquia melhor que a dos velhos e tediosos ponteiros usados em C, através de uma dica da Keli para o meu problema.


O grande diferencial seria que, mesmo possuindo uma alta gama de variáveis estáticas controladoras, eu ainda conseguisse manter um encapsulamento decente e funcionalidades relacionadas à manipulação de músicas dentro do seu devido escopo. Dessa forma a classe pai foi nomeada para paSDLMusicPlayer.

Tendo um gerenciador encapsulado para os atributos das musicas do game, a maioria dos meus problemas com variáveis estáticas e complexidade excessiva se resolveu, exceto pela perda de tempo ocasionada pelo esquecimento da declaração do destrutor da classe pai como virtual (hehehehe...).

Com um escopo mais protegido, fiquei livre para aumentar as funcionalidades. Agora é possível receber da paSDLMusic retornos contendo o ID da música, qual é a música atual que está tocando, se a música X está ou não reproduzindo e aumentar o volume de todas as músicas ou de uma específica.

Em resumo, com paSDLMusicPlayer, a reprodução de músicas ficou extremamente poderosa na engine, possibilitando diversas abordagens facilitadas para tratamento dos queridos sons que acompanham o fundo dos nossos jogos.

Essa nova classe me possibitou também tornar toda a paSDLMusic independente da paSDLAudio, sendo esta responsável apenas por inicializar a biblioteca SDL_Mixer e reservar as constantes de audio como o volume máximo permitido.

Ainda tenho que adaptar o desenvolvimento para a paSDLSoundEffect. Se eu conseguir tal façanha, sem novamente encontrar outro grande erro que me faça reiniciar a construção das classes, a parte de som estará concluída!

sábado, 19 de maio de 2012

DOCUMENTAÇÃO
Documento de Arquitetura do Jogo (7 - 8)

Oi!!!

Hoje falarei sobre as atualizações e dificuldades no desenvolvimento do  Diagrama de Casos de Uso mais a Realização de Casos de Uso que estão fortemente ligados, ou seja, uma alteração em um tem por consequência o balanceamento no outro.

Aparentemente um diagrama simples que tem como objetivo simular o comportamento e as várias escolhas de um jogador em nossa versão digital de Ticket to Ride Europe, mas a simplicidade do mesmo acaba diluída entre reunir todas as funcionalidades que oferecemos desde a inicialização do jogo até o final e a revisão de todas as escolhas possíveis de modo que não haja conflito entre estas, isso tudo agregado a Realização de casos de uso, que é a descrição de cada um dos casos de uso criados no diagrama citando os comportamentos comuns que o jogador pode ter, os possíveis erros e as regras gerais.

Eu pessoalmente tive problemas com extends em certos pontos do diagrama, para poder seguir em frente tive que tirar duvidas no material de apoio e fazer revisões com o Ráfagan.
Nessa questão posso destacar o ultimo empasse no Diagrama de casos de uso que foi uma discussão sobre um extend entre Acessar Opções e Voltar a Partida Atual, a flecha apontando para ambos os lados dava um certo conflito... então conversando chegamos a conclusão que não precisava dessa ligação pois o caso de uso Pausar Game é que faz essa ligação entre essas opções e voltar a partida atual.

É interessante destacar que ao começar a realização de casos de uso houve melhor visualização do ponto de vista do jogador interagindo com o jogo, isto causou algumas mudanças no diagrama, mas com toda certeza para melhor!
Neste ponto, tive que atualizar o diagrama, reorganizá-lo de acordo com as modificações exigidas pela descrição de determinados casos de uso, além de várias revisões entre Diagrama e Realização.

Destaco abaixo algumas Realizações de Caso de uso que além de importantes foram interessantes de montar o cenário, pois precisei utilizar-me das regras oficiais e encaixa-las nos casos de uso, de modo a abranger todos os comportamentos possíveis de acordo com elas e as escolhas do jogador:

UC011 – Receber Cartas.


Descrição:
A partida inicia e o jogador recebe as cartas de destino e de construção (trens coloridos)

Atores:
Jogador

Pré-Condições:
O caso de uso Iniciar Partida ter sido realizado.

Pós-Condições:
O jogador recebe as cartas iniciais de construção e de destino.
Fluxo Básico:

1. O jogador recebe 4 cartas de construção.(RN2)
2. O jogador recebe 1 carta de Destino com rota longa.(RN3)
3. O jogador recebe 3 cartas de Destino com rota normal.(RN4)
4. O jogador visualiza as cartas recebidas. (A1)
5. Realizar caso de uso Escolher Cartas de destino.

Fluxos Alternativos:

A1. O jogador clica em fechar a janela do jogo. (E1)

Fluxos de Exceção:

E1. O sistema informa que o jogo será finalizado e solicita confirmação da operação. (RN1)

Regras de Negócio:

RN1. O jogo será encerrado somente se o jogador confirmar que deseja finalizá-lo.
RN2. O restante das cartas de construção não entregues para os demais jogadores são organizadas para compra durante os turnos, 5 são viradas na mesa e as restantes são colocadas em um monte.
RN3. O restante das cartas de Destino com rota longa são retiradas da partida atual.
RN4. O restante das cartas de Destino rota normal não entregues para os demais jogadores são organizadas em um monte para compra durante os turnos.


UC017 – Comprar duas cartas de trem.

Descrição:
O jogador tem acesso as cartas de trem possíveis para comprar.

Atores:
Jogador

Pré-Condições:
O caso de uso Comprar Cartas ter sido realizado.
Pós-Condições:
O jogador compra a(s) carta(s) que deseja.
O turno atual do jogador finaliza.
Fluxo Básico:

1. O jogador compra duas cartas de trens coloridos abertas na mesa. (A1)(A2)(A3)(A4)(A5)(E1)
2. Fim de caso de uso. (A6)

Fluxos Alternativos:

A1. O jogador compra uma carta da mesa e outra do monte. (RN1)
A2. O jogador compra uma carta do monte e outra da mesa. (RN2) (E1)
A3. O jogador compra duas cartas do monte. (RN1)(Rn2)
A4. O jogador compra um coringa da mesa, realizar caso de uso Comprar Coringa da mesa.
A5. O jogador encerra o jogo. (E2)
A6. O jogador realiza o caso de uso Visualizar Tela do jogo até ser seu turno novamente ou o jogo acabar.

Fluxos de Exceção:

E1. O jogador deseja comprar como segunda carta um coringa da mesa, o sistema informa que a opção não é válida.
E2. O sistema informa que o jogo será finalizado e solicita confirmação da operação. (RN3)

Regras de Negócio:
RN1. O Jogador poderá permanecer com a segunda carta do monte mesmo que seja um coringa.
RN2. Se o jogador comprar a primeira carta do monte e esta for um coringa ele pode ficar com a carta e comprar outra da mesa ou do monte.
RN3. O jogo será encerrado somente se o jogador confirmar que deseja finalizá-lo.


UC022 – Construir Estação.

Descrição:
O jogador constrói uma estação ao aceitar que perderá 4 pontos a cada uma construída e paga uma ou mais cartas para realizar a ação.

Atores:
Jogador

Pré-Condições:
O caso de uso Escolher local ter sido realizado.
Pós-Condições:
O jogador constrói uma estação.

Fluxo Básico:

1. O jogador seleciona uma cidade para construir a estação. (A1)(E2)(E3)(RN2)
2. O jogador constrói a estação. (RN3)(RN4) (RN5) (RN6)
3. Fim do caso de uso. (A2)

Fluxos Alternativos:
A1. O jogador encerra o jogo. (E1)
A2. O jogador realiza o caso de uso Visualizar Tela do jogo até ser seu turno novamente ou o jogo acabar.

Fluxos de Exceção:

E1. O sistema informa que o jogo será finalizado e solicita confirmação da operação. (RN1)
E2. O jogo trava.
E3. O jogador escolhe um lugar inválido para construir a estação, retorne ao passo 1 do Fluxo Básico.

Regras de Negócio:
RN1. O jogo será encerrado somente se o jogador confirmar que deseja finalizá-lo.
RN2. O jogador só pode construir estações em cidades desocupadas.
RN3. O jogador só constrói a estação se aceitar que no final do jogo perderá 4 pontos com está ação.
RN4. O jogador precisa descartar 1 carta para criar a primeira estação.
RN5. O jogador precisa descartar 2 cartas da mesma cor ou locomotiva(s) para criar a segunda estação.
RN6. O jogador precisa descartar 3 cartas da mesma cor ou locomotiva(s) para criar a terceira estação.



UC025 – Retirar três cartas do baralho.

Descrição:
O jogador compra três cartas do monte e paga mais cartas a cada uma da mesma cor ou locomotiva que surgir.

Atores:
Jogador

Pré-Condições:
             O caso de uso Construir Túnel ter sido realizado.
         
Pós-Condições:
            O turno finaliza.

Fluxo Básico:

1.      O jogador compra três cartas do monte. (A1)
2.      As três cartas do monte são diferentes da cor escolhida para construção do túnel. (A2) (A3) (A4) (E2)
3.      Fim do caso de uso.

Fluxos Alternativos:

A1. O jogador encerra o jogo. (E1)
            A2. Uma carta é igual a cor das cartas escolhidas para construção do túnel, o jogador paga uma carta desta cor. (E2)(RN2)(RN3)(RN4)
            A3. Duas cartas são iguais a cor das cartas escolhidas para construção do túnel, o jogador paga duas cartas desta cor. (E2)(RN2)(RN3)(RN4)
            A4. Três cartas são iguais a cor das cartas escolhidas para construção do túnel, o jogador paga três cartas desta cor. (E2)(RN2)(RN3)(RN4)
         
Fluxos de Exceção:

E1. O sistema informa que o jogo será finalizado e solicita confirmação da operação. (RN1)
            E2. O jogo trava.

Regras de Negócio:

RN1. O jogo será encerrado somente se o jogador confirmar que deseja finalizá-lo.
RN2. Se uma das cartas for coringa o jogador paga um ou uma carta da cor escolhida para a construção do túnel.
RN3. O jogador pode utilizar um ou mais coringas para substituir cartas coloridas.
RN4. O jogador não tem a(s) carta(s) para pagar então recupera suas cartas utilizadas na tentativa da construção e encerra seu turno.

E mais uma parte está completa, agora estou fazendo  a primeira estrutura do Diagrama de Classes que provavelmente será modificado até próximo a entrega e também começarei o Diagrama de Atividades que poderá ser definitivo.



Novas funcionalidades

Olá


Finalizadas as principais funções das classes de música da engine, remodelei alguns aspéctos. Agora cada módulo é responsável por inicializar seus próprios submodulos, ou seja, paSDLWindow inicializa o vídeo e o timer, enquanto que a paSDLAudio inicializa o audio.

O que acontecia antes é que a paSDLWindow recebia as flags referentes à configuração inicial da SDL no construtor. Havia também outro construtor que não recebia as flags e dava um SDL_INIT_EVERITHING, o que era muito legal para a antiga paSDL, mas não para a nova engine totalmente refatorada.

Também inseri opções para modificar o volume de todos os canais e músicas ao mesmo tempo na paSDLAudio, além de funcionalidades para tocar um sfx por um determinado período desejado.

Ainda criei um método para se setar o volume da música em fatores de 0 a 100 (diferente da SDL, que vai de 0 a 128). Infelizmente não encontrei maneiras simples de fazer um retorno bem sucedido desse volume no valor da escala de 0 a 100, ficando a funcionalidade getVolume apenas tendo a possibilidade de retornar o volume na escala sdl. Graças a isso, estou pensando seriamente em remover essa funcionalidade e manter somente a padrão para evitar confusões.

Agora estou pensando maneiras de melhor organizar o encapsulamento da paSDLMusic, para que cada música possua seu próprio volume específico.

paSDLSoundEffect

Olá a todos!

Consegui, após muito esforço e trabalho, finalizar a classe de sound effects (sfx) sincronizada com o resto das classes de som. Lembra do tempo perdido com as funcionalidades inúteis de paSDLMusic? Pois é, aqui elas foram aproveitadas!

Diferente das músicas, existem vários sfx tocando ao mesmo tempo no jogo, ou seja, agora a dificuldade de controlar o que se esta reproduzindo e como se esta reproduzindo é bem maior.

Inicialmente, utilizei a mesma estrutura de paSDLMusic para criar a classe de sfx, modificando apenas as funções e pesquisando as melhores maneiras de controlar cada sfx. Felizmente, a SDL_Mixer oferece opções para controle dos canais, cabendo a mim atribuir cada sfx a um canal.

O problema surge quando os sfx são deletados e quando se necessita utilizar a maldita função que chama um ponteiro para uma função quando o sfx estiver totalmente reproduzido para atualizar a variável boleana stoped (som_parado != som_tocando && som_pausado ).
Diversas foram minhas tentativas de deixar esta classe da forma mais bonita e encapsulada possível, mas não obtive sucesso. Tive que recorrer às funções friend, declarando também esta classe como friend de paSDLAudio para ter acesso ao valor da quantidade de canais desejados pelo usuário, e também recorrer às famosas variáveis estáticas.

O meu plano inicial era criar um alocador dinâmico, onde se pudesse inserir quantos canais se bem entendesse e a memória suportasse. O maior problema disso tudo, além do trabalho árduo com alocação dinâmica de memória, seria usar corretamente a função Mix_AllocateChannels, pois não há como reduzir o canal exato da alocação, e tentar ferir esse princípio da SDL_Mixer seria um trabalho árduo demais.

A solução que encontrei foi estabelecer um limite máximo de 1000 canais, e permitir ao usuário inserir um valor menor se o mesmo desejar.

Dessa forma, consegui especificar o tamanho dos vetor contendo a boleana que indica se o sfx está parado ou não, e do vetor contendo boleanos para cada posição dos canais, indicando falso para não ocupado e true para ocupado. A partir dai fica fácil prever: Usa-se um for para percorrer o vetor boleano, e quando encontrar uma posição vazia, atribui esse numero ao canal utilizado pelo sfx correspondente, atualizando os valores do vetor para true (ocupado) e false (não ocupado) quando o objeto for deletado.

Outro problema que tive foi com relação a sempre tediosa função que chama o ponteiro de uma função quando o sfx estiver terminado. No caso das músicas é fácil porque só uma música toca por vêz, mas como controlar uma variável estática só para todos os sfx?

Além desse problema, perdi um bom tempo tentando desvendar a sintaxe para passar a função como parâmetro para a função SDL que recebe o ponteiro (Mix_ChannelFinished), já que esta recebe uma função que recebe um parâmetro (diferentemente do caso anterior em que nao havia parâmetro recebido). No fim das contas, acabei descobrindo que era exatamente a mesma coisa e fiquei alguns minutos me recuperando da tremenda humilhação perante a máquina rsrsrsrs...

A solução encontrada foi criar uma variável estática que aponta para sua respectiva variável stoped. Tendo o tamanho fixo do vetor e os pontos de atualização do vetor boleano dos canais, ficou fácil saber o momento exato de anular e apontar essa variável.

Ainda tive problemas de compatibilidade com alguns formatos aceitos pela SDL_Mixer. Portanto, ainda estou estudando maneiras de evitar o uso de músicas de formato FLAC e MOD, limitando apenas os formatos que são recomendados para as funcionalidades. Também quero adicionar alguma maneira de tocar sfx em MP3, mas provavelmente estabelecerei o padrão MP3 para músicas, e OGG e WAV para sfx.

Ainda tive problemas com um maldito caso de "_BLOCK_TYPE_IS_VALID pHead nBlockUse" causado quando deletamos de maneira errada alguma variável alocada. Demorei umas duas horas para perceber o erro, que também foi estúpido:

ERRADO:
fileName = new char[tmp] + 1;

CERTO:
fileName = new char[tmp + 1];

Felizmente o encontrei e tudo ocorreu bem.

Ainda pretendo adicionar mais algumas funcionalidades à paSDLAudio que está pouco trabalhada, e também explorar alguns recursos interessantes para sfx, como panning.

Até a próxima!

sexta-feira, 18 de maio de 2012

paSDLAudio e paSDLMusic

Olá leitor!

Após um certo tempo sem postar muitos avanços, venho hoje falar sobre como foi o desenvolvimento inicial da parte de som da engine. Primeiramente eu precisava reunir informações a respeito de como implementaria essa parte da engine, quais funções utilizaria, quais efeitos exploraria e qual seria o sistema de classes.

Após um estudo entre a SDL_Audio e a SDL_Mixer, preferi a Mixer, devido às suas funções mais aprimoradas. Assim, planejei dividir a engine de som em três partes:

Classe paSDLAudio: Responsável por inicializar a SDL_Mixer, bem como oferecer as constantes e ajustar parâmetros globais para as demais classes de som (ex: volume para Música e Efeitos).

Classe paSDLMusic: Responsável por gerar instâncias com métodos envolvendo a reprodução de músicas, bem como os parâmetros das músicas do jogo.

Classe paSDLSoundEffects: Responsável por gerar instâncias com métodos envolvendo a reprodução de efeitos, bem como do gerenciamento de seus canais e os parâmetros dos efeitos no jogo.


Neste post, irei abordar o desenvolvimento das duas primeiras classes, envolvendo o que deu certo e as centenas de coisas que deram errado (rsrsrsrsrsrs).

Iniciando o desenvolvimento da paSDLAudio, decidi por fazê-la oferecer uma preparação para o audio no jogo, bem como o suporte para as funcionalidades das outras classes de som. Por enquanto, essa classe possui somente a configuração da frequência do audio e da saída mono ou stereo.

O maior problema foi com a paSDLMusic. Primeiramente, planejei a classe para que essa contesse métodos para inicializar os atributos da música na classe, bem como seu conteúdo (do tipo Mix_Music). Os principais métodos da classe que desenvolvi ficaram totalmente funcionais. São eles:

1- reload: Recarregar a música, passando um novo endereço.

2- play: Tocar a música, passando o número de vezes que a mesma repetirá e o número de segundos do FadeIn. Os valores padrão são infinitos loops para o loop, e 0 para o tempo.

3- stop: Para a música. Recebe um tempo em segundos identificando o FadeOut. Por padrão é 0.

4- set/getVolumeMusics: Retorna e recebe o volume das músicas (alterando este parâmetro, toda os volumes das músicas são alterados).

5- isPlaying / isPaused / isStoped: Retorna o estado atual dos casos especificados.

6- pause: Pausa a música.

7- restart: Reinicia a música (sem fadeIn, mas reinicia o número de loops inseridos no primeiro play).


A maior dificuldade na criação dos métodos acima foi sincronizá-los de forma a oferecer uma boa estrutura anti-usuário. Por exemplo, o que aconteceria se eu apertasse pause e depois stop? Com o retorno padrão da SDL_Mixer, pause continuaria verdadeiro. Outro problema foi a ausência do estado stop, tendo eu que criá-lo baseando nos resultados das funções Mix_PlayingMusic() e Mix_PausedMusic().


Parece terminado, certo? Errado! Ainda falta estabelecer uma forma de comunicar o usuário quando a música termina, além de acrescentar a um contador o número total de músicas que ja foram criadas, para não exceder um certo limite (8 no caso).

Para tal, tive que utilizar a função Mix_HookMusicFinished, a qual recebe um ponteiro para uma função que será chamada quando a música finalizar.
A minha primeira tentativa de resolver o problema foi passar um ponteiro para um método, cuja sintaxe foi descrevida pelo autor que estava lendo o tutorial como "temida até pelos experts em programação avançada de ponteiros". Consegui programar o ponteiro de métodos, mas infelizmente a função só recebe realmente ponteiros de funções.

Meu segundo passo foi criar uma função do tipo friend, a qual recebesse uma referência a um objeto da classe para que eu pudesse modificar as variáveis privadas quando o fim da música fosse chamado. O grande problema é que a tal função também não aceita apontar para funções que recebem parâmetros.

Sem sucesso nas tentativas, comecei a tentar pelo caminho dos métodos e variáveis estáticas, já que estes podem ser acessados facilmente em qualquer lugar. Então, descobri como utilizar métodos estáticos, e também como fazer a misteriosa declaração de variáveis estáticas. Para declará-las com sucesso, foi necessário definí-las no escopo privado da classe e no topo do arquivo cpp.

Feitas todas as modificações necessárias dentro de todos os métodos da classe para substituir algumas variáveis e métodos para estáticos, tudo parecia correr bem, quando outro problema surge: Em caso de loop, a SDL_Mixer só chama a função quando todos os loops tiverem sido rodados, o que estragava completamente boa parte do que tinha feito.
A solução que encontrei foi gerenciar a reprodução uma a uma, mandando reproduzir uma vez a cada vez que a música acabasse, até um contador atingir o número total de loops desejados. O problema dessa abordagem foi que estabeleci praticamente a maior responsabilidade de gerenciamento para a função passada como parâmetro em Mix_HookMusicFinished. Isso se tornou um problema, pois estava perdendo o controle dos estados da música (pausada, parada, reproduzindo, finalizada).

Lá vou eu mais uma vez. Após resolver todos estes pepinos, surge outro problema: Acabei tendo a necessidade de definir o endereço das músicas como estático para que a função tivesse acesso. Isso feria completamente o sentido da classe, uma vez que cada objeto é uma música. Se a variável contendo as músicas for estática, então não existem objetos com músicas diferentes.
Tentei resolver esse problema aplicando um friend entre as classes paSDLAudio e paSDLMusic (hahaha, aprendi muitas coisas novas com essa maldita classe de som) para ter acesso a variáveis privadas de gerenciamento, e criei um vetor boleano que sinalizasse se a posição do vetor de músicas estava ocupada. Então, percorria-se todo o vetor em um for buscando posições false, e a primeira encontrada era atribuída como ID para aquela música.

Parece genial, mas é estúpido, pois para acessar a música certa eu também precisaria deixar o ID como estático rsrsrsrs... Nesse ponto, a brincadeira ja estava começando a perder a graça...

Ainda tentei, ao invés de usar a variável estático com o nome da música para reprodução, um ponteiro estático para a música atual, mas já estava tendo sérios problemas com os principios do KISS e YAGNI para fazer algo que simplesmente poderia ser resolvido facilmente pelo usuário...

Ou seja, se o usuário quer controlar quando a música termina, basta que ele mande a música ser reproduzida apenas uma vez usando play, e, quando isStoped() retornasse true, o usuário reiniciasse a reprodução usando restart(). É, as vezes uma funcionalidade a menos resolve todos os problemas...

Apesar dos problemas, consegui exercitar três conceitos que não utilizava (métodos e variáveis estáticas para classes, friend de funções e classes e ponteiros para funções e métodos). Também me rendeu uma boa refatoração, e centenas de testes da classe. No fim deu tudo certo!


Pretendo continuar implementando a parte de som, agora com foco nos SoundEffects.

Até lá!

segunda-feira, 14 de maio de 2012

Produções do fim de semana

Olá!

O fim de semana, a segunda e terça feira até agora não foram muito produtivos. Isso porque estive lidando com algumas coisas que eu não dominava muito bem. Foquei em aprimorar meus conhecimentos sobre a biblioteca SDL_Mixer, sistemas básicos de colisão, blend, e, na parte da documentação, diagramas de casos de uso, realizações de casos de uso, diagrama de atividades e diagrama de classes e objetos.

Os assuntos abordados que envolvem a documentação foram estudados a fim de auxiliar a Keli na revisão dos documentos e diagramas que já estão prontos.

Os assuntos referentes à colisão foram estudados seguindo o planejamento da busca pelas melhores maneiras de detecção de colisão no Jogo Ticket To Ride e no Gerador de Mapas. Estudei três técnicas:

- Colisão por Axiis Aligned Bouding Boxes.
- Colisão por Bouding Circle.
- Colisão de Bouding Circle com AABB.

Ainda, estudei a biblioteca SDL_Mixer, buscando separar as funcionalidades mais interessantes para a reprodução de sons para a engine. O desenvolvimento das classes de som já foi iniciado por mim hoje!

Pretendo até o final da quarta-feira estar com as classes de audio prontas e testadas, para então revisar o final da sprint da semana com a Keli e especificar as próximas tarefas.

sexta-feira, 11 de maio de 2012

Alterações na Documentação



Olá!!!

Neste post, informarei a situação da documentação e as alterações recentes.

Tivemos várias alterações na Estimativa de Desenvolvimento, tanto na parte de implementação, quanto na documentação em si. Levando em conta que minha responsabilidade é atualizar e balancear essas mudanças, sendo estas decididas em equipe, precisei modificar o Documento de Arquitetura e, consequentemente, as tarefas no Pivotal Tracker.

As principais modificações foram realizadas em:

* Requisitos Funcionais: Adição de itens no tópico Menu.

* Estimativas de Desenvolvimento:

  - Estória: Programação intermediária da engine: foi adicionada a tarefa "Programação da classe de tratamentos de erros da engine".

 - Estória: Elaboração inicial do jogo Ticket To Ride: a produção da arte foi melhor dividida em tarefas. Assim, nesta estória foram adicionadas as tarefas "Produção dos principais elementos da arte do jogo - Menu." e "Produção dos principais elementos da arte do jogo - Sprite das cartas de construção (trens coloridas)."

- Estória: Desenvolvimento intermediário do jogo Ticket To Ride: seguindo a divisão de aspectos de arte, adicionei as tarefas. "Produção dos elementos da arte – HUD Padrão", "Produção dos elementos da arte – HUD (Personagens)", "Produção dos elementos da arte – Tela(s) de Regras" e "Produção dos elementos da arte – Telas do Jogo".

  - Estória: Documentação: nesta estória havíamos definido certas tarefas sem ter o conhecimento do Documento de Arquitetura, pois este foi passado a nós após a realização da Estimativa de Desenvolvimento. Com o documento divulgado, resolvi substituir as tarefas anteriores pelos tópicos (vistos anteriormente no post Escopo geral da documentação) do referido documento.  

  - Estória: Programação avançada da engine: a pedido do Ráfagan, fiz a substituição da tarefa "Inserção de recursos OpenGL para produção de jogos 3D." pelas tarefas "Implementar cálculo de iluminação.", "Implementar efeitos de Blend,", "Implementar carregamento de modelos 3D." e "Implementar colisão 3D."

* Além de recalcular a soma das complexidades de cada estória alterada e fazer a substituição ou inserção das tarefas na ferramenta Pivotal Tracker, aproveitei para colocar etiquetas(labels) referentes ao nome de cada estória do Documento de Arquitetura:

- Estória: Programação básica da engine. > programação básica

- Estória: Programação intermediária da engine. > programação intermediária

- Estória: Programação de um Gerador gráfico de mapas T.T.R. com leitura de arquivos XML. > programação gerador gráfico.

- Estória: Elaboração inicial do jogo Ticket To Ride. > elaboração inicial.

- Estória: Desenvolvimento intermediário do jogo Ticket To Ride. > desenvolvimento intermediário.

- Estória: Fase final de produção do jogo Ticket To Ride. > fase final.

- Estória: Documentação. > documentação.

- Estória: Programação avançada da engine. > programação avançada.

- Estória: Teste do game. > testes.

- e + etiquetas de divisões: programação, arte, som e extras para facilitar a pesquisa por tipo de tarefa além da estória a qual já pertencem.

* Diagrama de Casos de Uso: ao realizar a tarefa de Realização de Casos de Uso seguindo este diagrama, senti a necessidade de fazer algumas alterações, como adicionar o caso de uso "Visualizar Regras" diretamente como opção do menu, e não apenas na animação programada para aparecer em determinado tempo após não ocorrer nenhum evento no menu; E um novo caso de uso chamado "Iniciar Partida" entre o sorteio da ordem dos jogadores e receber cartas para ficar mais claro o inicio da partida.

Por enquanto são estas, mas podem aparecer mais alterações, pois ainda estou fazendo a Realização dos Casos de Uso referentes a este diagrama.

Assim que terminar esta tarefa poderei comentar se houveram novas alterações.

Refatoração Concluída

Olá!

Das refatorações que faltavam, nenhuma falta mais!

paSDLWindow deu origem à nova classe paSDLEvent como comentado no post anterior, paOpenGL se tornou paOpenGLWindow, dando origem às classes paOpenGLDraw, sendo esta responsável pela configuração do desenho na tela (iluminação, blend, configuração de texturas,...); e paOpenGLTransform, responsável por realizar as transformações básicas e inversões de matrizes.

Também trabalhei para melhorar o sistema de proteção das classes,  declarando construtores como explicitos, colocando os construtores de cópia padrão no escopo private, e implementando outros quando necessário.

Criar uma nova classe para o gerenciamento do desenho em OpenGL ainda necessita de alguns ajustes, mas está quase lá.

Pretendo daqui pra frente focar na finalização da classe paOpenGLDraw, criar um exemplo para teste das novas alterações na engine que ainda não foram testadas, e dar algumas modificações no modo de carregamento de texturas e controle da câmera, além de modificar a classe paSDLEvent para comportar um modelo de eventos mais simples para o usuário.

Terminadas essas alterações, estarei focando no desenvolvimento de um modelo de colisão por seleção usando OpenGL, e se eu conseguir, o próximo passo será desenvolver o gerador de mapas!

Até lá!

Nova funcionalidade: paSDLEvent

Olá pessoal!

Hoje eu consegui uma proeza que ha tempos estava querendo fazer. A captura de eventos da engine!!!

Quando inciei a programação da mesma, havia planejado três tópicos inciais: Desenho na tela, GameLoop e captura de eventos.

Os dois primeiros eu consegui, porém o terceiro tópico era sempre adiado, por motivos de importância maior, como, por exemplo, me atualizar na matéria de OpenGL. Essas obrigrações acabaram me obrigando a criar uma estrutura simples de captura de eventos.

Mas isso terminou hoje. Com a refatoração da engine, a próxima parte a ser refatorada da paSDLWindow seria os métodos com tratamento de eventos. Nesse momento, a necessidade de criar uma classe para tratamento de eventos formalizada se tornou iminente.

No meu planejamento inicial, o objetivo era basicamente programar uma estrutura do tipo Polling, onde todas as teclas seriam testadas quanto ao seu estado: ativo, pressionado ou liberado, muito semelhante ao o que acontece na Chien2D. Essa abordagem permite que multiplos eventos do teclado (como o pressionar de várias teclas ao mesmo tempo) seja efetuado mais facilmente, abordagem esta que a operação manual de captura de eventos usada pela SDL não comporta.

Para quebrar a dificuldade dessa implementação, a dividi em três dificuldades:

1- Capturar um evento qualquer;
2- Capturar um evento das teclas desejadas;
3- Capturar um evento de uma tecla desejada levando como consideração as estruturas ativo, pressionado e liberado.

Na primeira tarefa, implementei uma estrutura básica de captura de eventos, recebendo os casos de SDL_KEYDOW, SDL_KEYUP, SDL_QUIT, SDL_MOUSEMOUTION, SDL_MOUSEBUTTONDOWN e SDL_MOUSEBUTTONUP.

Para a segunda tarefa, implementei uma estrutura de enumeração, contendo os nomes das constantes das teclas que a engine irá suportar. estas constantes são:


K_UP, K_DOWN, K_RIGHT, K_LEFT, K_ESC, K_F1, K_F2, K_F3, K_F4, 
K_A, K_B, K_C, K_D, K_E, K_F, K_G, K_H, K_I, K_J, K_K, K_L, K_M,
K_N, K_O, K_P, K_Q, K_R, K_S, K_T, K_U, K_V, K_W, K_X, K_Y, K_Z, 
K_0, K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9,
K_ENTER, K_SPACE, K_LALT, K_RALT, K_LCTRL, K_RCTRL, K_LSHIFT, K_RSHIFT, K_ENDBUTTON

M_LEFT, M_RIGHT, M_MIDDLE



Por fim, a captura levando como consideração as tags mencionadas. Para tal, precisei implementar structs que contivessem as três tags como variáveis boleanas, e atualizá-las a cada pressionar de teclas, sendo que cada tecla possui sua própria struct. no caso das teclas liberadas é fácil, mas como identificar se uma tecla está pressionada ou ativa?

A solução que implementei foi inicializar a cada gameloop os estados das teclas pressionado e ativo para falso, ou seja, para que o estado pressionado voltasse a ser verdadeiro, o usuário se obrigaria a apertar diversas vezes o evento da tecla para alterar seu valor boleano de pressionado. Já o estado ativo não era ressetado no inicio de cada GameLoop, mas apenas quando a tecla fosse liberada.

Quando implementei a classe no jogo, eu somente tive que mencionar a captura de polling da mesma na camada mais acima de abstração da engine (ou seja, em paGameLoop), e o usuário se preocuparia apenas em capturar o estado atual da tecla desejada por meio de um ponteiro.

Por hoje é só. Ainda pretendo implementar uma interface secundária de captura de eventos para os usuários que não forem utilizar a paGameLoop.

Até a próxima!

quinta-feira, 10 de maio de 2012

Demais alterações

Olá!


Terminada a detecção dos erros anteriores e a organização da classe paGameLoop, iniciei a refatoração da classe paSDL. Pra começar troquei seu nome para paSDLWindow, de forma que os métodos relacionados a tratamento de eventos e desenho e carregamenta de surfaces necessariamente devem ser removidos. Como a interface de tratamento de eventos ainda não está completa, decidi começar pelo desenho.

Esse processo deu a luz a uma nova classe: paSDLDraw. Ela é responsável por carregar e desenhar qualquer coisa que envolva a SDL. Os maiores trabalhos que tive com ela foram remodelar a classe paSDLSprite para que esta não mais acessasse a classe paSDLWindow para desenhar, e também na classe paOpenGL, a qual desenha as texturas usando o tratamento de inversão de píxels com a SDL.

Meus próximos objetivos agora são finalizar a refatoração da classe paSDLWindow, assim como as da paOpenGL; e depois iniciar a revisão do código referente à classe paCamera, que ainda não está completa!

Até a próxima!

Refatorações e Correções de problemas

Olá a todos!

Nessa quarta-feira (09/05) me dediquei inteiramente a buscar maneiras de melhorar o código da engine para poder seguir em frente. Os maiores problemas envolvidos a essa iniciativa envolviam o fato de que muitas implementações mais novas estavam pouco encapsuladas (por exemplo, a classe paGameLoop acabava deixando de fazer algumas funções mais complexas, deixando essa responsabilidade para o usuário).
Outros problemas referiam-se às classes paSDL e paOpenGL. O maior deles era que a classe openGL é filha de SDL. Pensando nas relações hierarquicas "É UM", essa assertiva se torna confusa.

Buscando resolver essas coisas, comecei inicialmente tentando modificar a classe paGameLoop para fazer algumas funções de ajustes da janela, como inicializá-la, desabilitar o modo de controle de GameLoop padrão, configurar o desenho, dentre outras. Ou seja, o usuário que não desejar implementar as configurações desejadas para a janela e para a engine manualmente, poderá fazer uso desses métodos de configuração.

Nessa altura do campeonato, alguns bugs e comportamentos estranhos estavam me incomodando (para uma maior visão sobre o problema abordado a seguir, leia o post anterior). O primeiro deles envolvia a lentidão da compilação após a inserção das novas configurações de projeto ja comentadas anteriormente. Em seguida vinham erros msiteriosos que apareciam nos includes e em algumas regiões do código, mas que, ao compilar, o executável era gerado com sucesso. O ultimo problema envolvia a necessidade de gerar um novo arquivo lib a cada modificação na engine, além de que muitas vezes parecia que os arquivos acessados pelos jogos não eram os mesmos que os arquivos da engine que estava modificando

Como havia feito muitas modificações no projeto, estava difícil saber de onde vinham todos esses erros. E depois de tentar resolvê-los de centenas de maneiras diferentes, acabei desistindo e voltando para a abordagem anterior de um projeto só com a engine e os jogos em conjunto. Enquanto programava no código desta maneira, descobri que o motivo da lentidão na compilação eram os propertie managers que não faziam parte do computador que eu estava usando. Agora sei que mesmo funcionando, não é a melhor escolha manter os propertie managers de ambos os computadores no projeto.

O outro caso dos erros misteriosos se resolveu quando eu passei a dizer o endereço exato dos arquivos do tipo header no include. Como dividi o projeto em uma pasta include e outra para a lib, mantendo os arquivos cpp junto ao arquivo de projeto do visual studio, os arquivos .h, mesmo estando juntos, eram sinalizados como arquivos não encontrados pelo visual studio. O intrigante é que a teoria de que estes arquivos estavam no local certo se fazia satisfeita quando a compilação rodava normalmente.

Por exemplo, o include abaixo no paSDLSprite.h era sublinhado pelo VS como um erro:

#include "paSDL.h"

Engraçado para arquivos localizados no mesmo lugar... Mas a solução para satisfazer as vontades do VS está em especificar o endereço do arquivo de acordo com o local onde se encontra o arquivo do projeto:

#include "include/paSDL.h"


Por enquanto é só. No próximo post especificarei as demais modificações que fiz na engine...

terça-feira, 8 de maio de 2012

Nova organização do projeto no VS 2010

Olá leitor!

Hoje vou falar sobre o processo de organização que o Projeto passou dentro de seu ambiente de desenvolvimento: A IDE Visual Studio 2010!

No início do desenvolvimento da engine, o ambiente estava apenas dividido basicamente em uma pasta para os headers files e outra para os cpp files.
Com o aumento das classes e a integração com a biblioteca de matemática de vetores do professor V. Mendonça, foi necessário criar pastas dentro do projeto que o dividissem em 4 partes:

a) .h paEngine;
b) .h math
c) .cpp paEngine;
d) .cpp math

Para um projeto único, esta abordagem é suficiente. Porém, o que estava acontecendo é que eu utilizava a engine em diversos projetos: TicketToRide, Protótipo3D, Gerador de Mapas e exercícios OpenGL, sem contar as trocas de versão com a Keli.

Consequentemente, como a engine estava em fase de desenvolvimento, diversas alterações se faziam necessárias, resultando em um problema de controle de versão ideal para a engine.
A primeira solução foi trocar o método manual de controle de versão que estavamos utilizando pelo controle usando o GIT, mas ainda sim correriamos o risco de acabarmos esquecendo de atualizar a versão, e toda e qualquer ateraçãozinha acarretaria em uma nova versão também.

A solução que encontrei para o problema foi criar um projeto no Visual Studio do tipo Static Library. Com ele, eu teria a engine focalizada em um único local e o problema de alteração mútua da mesma se resolveria.
Resolvido o problema, ainda de quebra acabei fortalecendo ainda mais o encapsulamento do projeto, e finalmente formalizei a engine em um formato simples para o compartilhamento da mesma com minha colega Keli.

Resolvido o problema maior, ainda tive que dar conta de outro detalhe: Os chatíssimos Includes!!!

Como utilizo mais de um computador para trabalhar, constantemente é necessária a mudança dos endereços das variáveis de ambiente responsáveis por linkar as libs ao programa. Isso se tornou ainda mais complicado no caso da engine modulada em dois projetos, pois para trabalhar com ela necessitaria não mais atualizar os linkers de um projeto (coisa que ja é muito chata de se fazer), mas sim de dois! Se trocasse de computador 3 vezes ao dia, teria 6 alterações de linker!

A solução encontrada foi criar arquivos do tipo Property Manager, os quais possibilitam que se forneça todas as configurações de propriedades do projeto no mesmo, e em seguida incluí-lo ao projeto em questão. Ou seja, configurei dois arquivos Property Manager, um para cada computador, e dessa forma só preciso agora incluir ambos no projeto e adeus includes chatos...

Por hoje é só!
Até a próxima...

sexta-feira, 4 de maio de 2012

DOCUMENTAÇÃO
Documento de Arquitetura do Jogo (6)

Olá!
   Continuando com a descrição dos aspéctos do Documento de Arquitetura, hoje veremos como foi construído o sexto tópico, intitulado Estimativa de Desenvolvimento, por nós, onde foi utilizado o método Planning Poker. Também será abordado como este planejamento foi integrado à ferramenta Pivotal Tracker, sendo este um ambiente de gerenciamento de projeto baseado na metodologia Scrum.

Sobre Planning Poker:
   É um Método de estimativa que usa como base o conhecimento dos desenvolvedores para estimar a quantidade de trabalho necessário para realizar o projeto.
   Para isso, são feitas estórias com suas devidas tarefas. Cada tarefa tem seu peso e a complexidade estimada, sendo esta última decidida pela própria equipe de acordo com uma escala pré-estabelecida.
    Geralmente se utiliza um baralho com os valores da escala para ser jogado durante a reunião com a equipe, definindo assim através de uma discussão entre os membros o valor mais adequado. No caso de divergências sem consenso, o maior valor atribuído será o levado em consideração na documentação.

(Figura 1 - Exemplo de baralho Planning Poker)
(Figura 1 - Exemplo de baralho Planning Poker)

Modelo de Estórias:

Estória 1: <descrição da estória>
Tarefa 1: Criação de Menu
Complexidade: 1
Tempo: 2 horas

Estória 2: <descrição da estória>
Tarefa 1: Criação de Menu
Complexidade: 2
Tempo: 4 horas
Tempo Total = 2h + 4 hs =  6 horas

Utilizando o Método:
     Pensamos em tudo o que achamos necessário para a realização do projeto, e em seguida analisamos as tarefas semelhantes e as agrupamos, formando as estórias. Utilizamos a escala de Fibonacci (1,2,3,5,8,13,21) para dar os pesos a cada uma das tarefas da estória.

   Segue uma de nossas estórias e suas tarefas com devidos pesos e atribuição de horas:

Estória: Programação básica da engine.
Descrição: Criação inicial de uma engine que possibilite a criação de protótipos usando OpenGL.
· Tarefa 1: Programação da interface de configuração da janela para WINDOWS. (3)
· Tarefa 2: Programação do desenho básico na tela com SDL. (1)
· Tarefa 3: Programação de uma interface de desenho de Sprite em quadros. (13)
· Tarefa 4: Programação do Game Loop. (21)
· Tarefa 5: Programação da captura de eventos usando Polling (Atenção especial para o MOUSE). (21)
· Tarefa 6: Programação da interface básica de configuração de desenho em OpenGL.(3)
Complexidade: 62
Tempo: 150 homens/hora.

Sobre o Pivotal Tracker:
É uma ferramenta online muito utilizada para projetos ágeis. Nela os desenvolvedores controlam as sprints através do peso atribuído a cada tarefa e a velocidade selecionada.
Mais informações: https://www.pivotaltracker.com/

Utilizando a Ferramenta:
Como a ferramenta trabalha com o escopo de uma única sprint por vez, cada tarefa criada anteriormente utilizando a metodologia é cadastrada e não as estórias.
Assim, ao clicar em “ADD STORY”, cadastramos uma de nossas tarefas no painel (Figura 2) que aparece já na aba “ICEBOX”; e nele colocamos nome, tipo, pontos (complexidade), responsável, descrição, etiquetas, tarefas (para controle de quem estiver implementando), atividades e até anexos se necessário.
(Figura 2 – Painel de cadastro da tarefa-estória)
(Figura 2 – Painel de cadastro da tarefa-estória)

          Ao cadastrar todas as tarefas já definidas, basta definir a velocidade das iterações e arrastar as tarefas para “CURRENT” ou “BACKLOG”, e estas se organizarão de acordo com a complexidade e a velocidade.

(Figura 3 – Visão Básica da ferramenta Pivotal Tracker com tarefas do projeto)
(Figura 3 – Visão Básica da ferramenta Pivotal Tracker com tarefas do projeto)

Confira o link do nosso projeto cadastrado como Public no Pivotal Tracker: 

Link: https://www.pivotaltracker.com/projects/534473

Até a próxima!