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
terça-feira, 22 de abril de 2008
O padrão Visitor
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.
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. :)