A jornada, não tão jornada, mas, ainda uma jornada.. você entendeu ;-)

Este espaço é destinado a diversos temas, relacionados a minhas atividades ou mesmo ao cotidiano.

Monday, October 29, 2007

Tutorial de XNA - Parte III

Seguindo a minha série de artigos sobre XNA, que já abordou a estrutura inicial de um jogo, desenho de sprites e moviementação, vou abordar agora um dos tópicos mais importantes dos jogos, colisões.

Também aproveitei para incluir umas "coisinhas novas" no código dos exemplos anteiores, como transparência no sprite.
-------------------------------

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de orientação a objetos e que tenha lido a primeira e a segunda parte do tutorial.

Até agora nós descobrimos como é a estrutura de um jogo no XNA, como movimentamos os nossos desenhos e como recebemos as teclas pressionadas no teclado. Nesta parte do tutorial eu vou falar sobre um tópico muito importante dentro da área de jogos, a colisão.

Antes de falar da colisão usando o XNA propriamente dito, vou explicar a teoria por traz desse método de colisão, ai sim, eu mostrarei um exemplo desse tipo de colisão usando o XNA.

Mas, o que é “colisão”?

Dentro dos jogos, você precisa muitas vezes testar se dois objetos estão colidindo, como no mundo real. Por exemplo, se um carro do seu jogo bateu na parede, se um dos jogadores foi atingido por uma bala, se a sua espada “ultra-mega-hiper-poderosa” atingiu o inimigo, ou simplesmente se sua bolinha atingiu o chão.

Mas, lembre, nos jogos 2D todos esses objetos são imagens e para testar se eles colidiram, você não pode testar apenas as suas coordenadas de desenho. Ai é que entram as técnicas de colisão.

Um ponto importante é pensar primeiro que para cada caso existe uma técnica mais adequada, então o que eu estou mostrando aqui, pode não ser adequado ao seu jogo simplesmente pelo modo como você o implementou, ou então porque torna jogo mais lento, geralmente não há uma regra, cada caso é um caso.

Bounding Box (caixa delimitadora)

A idéia é bastante simples, nós vamos associar a cada objeto da tela uma área retangular, dada pelos limites horizontais e verticais da sua imagem. Daí, testaremos se a intersecção desses limites, resultando ou não na colisão. Ai você diz: “hein??!!” e eu digo, “ta bom vou explicar melhor :-)”

Repare na imagem abaixo, onde temos dois retângulos desenhados na tela:



Cada um deles ocupa uma área da tela delimitada por suas linhas de contorno, que são dadas pelos limites horizontais e verticais, veja a imagem abaixo:
No caso do retângulo azul, ele é determinado pelas coordenadas X1, X2, Y1 e Y2. Sendo as outras coordenadas pertencentes ao retângulo verde. Para testar a colisão basta verificar essas coordenadas. Portanto usaremos essas linhas para determinar se um retângulo colidiu com o outro.

Vamos pegar o mesmo caso da figura anterior e estudar as coordenadas, na imagem anterior X1 e X2 são menores que X3 o que significa que o segundo retângulo está ao lado direito do primeiro. Caso X1 e X2 fossem maiores que X3 o retângulo verde estaria à esquerda do azul.

A mesma comparação pode ser feita com as coordenadas Y das imagens, como Y1 e Y2 são menores que Y3, então o retângulo azul está acima do verde, em caso contrário, estaria abaixo.

Mas, para nós, o mais importante é detectar a colisão e não a localização. Essa colisão acontece quando as linhas que determinam nossos retângulos formam uma área em comum aos dois retângulos. Por exemplo:


Nesse caso, a área amarela é formada pela intersecção dos dois retângulos e nesse caso temos certeza de que os dois objetos colidiram. No exemplo temos "X1 menor que X3 menor que X2 e Y1 menor que Y3 menor que Y2” o que indica uma colisão por baixo e do lado direito.
Obs.: tive de trocar os sinais de maior e menor porque o editor do blog tava "comendo" o texto.
Então, para testar se o objeto colidiu por baixo e pelo lado direito, precisamos apenas checar se as coordenadas dele atendem a essa condição. Mas, isso não seria suficiente para nós que queremos uma colisão mais genérica, então precisamos testar outros casos.

Bom, no meu caso vou criar um método muito simples que testa se existe uma intersecção entre as “caixas delimitadoras” formadas pelas linhas do exemplo anterior, ele vai informar apenas se os objetos colidem ou não, não me preocupei com a localização relativa dos objetos (acima, abaixo, etc).

Ai vai o código do método “testar_colisao” que retorna true em caso de colisão e false em caso contrário.
protected bool testar_colisao(Texture2D box1, Vector2
posi1, Texture2D box2, Vector2 posi2)

{
//por padrão os objetos não
colidem
bool status = false;

//coloque as posições em nomes mais
fáceis :-)
float x1 = posi1.X;
float x2 = posi1.X + box1.Width;

float x3 = posi2.X;
float x4 = posi2.X + box2.Width;

float
y1 = posi1.Y;

float y2 = posi1.Y +
box1.Height;

float y3 = posi2.Y;

float y4 = posi2.Y + box2.Height;

//teste os
limites e veja se os objetos colidiram
if ((((x3 <= x1) && (x1 <= x4)) ((x3 <= x2) && (x2 <= x4))) && (((y3 <= y1) && (y1 <= y4)) ((y3 <= y2) && (y2 <= y4))))

{

//achei uma colisão!!

status = true;

}

//devolva o resultado

return status;

}

Agora que já sabemos como testar a colisão, vamos ver um exemplo de código que usa esse método para testar a colisão. O nosso exemplo vai usar todo o código dos tutoriais anteiores, só que no lugar de uma bola, teremos os dois retângulos que usei nos exemplos deste tutorial para explicar como funciona a colisão. Repare que para achar as coordenadas eu usei as propriedade Width (largura) e Heigth (altura) das texturas.

O software é simples, temos dois retângulos, um fixo e outro que vai se mover de acordo com os as setas do teclado, quando esses dois retângulos colidirem, um deles ficará transparente.

Inclui duas texturas, uma para cada retângulo:


//texturas
Texture2D retanguloazul;
Texture2D retanguloverde;
Depois incluímos os vetores de posição para cada um:

//vetor para posição
Vector2 posicao_verde = Vector2.Zero;
Vector2 posicao_azul; //vai ficar parado
Preciso de um lugar para guardar o resultado os testes de colisão:

//objeto para guardar o resultado dos testes de colisao
public bool resultado_colid;

Além disso, para deixar um dos retângulos transparentes, eu preciso definir uma cor e determinar o Alpha dela, usando um objeto do tipo “color” que recebe 4 argumentos, os índices R (red), G (green), B (blue) e o A(alpha) que determina justamente os valores de transparência.

Esses valores variam de 0 a 255 e a mistura deles gera as cores no padrão RGB. Quando você aplica o valor 0 ao A, indica transparência total e 255 indica sem transparência.
//cor transparente
//branco com transparencia
Color my_color = new Color(255, 255, 255, 150);

Para saber mais sobre isso, leia o artigo de André Furtado no SharpGames.

Agora, vamos ver como fica o código:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;


//executando o jogo
namespace meu_jogo
{
class novo_jogo
{
static public void Main() //método principal da aplicação
{
jogo teste = new jogo();
teste.Run();
}
}
}

//o
código da classe jogo, herdado da classe pai "Game"

class jogo : Game
{
//permite a configuração do ambiente
GraphicsDeviceManager
config_am;
ContentManager recursos;

//texturas
Texture2D
retanguloazul;
Texture2D retanguloverde;

//sprite
SpriteBatch
s_azul;
SpriteBatch s_verde;

//vetor para posição
Vector2
posicao_verde = Vector2.Zero;
Vector2 posicao_azul; //vai ficar parado

//objeto para guardar o resultado dos testes de colisao
public bool
resultado_colid;

//branco com transparencia
Color my_color = new
Color(255, 255, 255, 150);

//construtor da classe
public jogo()
{
config_am = new GraphicsDeviceManager(this);
recursos = new
ContentManager(Services);

}

//inicializa outros itens que não
requerem a inicalização do dispositivo gráfico
protected override void
Initialize()
{
posicao_azul.X = 200;
posicao_azul.Y = 200;

base.Initialize();
}

//método para carregar a textura.
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
retanguloazul =
recursos.Load(@"imagens\rect_azul");
retanguloverde =
recursos.Load(@"imagens\rect_verde");

s_azul = new
SpriteBatch(config_am.GraphicsDevice);
s_verde = new
SpriteBatch(config_am.GraphicsDevice);
//preciso inicializar as texturas
antes de usar a colisão
resultado_colid = testar_colisao(retanguloazul,
posicao_azul, retanguloverde, posicao_verde);
}
}

protected
override void Update(GameTime gameTime)
{
//um objeto para guardar o
status do teclado
KeyboardState teclado = Keyboard.GetState();
//veja se
algum objeto colidiu
resultado_colid = testar_colisao(retanguloazul,
posicao_azul, retanguloverde, posicao_verde);

//testando a qual tecla
foi pressionada e
//incrementando a coordenada correta
if
(teclado.IsKeyDown(Keys.Right))
{
posicao_verde.X++; //mover para a
direita
}
else if (teclado.IsKeyDown(Keys.Left))
{
posicao_verde.X--; //mover para a esquerda
}
else if
(teclado.IsKeyDown(Keys.Down))
{
posicao_verde.Y++; //mover para baixo
}
else if (teclado.IsKeyDown(Keys.Up))
{
posicao_verde.Y--;
//mover para cima
}
else if (teclado.IsKeyDown(Keys.Escape))
{
this.Exit(); //sair da aplicação
}


}

protected
override void Draw(GameTime gameTime)
{
config_am.GraphicsDevice.Clear(Color.Black);

//desenhe o retangulo
azul em uma posicão fixa e sempre com a mesma cor.
s_azul.Begin();
s_azul.Draw(retanguloazul, posicao_azul, Color.White);
s_azul.End();

if (resultado_colid) //se colidiu, desenhe com transparencia
{
s_azul.Begin();
s_azul.Draw(retanguloverde, posicao_verde, my_color);
s_azul.End();
}
else //em caso contrário desenhe normal
{
s_azul.Begin();
s_azul.Draw(retanguloverde, posicao_verde, Color.White);
s_azul.End();

}
}

//testa a colisao entre dois objetos
//recebe como argumentos as texturas e os vetores de posições de cada objeto
protected bool testar_colisao(Texture2D box1, Vector2 posi1, Texture2D box2,
Vector2 posi2)
{
//por padrão os objetos não colidem
bool status =
false;

//coloque as posições em nomes mais fáceis :-)
float x1 =
posi1.X;
float x2 = posi1.X + box1.Width;

float x3 = posi2.X;
float x4 = posi2.X + box2.Width;

float y1 = posi1.Y;
float y2 =
posi1.Y + box1.Height;

float y3 = posi2.Y;
float y4 = posi2.Y +
box2.Height;

//teste os limites e veja se os objetos colidiram
if
((((x3 <= x1) && (x1 <= x4)) ((x3 <= x2) && (x2 <= x4))) && (((y3 <= y1) && (y1 <= y4)) ((y3 <= y2) && (y2 <= y4)))) { //achei uma colisão!! status = true;

}
return status; //devolva o resultado
}
}



Agora vamos aos pontos fortes e fracos desse método de colisão, primeiro os pontos fortes:

- Fácil de implementar para objetos que estejam alinhados aos eixos X e Y.
- Tem bom desempenho para objetos alinhados com os eixos, se comparado a outros métodos.

Depois os pontos fracos:

- Não atende a maioria dos casos, visto que os objetos nem sempre são retangulares.
- Em objetos de formas não retangulares, provoca “falsas colisões” visto que as texturas nem sempre preenchem toda a área das “caixas delimitadoras”.

Um exemplo simples, onde as texturas provocariam “falsas” colisões, seria o mesmo código, usando “círculos no lugar de retângulos”.

Agora vou dar umas dicas de utilização desse método nos seus jogos:
- Se for usar esse método, desenhe os objetos de forma que eles ocupem o máximo da área da imagem, isso vai reduzir os espaços em branco na imagem e por tabela reduz os erros nas colisões.

- Divida as imagens em imagens menores e anime via software, na hora de testar a colisão, você testa as partes individualmente, isso melhora a aproximação e diminui muito o erro nas colisões por “bounding box”. Por exemplo, se você tem um personagem do tipo medieval, um guerreiro com uma espada e quer testar a colisão do sprite de ataque do guerreiro com um monstro, você tem duas opções, veja o exemplo abaixo.
A primeira é desenhar o boneco parado e em um outro sprite, desenhar o ataque.
Se você fizer isso, vai testar toda a área do sprite de ataque e isso inclui as áreas abaixo e acima da espada que não devem provocar colisão. Isso geraria falsos ataques e seu boneco seria “invencível” XD

Outra idéia, mais realista, é criar três sprites, um parado, outro de ataque e em separado, criar o sprite da espada que será desenhado na posição do braço do seu guerreiro. Quando for necessário testar a colisão, você testa apenas o sprite da espada e como a espada é pequena, a aproximação da colisão é boa.


Quando você for desenhar os objetos, mande desenhar a espada no local do braço e depois teste a colisão apenas na espada, repare que o sprite da espada sozinho ocupa quase toda a área da imagem (pontilhado vermelho), melhorando bastante a aproximação da colisão.

E por final, vale a pena ressaltar que esse é apenas um método de colisão e que geralmente não é o método mais usado. Para o caso do nosso "guerreiro" você poderia usar um outro método de "colisões por pixel", isso evitaria a necessidade de criação do sprite da espada.
Ufa!! já sabemos como criar a estrutura básica de um jogo me XNA, sabemos desenhar na tela, mover, usar transparência nos nossos sprites e por fim aprendemos um método de colisão. Com o que mostrei nas três partes desse tutorial, você já pode criar diversos joguinhos, espero que a leitura tenha valido a pena, por hoje é só e até o próximo tutorial ;-)

Labels: , , , ,

Monday, October 22, 2007

Tutorial de XNA – Parte II

Tutorial de XNA – Parte II
Por José Ferreira da Silva Junior

Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de orientação a objetos e que tenha lido a primeira parte do tutorial.

Dando seqüência ao meu pequeno tutorial, vamos revisar os conceitos estudados no primeiro texto para em seguida aprender algo novo. Na “aula” anterior, descobrimos o XNA, vimos o que é, qual a sua idéia e como é a estrutura básica de um jogo. Baseado nisso, criamos a nossa primeira janela do jogo e desenhamos uma imagem na tela.

Agora, vamos ver como movimentar a imagem, testando o recebimento de teclas e encerrando o jogo caso o ESC seja pressionado.

Revisando a estrutura básica do jogo:
- Primeiro carregamos os elementos necessários ao jogo (método ContentLoadGraphics)
- Atualizamos os dados dos objetos do jogo (método Update)
- Desenhamos as imagens na tela (método Draw)

Mas, de nada adianta desenhar a imagem no mesmo lugar, ou seja, ficar sem movimentação.

A nossa nova lógica vai ser bem simples, na rotina de atualização (Update) nós verificamos o status do teclado (teclas pressionadas) e tomamos alguma atitude de acordo com o nosso novo status.

Para guardar o status do teclado, nós vamos usar a classe KeyboardState, ela vai nos informar as teclas que foram pressionadas pelo usuário. Para obter as teclas usaremos o método GetState da classe Keyboard, para tanto, usamos a seguinte sintaxe:

KeyboardState teclado = Keyboard.GetState();

Aqui nós guardamos o status do teclado, falta apenas testar se alguma das teclas pressionadas corresponde a um de nossos comandos, para tanto, usaremos o método “IsKeyDown” da classe KeyboardState.

A enumeração “Keys” nos fornece a listagem das teclas para que você possa testar o status de cada uma delas (caso seja necessário). Nesse caso testaremos a tecla “Right” (seta para a direita).

teclado.IsKeyDown(Keys.Right)

O resultado desse comando é do tipo “booleano” (true ou false) e informa se a tecla foi pressionada ou não. Para utilizar essas classes, precisamos incluir o namespace Microsoft.Xna.Framework.Input.

Com esses recursos já podemos movimentar o sprite na tela, testando qual foi a tecla pressionada e incrementado as posições X e Y do vetor “Posicao”. Repare na rotina de desenho do nosso sprite:

sprite.Draw(textura, posicao, Color.White);

“Posicao” é um objeto do tipo vector2 e possui dois atributos “x” e “y” do tipo float, que serão usados para representar as posições do nosso sprite na tela, o que faremos aqui é, a cada pressionamento das teclas direcionais, modificaremos os valores dessas coordenadas.

Devemos lembrar que as coordenadas da tela iniciam com a posição “zero” (ponto (0,0)) no canto superior esquerdo da tela, ao contrário do que estamos acostumados quando estudamos o plano cartesiano, então nossa tela tem as coordenadas assim:


Então, se incrementamos os valores de Y, na verdade, estamos fazendo com o que o Sprite se movimente para baixo, os limites para desenhar o sprite, são as resoluções, ou seja, para os valores de X temos o tamanho horizontal da tela e para Y o tamanho vertical.

Então o que fazemos é, na rotina Update, testamos as teclas pressionadas e atualizamos os valores de X e Y do vetor posição, quando a rotina Draw for chamada, o sprite será desenhado no local correto, com as posições atualizadas.

Por exemplo, para testar se o direcional para cima foi pressionado e movimentar o sprite para cima nós faríamos:

if (teclado.IsKeyDown(Keys.Up))
{
posicao.Y--;
}

Podemos fazer o mesmo com as outras teclas que desejamos testar, assim, chegamos ao nosso novo código:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

//executando o jogo
namespace meu_jogo
{
class novo_jogo
{
static public void Main() //método principal da aplicação
{
jogo teste = new jogo();
teste.Run();
}

}
}

//o código da classe jogo, herdado da classe pai "Game"

class jogo : Game
{
//permite a configuração do ambiente
GraphicsDeviceManager config_am;
ContentManager recursos;

//texturas
Texture2D textura;
//sprite
SpriteBatch sprite;
//struct para posição
Vector2 posicao = Vector2.Zero;

//construtor da classe
public jogo()
{
config_am = new GraphicsDeviceManager(this);
recursos = new ContentManager(Services);

}

//método para carregar a textura.
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
textura = recursos.Load(@"imagens\bola");
sprite = new SpriteBatch(config_am.GraphicsDevice);

}
}

protected override void Update(GameTime gameTime)
{
//um objeto para guardar o status do teclado
KeyboardState teclado = Keyboard.GetState();


//testando a qual tecla foi pressionada e
//incrementando a coordenada correta
if (teclado.IsKeyDown(Keys.Right))
{
posicao.X++; //mover para a direita
}
else if (teclado.IsKeyDown(Keys.Left))
{
posicao.X--; //mover para a esquerda
}
else if (teclado.IsKeyDown(Keys.Down))
{
posicao.Y++; //mover para baixo
}
else if (teclado.IsKeyDown(Keys.Up))
{
posicao.Y--; //mover para cima
}
else if (teclado.IsKeyDown(Keys.Escape))
{
this.Exit(); //sair da aplicação
}


}

protected override void Draw(GameTime gameTime)
{
config_am.GraphicsDevice.Clear(Color.Black);

sprite.Begin();
sprite.Draw(textura, posicao, Color.White);
sprite.End();

}
}


Por hoje é só, aprendemos como mover o sprite na tela e como verificar o pressionamento de teclas, espero que este texto seja útil a vocês.

Até a próxima.

Labels: , ,

Wednesday, October 10, 2007

Tutorial de XNA - Parte I

Aproveitando a folga depois das provas e do SEC onde ministrei um Mini-Curso de C#, eu resolvi brincar um pouco com o XNA e aproveitei para escrever um pequeno texto, a cada parte do meus estudos eu vou publicar aqui um "mini artigo", espero que vocês gostem :-)

--------------------------------------

Tutorial de XNA – Parte I
Para o melhor entendimento deste material é necessário que o leitor tenha conhecimentos prévios de C# e de orientação a objetos.

Como bom “RPGista” que sou, sempre gostei de jogos e ainda tenho a vontade de criar os meus, para tanto já tinha até iniciado meus estudos em Allegro[1] , o que não foi assim tão amigável. Eu não tive tempo de concluir o meu primeiro experimento em allegro por causa da semana de provas na universidade, sem falar do retorno do grupo de estudos de .NET e do trabalho, resolvi então retomar o estudo dos jogos unindo o útil ao agradável, o grupo de .NET e o Framework para desenvolvimento de jogos da Microsoft, o XNA.

Comecei então a pesquisar a respeito, primeiro as definições formais, as novidades, porque usar e por fim, a parte mais prazerosa que é montar um jogo de exemplo. Então, sem mais delongas, vamos a introdução ao XNA, espero que este texto seja útil para outras pessoas.

O que é XNA?

Bom, como todo framework, o XNA é uma plataforma para desenvolvimento que visa facilitar em algum nível o desenvolvimento de aplicações, no caso os jogos. Por exemplo, ao desenvolver jogos, o programador precisa criar, dentre outras coisas, um loop principal, onde ficam as etapas básicas que todo jogo deve ter, como carregamento de recursos, atualização dos dados do “mundo” do jogo, desenho das cenas com as modificações e descarga de recursos, isso o XNA já traz para você. Sem falar que dependendo dos recursos usados o mesmo jogo produzido para PC pode rodar em outras plataformas (como o XBOX). Resumindo, como muitas iniciativas da Microsoft o XNA visa facilitar em muita coisa o desenvolvimento de jogos.

Mas, deixemos de “bla-bla-bla” e vamos à prática. Você pode encontrar maiores informações no endereço:
http://msdn2.microsoft.com/en-us/xna/default.aspx

O software necessário para reproduzir este tutorial:
- Visual C# Express
- Visual Game Studio 1.0

Você pode baixar ambos do site da Microsoft.

Começando o projeto.

Para iniciar o estudo, vamos criar um projeto do tipo “Windows Game”, quando aparecer o código do “template” nós vamos apagar e recomeçar o nosso código, assim vamos entender melhor o que cada parte do código faz.

Para tanto vá até “File -> New Project” e depois escolha “Windows Game”.

No “Solution Explorer” (onde aparecem os componentes do projeto) vão aparecer dois arquivos “Program.cs” e “Game1.cs”, apague os dois, eles contem código pronto para uma estrutura básica do jogo, nós vamos criar o nosso código.

Agora adicione um arquivo em branco onde nós vamos começar a criar o nosso projeto, para tanto, clique com o botão direito dentro do “Solution Explorer” no nome do projeto e vá até a opção “Add” e depois “New Item” e selecione “Class”, dê um nome ao arquivo e pronto.

A estrutura do jogo.

Como eu já havia citado antes, a maioria dos jogos possui alguns elementos comuns em suas estruturas de controle, como um loop “geral”, um local para carregamento de recursos, um para atualização do “mundo”, um para descarga e outro para desenho. No XNA a classe “Game” fornece essa estrutura.

Vamos ao código:

Primeiro vamos adicionar os namespaces necessários para a estrutura do jogo, no caso para poder usar a classe Game do XNA.

using System;
using Microsoft.Xna.Framework;


Agora vamos herdar da classe Game uma nova classe que vai conter a estrutura do nosso jogo:

//o código da classe jogo, herdado da classe pai "Game"

class jogo : Game
{
//construtor da classe
public jogo()
{

}
}


Repare que eu apenas criei a classe, herdando os membros da classe Game e depois criei um construtor que ainda está vazio. Agora, o código da rotina principal da aplicação:

class novo_jogo
{
static public void Main() //método principal da aplicação
{
jogo teste = new jogo();
teste.Run();
}

}


Com esse código já será criada uma janela para a nossa aplicação, mas, nada foi feito ainda. Vale ressaltar que agora usaremos a estrutura herdada da classe game na nossa aplicação, ela nos fornece um loop principal para o jogo, cuida da inicialização dos dispositivos e provê alguns métodos que nós iremos sobrescrever para criar a lógica do nosso jogo.

Agora vamos aos métodos da classe game que serão utilizados na nossa estrutura:

Draw -> Chamado quando o jogo determina seu tempo específico de renderização. Iremos sobrescrever esse método para criar nossas rotinas de desenho das imagens.

Initialize -> Chamado depois que “Game” e GraphicsDevice são criados, mas, antes de LoadGraphicsContent.

LoadGraphicsContent -> Chamado quando os recursos gráficos precisam ser carregados, iremos sobrescrever esse método para carregar nossas imagens.

UnloadGraphicsContent -> Chamado quando os recursos gráficos precisam ser descarregados.

Update -> Chamado quando a lógica do jogo precisa ser atualizada, aqui criaremos as rotinas que manterão nosso “mundo” atualizado.

Eu fiz uma tradução rasteira da documentação do XNA no Msdn, portanto perdoem os erros de tradução. Logicamente, fiz uma “reescrita” no texto explicativo para adapta-lo as necessidades deste documento.

Configurando o ambiente.

Agora precisamos configurar o nosso jogo de acordo com o ambiente onde ele está rodando, coisas como resolução, cores, etc. Para fazer isso, vamos modificar o código da nossa classe jogo, deixando-o da seguinte forma:

class jogo : Game
{
//permite a configuração do ambiente
GraphicsDeviceManager config_am;

//construtor da classe
public jogo()
{
GraphicsDeviceManager config_am = new GraphicsDeviceManager(this);
}
}


Repare que depois disso até a cor de fundo da nossa aplicação já recebe um valor “default”. :-D

Agora adicione os seguintes namespaces:

using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;


E finalmente podemos cuidar do código para carregar e exibir a imagem.

Primeiro, vamos adicionar um ContentManager que é usado para gerenciar nossos recursos (os arquivos de texturas, por exemplo) e depois vamos instancia-lo no nosso construtor.

class jogo : Game
{
//permite a configuração do ambiente
GraphicsDeviceManager config_am;
//gerenciador dos recursos
ContentManager recursos;

//construtor da classe
public jogo()
{
GraphicsDeviceManager config_am = new GraphicsDeviceManager(this);
recursos = new ContentManager(Services);
}
}


Agora, antes de carregar propriamente os recursos, vamos adicionar a imagem da textura ao nosso projeto, para tanto, criamos uma pasta no Solution Explorer com o nome “imagens” e depois adicionamos o arquivo contendo a textura aqui chamado de “bola.jpg”

Agora clique com o botão direito na imagem e depois em “Properties” e procure pela propriedade “Asset Name”, ela será usada para referenciar a imagem dentro do projeto.

No caso desse documento, o Asset Name usado é “bola”.

A Classe “Game” do XNA chama automaticamente o método “LoadGraphicsContent” para carregar os recursos, como estamos herdando dessa classe, vamos aproveitar esse método para carregar as nossas texturas, para fazer isso, vamos sobrescreve-lo.

//método para carregar a textura.
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
textura = recursos.Load(@"imagens\bola");

}
}


Nele testamos se os recursos já foram carregados através do parâmetro “LoadAllContent” e depois carregamos o que for necessário. Repare que usamos o objeto “recursos” que é o nosso gerenciador de recursos e que foi instanciado no construtor da classe, nele chamamos o método Load que informando o tipo de objeto que vamos carregar (no caso uma textura 2D) e por final passamos uma string com o asset name da imagem, como criamos uma pasta para a imagem, devemos preceder o nome com a pasta, o arroba é usado para passar a string do “jeito que está” desprezando os caracteres especiais.

Pronto, agora passaremos a imagem para um sprite e depois mostraremos ela na tela.

A Classe Game do XNA também possui um método DRAW que é chamado automaticamente e que serve para incluir o código onde os objetos são desenhados, iremos sobrescrever esse método na nossa classe, para desenhar a nossa imagem. Mas, antes vamos adicionar um objeto “SpriteBatch” a nossa classe que vai servir para desenhar de fato a imagem e depois vamos instancia-lo, também vamos adicionar um objeto vector2 que chamaremos de “posicao” e que será a posição do desenho na tela. O código da nossa classe ficará assim:


//o código da classe jogo, herdado da classe pai "Game"

class jogo : Game
{
//permite a configuração do ambiente
GraphicsDeviceManager config_am;
ContentManager recursos;

//texturas
Texture2D textura;

//sprite
SpriteBatch sprite;

//struct para posição, inicializado no zero (x = 0 e y = 0)
Vector2 posicao = Vector2.Zero;

//construtor da classe
public jogo()
{
config_am = new GraphicsDeviceManager(this);
recursos = new ContentManager(Services);

}

//método para carregar a textura.
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
textura = recursos.Load(@"imagens\bola");
sprite = new SpriteBatch(config_am.GraphicsDevice);

}
}

//método para desenhar na tela
protected override void Draw(GameTime gameTime)
{
sprite.Begin();
sprite.Draw(textura, posicao, Color.White);
sprite.End();

}
}


Repare nas linhas em que instanciamos o objeto “sprite” e depois no método Draw onde desenhamos efetivamente o sprite na tela.

Pronto, por agora já é o bastante, na próxima etapa do tutorial eu mostrarei como movimentar os objetos.


[1] Biblioteca gráfica multiplataforma que pode ser usada para desenvolver jogos em C ou C++.

Labels: , , ,