terça-feira, 22 de abril de 2008

O padrão Visitor

Enquanto vou brincando com o código, tentando organizar as classes de uma forma legal e intuitiva, me deparo sempre com dois problemas: aumento da complexidade e reescrita de código.
Da parte do aumento de complexidade, é mais o caso de experiencia mesmo.
A reescrita de código, tenho me deparado com um problema em particular: Eu crio objetos que representem sólidos. Alguns deles ficarão parados na tela, como paredes ou caixas. Outros sofrerão a ação da gravidade, do vento ou qualquer outra situação que altere o comportamento desses objetos.
Como coloquei a dois posts atrás, implementar essa gravida é bem simples, 1 linha no método Update() e está pronto. Para testar,criei uma classe de sólidos que caem e todas as que tivessem esse comportamento, herdariam dela, algo bem simples para começar. Dai começaram os "e se...?". Se para cada tipo de solido que eu quisesse criar, teria que criar uma nova classe e herdar dessa "objeto que cai". Quantas classes eu teria ao criar cada classe para um comportamento? Se eu quisesse que uma caixa caia?
Seria muito interessante se eu pudesse acoplar esse comportamento ao objeto quando eu quisesse. Se eu tenho uma caixa e quero que ela passe a cair, bastaria dar a ela esse comportamento e tudo funcionaria bem. Como não da para fazer herança múltipla, comecei a pesquisar nos padrões do gof por algum que me ajudasse a implementar isso. Por sorte, ele existe!
O padrão Visitor é uma solução para separar o algoritmo da estrutura. Uma das vantagens desse padrão é a habilidade de adicionar novas operações a uma estrutura já existente. Com ele, podemos ter a classe ObjetoSolido e o comportamento de queda em uma classe Gravidade, separada da estrutura do ObjetoSolido. Isso é feito através de uma interface, onde o objeto que vai executar esse método da classe do comportamento, passa uma referencia dela mesmo juto dos parâmetros normais da classe.
No caso desse exemplo, teríamos:

Visitor gravidade = new Gravidade(); //esse é o nosso visitor, responsável pelo comportamento de queda.
Solido solido = new Solido("caixa"); //solido que recebera o comportamento
solido.accept(gravidade); //recebe o comportamento Gravidade


Internamente, o método accept(Visitor visitor) de Solido faz o seguinte:

public void accept(Visitor visitor) {
visitor.visitSolido(this);
}

Ao passar para o Visitor uma referencia de si mesmo, o visitor pode acessar os métodos e atributos públicos dessa classe, que no nosso caso, vai adicionar a aceleração da gravidade ao Solido. Assim como o comportamento de queda foi adicionado, outros também poderiam ser feitos da mesma maneira, como movimentação através do teclado, sons... as possibilidades são infinitas.

Para quem quiser saber mais sobre o Visitor, segue o link (inglês): http://en.wikipedia.org/wiki/Visitor_pattern

segunda-feira, 21 de abril de 2008

Um pouco mais do projeto

Voltando a falar do projeto, o que era a ideai inicial do blog, entregamos o anexo II. Com ele, de acordo com o nosso cronograma, segue a descrição do projeto.
Alguns pontos principais são tratados nessa descrição: O que será o projeto, em que será implemenado, o mercado de jogos e alguns jogos relacionados.
Para isso, rolou um pesquisa e algumas coisas interessantes apareceram. Seguem algumas:

  • Em 2006, o orçamento da indústria de jogos ultrapassou $38 bilhões, superando o do cinema.
  • O produção brasileira de jogos ainda é pequena, comparada aos demais países, mais forte na área de celulares. Isso em muito se deve ao problema de pirataria das mídias para console e pc.
  • Em 2007, a média salarial dos programadores, nos Estados Unidos, iam de U$57,000 para novatos chegando até quase U$120,000 para diretores técnicos com mais de seis anos de experiência.
Um mercado carente de trabalhadores bem formados, que está em crescimento, com salário inicial de U$57,000 anuais.
Interessou? :)

O link da pesquisa do mercado: http://www.gamecareerguide.com/

Colisão

Queria fazer um post sobre como organizar os objetos dentro do código mas eu ainda estou bem perdido nisso :)
Assim sendo, vou falar um pouco de colisão.
Falando bem por alto, algoritmos de colisão tratam de detectar quando objetos colidem (dããã), ou seja, quando um objeto encosta ou penetra outro objeto.
Para a nossa alegria, o XNA facilita a nossa vida enormemente pois na versão 2.0 passou a disponibilizar métodos para tal. Ele ja faz colisão entre sólidos (cubo, esfera...) e não-sólidos(planos, retas...), entre tipos diferentes como esferas e planos... tudo isso com um só método. Com a chamada objeto.Intersect(objetoAlvo) e pronto.
Até ai ótimo mas tudo isso não serve de nada sem o principal. O mais importante da colisão não é a detecção em si, mas a reação dos objetos que colidem.
Um exemplo para ilustrar e para facilitar, vamos trabalhar em 2D:
Um objeto Bola cai perpendicularmente em direção a um objeto Caixa. Em cada um desses objetos, eu crio um atributo _boudingBox do tipo Rectangle que irá guardar a representação geometria desse objeto. Note que o Rectangle não da para usar no teste de colisão com uma das classes que o XNA disponibiliza para tal mas vai ser suficiente para o exemplo já que seu uso é quase idêntico.
Dai, já assumimos que essa bola está acelerando sob ação da gravidade. Logo, pode-se dizer que o Update() dessa bola é:
public void Update() {
...
this.aceleracao.Y = 10f; //aceleração da gravidade
this.velocidade.Y += this.aceleração.Y; //incrementa a velocidade com a aceleração

this.Y += this.velocidade.Y; //muda a posição da bola de acordo com a velocidade
...
}

Pronto, nossa bola está caindo. Agora vamos testar, no Update de quem controla os objetos, se a bola colide com a caixa:
public void Update() {
...
if (obejtoBola._boudingBox.
Intersect(objetoCaixa._boudingBox))
{
//a bola colide com a caixa
}
...
}

Simples, não? Agora você ja sabe que eles colidem, mas e dai?
Daqui em diante temos que implementar a reação que a bola terá ao colidir com a caixa. Para isso, vamos adicionar algumas linhas no Update() da bola.
public void Update() {
...
this.aceleracao.Y = 10f; //aceleração da gravidade
this.velocidade.Y += this.aceleração.Y; //incrementa a velocidade com a aceleração

if (this.estaColidindo)
{
this.velocidade.Y = -
this.velocidade.Y; //inverte a velocidade vertial, ou seja, joga a bola de volta para cima
}

this.Y += this.velocidade.Y; //muda a posição da bola de acordo com a velocidade
...
}

Pronto. Agora toda vez que a bola bater na caixar ela vai para cima de volta. Assim, reproduzimos o "quique" da bola.

Depois dou uma explicada melhor em colisão. Seguem alguns links para quem quiser saber mais:
http://www.harveycartel.org/metanet/tutorials/tutorialA.html Muito bom, tem vários exemplos e alguns applets mostrando a reação de alguns objetos a colisão
http://www.cs.unc.edu/~geom/collide/ Alguns textos, vídeos e exemplos bem interessantes

domingo, 20 de abril de 2008

Meio sumido

Um mês sem posts...
Alguns problemas pessoais + trabalhos + semana de provas e o resultado é esse. Agora que a tempestade passou, parece que tudo volta a normalidade, inclusive as postagens.
Por enquanto é só. Proximos posts: administrando varios itens e colisão.

Obs: daqui para frente, vou começar a tratar mais da parte de programação e modelagem. Como ainda sou bem fraco com o C# e o .Net (estou aprendendo conforme estudo o XNA), peço que desconsiderem os codigos bagunçados e mal estruturados. :)

quinta-feira, 20 de março de 2008

E você reclamando de cálculo?

Sempre gostei de computador e sempre me dei bem em matemática, mesmo que não gostasse de estudar pra isso.
Quando chegou o segundo grau, a hora de decidir um curso para a faculdade, não tinha outro caminha além de informática. No entanto, a minha "revolta" com a matemática me fez escolher cursar Comunicação Social, fiz um período mas não rendeu. Aceitando meu destino, fui cursar Sistemas de Informação.
Dai, tive: Matemática Discreta I, II, Cálculo, Probabilidade...
Por fim, acabei fazendo as pazes com a matemática. Isso por que comecei a entender para que serviam todos aquelas formas, aplicando-as em algo concreto.
Isso não foi para contar o meu relacionamento com a matemática e sim, ilustrar a quem quer lidar com as áreas que envolvem informática, como tem que se dar bem com a matemática.
O motivo por trás deste post é:
A alguns dias estou pesquisando sobre algoritmos de jogo como colisão e movimento e tudo que se encontra, são fórmulas de geometria, trigonometria e física. Aquelas que vimos no segundo grau, quando o professor passava uma fórmula no quadro para descobrir a tangente e pensávamos: "Legal, assim eu sei a tangente, e dai?" ou "O gráfico da equação de 2o grau é uma parábola... minha vida mudou!". Experimente fazer um ponto andar na diagonal sem seno e cosseno. Um tiro de canhão sem usar equação de 2o grau? Ou uma query sem a mínima noção de teoria de conjuntos!
Para quem pretende trabalhar com jogos, o buraco é mais embaixo.
Matemática, geometria, trigonometria e física mecânica de ensino médio precisam ser tão, ou mais, fluentes!
Pesquisando, me deparei com coisas como quadTree, quaternion, e vários outros termos sendo falados tão regularmente nos artigos que me toquei que saber fazer uma integral bem não é mérito nenhum, pelo ao menos para quem quer entrar para a área de produção de games.
Se você pretende trabalhar com games, saiba que matemática deverá ser fluente como português ou inglês. Saber plenamente a matemática, geometria e física de ensino médio e estudar esses conceitos mais avançados são requisitos mínimos para a área, e é exatamente isso que vou fazer daqui pra frente. Matemática no café, geometria no almoço, física no jantar e uma cervejinha mais a noite, afinal, ninguém é de ferro.


terça-feira, 18 de março de 2008

Primeiras experiências

Esse final de semana, uma chuva absurda e eu a pé! Foi a deixa pra começar a estudar o XNA.
Abrindo Visual Studio, criar novo projeto, Xna Windows Game... Abre um dos n textos que peguei na internet e vamos lá!
A primeira parte é entender os métodos da classe principal do jogo, a Game1.
Simplificaram bastante as coisas, uma para carregar conteúdo, outra para carregar grafico, uma para descarregar, um loop de lógica e um loop de impressão. Tudo bem organizado mesmo.
O próprio xna já vem configurado de tal forma que ele mesmo cuida de gerar uma janela, o tipo de coisa que você passa horas fazendo em outras linguagens. Lá basta definir a altura, largura e se é fullscreen, e só! Mais que isso é detalhe. Até ai, ok.
Legal, vamos começar agora... mas por onde?
Provavelmente essa é a parte mais difícil de todas. Por fim, escolhi importar e exibir uma imagem na tela. Depois de algumas horas lendo e pesquisando, consegui e cheguei a conclusão de que é mais idiota que imaginava.
O mais dificil ali é o pontapé inicial, já que não é o tipo de estrutura que está acostumado a ver, principalmente eu que venho de web, onde os programas: começa, executam e terminam em ate 1 ou 2 segundos!
Depois, comecei a me meter a besta lendo as teclas: apertar esc e fechar o jogo, molezinha.
Sendo assim, por que não movimentar o meu desenho com as setinhas do teclado pela tela?
Incrementa o x e o y... Movimento feito!
E se eu aplicasse aceleração no movimento?
Ah, tranqüilo, pega o incremento, multiplica pela aceleração... Aceleração feita!
E nesse ritmo foi: aumento de aceleração, rotação do sprite.
Pronto, ele ja andava, acelerava e parava, e rodava mas ele não fazia uma curva, simplesmente rodava e andava de lado.
Como fazia pra ele andar na diagonal? Já que eu conseguia andar pra cima e pra baixo alterando o Y, esquerda e direita alterando o X, mas diagonal... ai bateu o estalo, momento flashback a aula da tia maricota lá no segundo grau, trigonometria!
Rezando pra São Google por mais uns minutos, tudo clareou: X*cos(angulo) e Y*sin(angulo)!
Perfeito! Num é que funcionava mesmo?!
Fui me empolgando, a chuva não dava trégua e quando vi, já entrava pela madrugada até que a cama começou a exercer uma gravidade e, o que a principio era uma barata (sim, o sprite que eu usava pro teste era uma barata), tornou-se um borrão marrom andando sobre um borrão azul. Bom, era hora de dormir.
Resultado final:

  • Básico de XNA
  • Importação de imagem
  • Leitura de Teclas
  • Movimentação
  • Aceleração
  • Rotação
  • Movimento em diagonal
  • Inércia
  • Desaceleração por atrito
A proxima será debulhar um exemplo que baixei com colisão, plataformas, pulo, gravidade. Ou seja, a base do nosso projeto final.

quarta-feira, 5 de março de 2008

O nome!

Depois de tanto apanhar, arrumamos o maldito nome. Nada como o bom e velho brainstorm (valeu Ota)!
O grande problema era arrumar um nome neutro já que o jogo não possui história, ainda.
O único certo é que será um jogo de luta, então, seguindo essa linha, saiu o nome: Kenpachi.
Aos que conhecem Bleach, qualquer semelhança não é coincidência. Claro que não é uma referencia direta mas sim ao significado. Segue um trecho do texto da Wikipédia sobre ele que explica isso:
"Ele também se deu um nome: Zaraki Kenpachi, sendo Kenpachi um título dado ao guerreiro mais forte, ou seja, o guerreiro mais forte de Zaraki."
Sendo assim, o nome se encaixa bem no contexto do jogo.

A urgência do nome era para fechar o anexo I, que deve ser entregue hoje!
Outra coisa interessante são os notes que Vitor e eu pegamos, que vai adiantar nossa vida em muito, principalmente eu que, trabalhando no rio, fica bem difícil de usar o pc em casa.
Agora com a entrega do anexo I, vai o cronograma. Assim, depois da entrega, comça oficialmente o projeto. Primeira parada: Descrição do projeto!


Para quem não conhece o personagem, texto sobre Zaraki Kenpachi está aqui.