Sie sind auf Seite 1von 91

Apostilas I, II e III

Autor: Lucas Mousinho da Fonseca (lucmousinho@gmail.com)


Tpicos da apostila I e II:
Introduo
O que o C#?
Orientao a Objetos
Mquina Virtual
Estrutura de um programa em C# com Hello World
Tipos por Valores
Tipos por Referncia
Tipos suportados em um namespace

XNA Game Programming


Arquitetura de um programa em XNA
Game Loop
Planejamento de um jogo

Funcionalidades bsicas do XNA


Criando um projeto XNA
Content Pipeline
Mtodos de inicializao e finalizao
GameTime
Sistema de coordenadas 2D de uma tela
Texturas e Sprites
Escrevendo textos na tela
Controlando um sprite
Debug do cdigo

Coliso entre objetos


Coliso entre sprites
Coliso Bouding Box
Coliso Bounding Sphere

Msicas de fundo e efeitos sonoros


Msicas de fundo e efeitos sonoros

Dicas para criao de um projeto


Dicas para criao de um projeto

Fluxo de telas
Criao e gerenciamento do fluxo de telas

Tcnicas de Scrolling
Tcnica de Side Scrolling
Tcnica de Parallax Scrolling

Coliso por Pixels


Coliso por Pixels

Animao 2D
Animao 2D

2
Construo de fases
Definio de tiles
Descrio do funcionamento da tcnica
Criao do projeto
Utilizao de arquivos .XML
Contruindo mapas com o XNA Tile Map Editor

Cmera 2D
Criao de uma Cmera 2D

Sistema de Save e Load


Salvar e carregar dados de um arquivo

Fsica
Movimento Retilneo Uniforme (MRU)
Lanamento vertical com movimento retilneo uniformemente variado (MRUV)
Lanamento oblquo (deslocamento parablico)

3
Nota:

Esta apostila foi elaborada e escrita por Lucas Mousinho da Fonseca


(lucmousinho@gmail.com), com exemplos criados pelo respectivo autor e (alguns) baseados
em tutoriais encontrados nos sites e livro abaixo:

Websites:

http://kleberandrade.wordpress.com/

http://devtuts.com.br/

Livro:

Beginning XNA 3.0 Game Programming: From Novice to Professional

4
Introduo

5
O que o C#?

C# (ou C Sharp) uma linguagem de Programao Orientada a Objetos (POO)


fortemente tipada, desenvolvida pela Microsoft como parte da plataforma .NET. A sua sintaxe
orientada a objetos foi baseada no C++, mas inclui muitas influncias de outras linguagens de
programao, como Object Pascal e Java. (Wikipedia.org)

Observao:

Antes de comear, certifique-se de ter instalado corretamente a verso mais recente


do Visual C# Express junto as suas dependncias (pode ser encontrado para download gratuito
em http://www.microsoft.com/express/Downloads/). Em nossas aulas utilizaremos sempre
esta IDE, sendo no momento a nica que permite total integrao com o framework XNA.

Orientao a Objetos:

Assim como outras linguagens (Java, C++, etc), o C# uma linguagem orientada a objetos e
oferece suporte aos seus trs recursos bsicos da O.O. :

Encapsulamento:

O ato de empacotar ao mesmo tempo dados e objetos denominado


encapsulamento. O objeto esconde seus dados de outros objetos e permite que os
dados sejam acessados por intermdio de seus prprios mtodos. Isso chamado de
ocultao de informaes.

O encapsulamento protege os dados do objeto do uso arbitrrio e no-


intencional;

O encapsulamento o resultado de ocultar do usurio os detalhes da


implementao de um objeto;

O encapsulamento importante porque separa a maneira como um objeto se


comporta da maneira como ele implementado;

Segue a definio de como implementar os conhecimentos ou aes de uma


classe sem informar como isto feito.

Herana:

um relacionamento que pode ser realizado entre classes, permitindo a


extensibilidade e reusabilidade de rotinas e dados j existentes (variveis e funes).
As classes podem ser divididas em dois tipos:

Subclasse: uma classe que um subtipo de uma ou mais classes. Como tal, ela
herda todas as caractersticas de suas superclasses. Em outras palavras, todas
as caractersticas de uma classe podero ser reutilizadas por suas subclasses.
Se a classe B herda de A, ento dizemos que B uma subclasse de A.

6
Superclasse: Uma classe que um supertipo de uma ou mais classes. Como tal,
ela uma classe a partir da qual todas as suas caractersticas so herdadas por
suas subclasses. Em outras palavras, todas as caractersticas de uma
superclasse so reusveis por aquelas classes que so seus subtipos. Se a
classe B herda de A, ento dizemos que A uma superclasse de B.

Polimorfismo:

Polimorfismo significa ter muitas formas, que remete a um nico nome


representando um cdigo diferente, selecionado por algum mecanismo automtico.
um dos conceitos mais complicados de se entender, e tambm um dos mais
importantes. Na orientao a objetos, isso significa que um mesmo tipo de objeto, sob
certas condies, pode realizar aes diferentes ao receber uma mesma mensagem.
Ou seja, apenas olhando o cdigo fonte no sabemos exatamente qual ser a ao
tomada pelo sistema, sendo que o prprio sistema quem decide qual mtodo ser
executado, dependendo do contexto durante a execuo do programa.

Vale ressaltar que em C# tudo parte de um tipo (por exemplo: class, interface e
structs), no existem variveis ou funes globais fora deste escopo (como em php), e toda e
qualquer instncia de um tipo um objeto, sendo estes derivados de uma classe base em
comum, neste caso a Object.

Mquina Virtual:

Assim como em Java, as aplicaes em C# rodam sobre uma mquina virtual, a qual
cuida da interao entre o seu sistema (alto-nvel) e o hardware (baixo-nvel), o que
proporciona diversos benefcios como o Garbage Collector (coletor de lixo de memria) e a sua
fcil portabilidade, o que, no entanto, gera uma perda no desempenho do seu sistema.

Exemplo do funcionamento de uma mquina virtual (interpretador) para C#

7
Estrutura de um programa em C# com Hello World:

Para compilar seu primeiro programa em C#, ser necessrio primeiramente criar um
novo projeto. Para isto, v em File > New Project e selecione o Template: Console Application
em Project Types: Visual C#, conforme mostrado na imagem abaixo:

Janela para escolha do tipo de projeto no Visual C# 2010

Feito isto, o Visual C# criar seu novo projeto junto a classe Program.cs. Caso a mesma
no abra automaticamente na sua janela, efetue 2 cliques sobre ela na rvore do canto
direito, e mos a obra!

O Visual Studio gera automaticamente o layout principal de cada classe, sendo este
caracterizado da seguinte forma:

Estrutura de uma aplicao simples para rodar em um terminal

8
Historicamente, ao se tratar do ensino de novas linguagens de programao, utiliza-se
de um exemplo com Hello World. Ento, para no fugir a tradio, basta inserir uma linha
entre as chaves da funo Main contendo o seguinte:

Console.WriteLine("Hello World!");

Esta a estrutura principal de uma classe em C#. Utiliza-se o using para usar as
funcionalidades de determinada classe ao seu programa (semelhante ao import em java), o
namespace utilizado para definir um escopo para as classes e variveis globais (geralmente
com o mesmo nome do projeto), sendo a classe nomeada aps o termo class. Uma
observao importante o fato de em C# ser possvel de se criar mais de uma classe em um
nico arquivo.

Assim como em outras linguagem de programao, possvel criar mtodos


construtores (public nomedaclasse(atributos)), e para se executar um programa, a classe
principal do mesmo dever possuir um mtodo de entrada chamado Main():

static void Main()


{
}

Neste caso, foi utilizado a palavra static na declarao da funo, o que faz com que
esta funo seja local, podendo ser assim utilizada apenas no arquivo em que for declarada.

Os comentrios funcionam da mesma forma que em C. Utilize // para comentar uma


nica linha, ou /* texto */ para comentar um trecho de seu cdigo.

Diferente de C, o C# possui os tipos string e bool (booleano) para variveis, alm


de uma infinidade de outros, como o enum, o qual funciona de forma semelhante a uma
Struct, sendo que esse enumera as variveis declaradas internamente, geralmente no tipo
int.

Abaixo pode-se conferir os tipos bsicos utilizados na linguagem:

Tipos por Valores:

bool byte char decimal double

enum float int long sbyte

short struct uint ulong ushort

Tipos por Referncia:

class delegate interface object string

9
Tipos suportados em um namespace:

class interface struct enum delegate

outro namespace

OBS: Para fazer a converso entre os tipos por valores, utiliza-se o objeto Convert. Este
objeto tem diversas funes para realizar as converses em questo. Abaixo voc confere
algumas:

ToString ToInt16 ToInt32 ToDouble ToByte

Exemplos de Orientao a Objetos:

Criao de Objetos: A criao e inicializao de um objeto de uma classe segue o


mesmo padro de java, ou seja: <Tipo> <nome> = new <Tipo>().

Herana: Para se realizar herana, faz-se da seguinte forma: public class <nome da
classe filha> : <nome da classe pai>.

Encapsulamento: Para a criao de uma varivel ou funo utilizando-se do conceito


de encapsulamento, basta acrescentar antes de sua declarao a palavra private,
criando, caso necessrio, mtodos (funes) pblicos para se manusear os dados por
meio de uma classe externa.

Polimorfismo: Para utilizar o polimorfismo, primeiro deve-se criar uma subclasse


(herana) de outra, sendo que est s pode utilizar mtodos que sobreponham
(override) a sua superclasse. Ao declarar um objeto do tipo superclasse, voc pode
inicializ-lo como o tipo subclasse (<Tipo superclasse> <nome> = new <Tipo
subclasse>()).

Estas informaes so suficientes para dar-se incio ao estudo do framework XNA. Para
mais informaes a respeito da linguagem C#, visite o site oficial da Microsoft:
http://msdn.microsoft.com/en-us/library/aa287558.aspx .

10
XNA Game Programming

11
O que XNA?

XNA um framework criado pela Microsoft para facilitar e popularizar o


desenvolvimento de jogos digitais, permitindo a sua criao atravs da linguagem C#, e dispe-
se de ricas APIs de desenvolvimento para os seguintes ambientes: PCs com Windows, console
Xbox 360 e SmartPhones com Zune ou Windows Phone 7.

O XNA possui facilidades e se adequa a desenvolvedores iniciantes e experientes que


desejam implementar projetos ou testar solues por meio de um jogo. Como qualquer
sistema, possui suas vantagens e desvantagens, as quais so citadas abaixo:

Vantagens:

Desenvolvimento de jogos multiplataforma;

Simplifica o processo de desenvolvimento;

Diversas APIs feitas pela comunidade;

Debug em tempo real;

Muitos Starter Kits e tutoriais.

Desvantagens:

Gerenciado por um framework, perde um pouco no desempenho;

Necessita de uma placa de vdeo com suporte a no mnimo Shaders


2.0;

Somente roda em plataformas da Microsoft (Oficialmente).

O framework XNA teve como base o DirectX, tambm desenvolvido pela Microsoft, e
sofreu diversas atualizaes desde o seu surgimento em 2006, sendo tambm integrado a
diversas faculdades de desenvolvimento de jogos e a competies internacionais e nacionais, e
ganhando um espao para jogos de desenvolvedores indies na Xbox Live (rede do console
Xbox 360).

Evoluo das plataformas para desenvolvimento de jogos da Microsoft

12
Ambiente de desenvolvimento:

Como ja mostrado anteriormente, a plataforma utilizada para desenvolvimento de


jogos em XNA com C# o Visual Studio C#, em nosso caso, a verso 2010.

Janela para explorao de um projeto aberto no Visual Studio C# 2010

Esta IDE disponibilizada gratuitamente, em sua verso express, no site da Microsoft.

Observao:

Certifique-se de instalar o XNA framework (aqui utilizamos a verso 4) antes de


prosseguir com o aprendizado. O mesmo pode ser baixado gratuitamente no seguinte site:
http://create.msdn.com/

13
Arquitetura de um programa em XNA

Ao se criar um projeto em XNA, so geradas automaticamente duas classes, descritas a


seguir:

Program.cs:

Responsvel por iniciar e manter a execuo do jogo em si, atravs do seguinte


mtodo:

static void Main(string[] args)


{
using (Game1 game = new Game1())
{
game.Run();
}
}

Note que na criao do objeto game foi utilizado o using, pois ao trmino da
execuo, o mesmo libera automaticamente qualquer informao utilizada
que esteja alocada na memria do computador.

Game1.cs:

A classe Game (que o Game1 herda) a central da arquitetura XNA, onde


ocorrem as principais operaes do jogo. Possui um mtodo construtor, e os
cinco principais mtodos pertencentes ao Game Loop de um jogo feito com o
framework.

Game Loop:

O Game Loop um lao onde ocorrem todas as operaes lgicas e grficas, tendo
anteriormente a inicializao de todos os recursos necessrios para a execuo de um jogo. No
XNA, ele representado basicamente pelos seguintes mtodos:

Initialize() - inicializa recursos lgicos no grficos

LoadContent() - utilizada para carregar recursos

UnloadContent() - chamada para liberar recursos

Game Loop:

Update(GameTime gameTime) - onde se constri a lgica do jogo


(clculos)

Draw(GameTime gameTime) - utilizado para carregar as rotinas de


desenho em uma tela

Note que o Game Loop em si representado apenas pelos mtodos Update e Draw, os
quais so chamados, respectivamente na ordem citada, a cada lao de execuo.

14
Imagem representando a ordem de execuo de um jogo feito em XNA

Vale ressaltar que a quantidade de laos a serem executados por segundo est
diretamente ligado quantidade de frames por segundo (FPS) do jogo. Se um jogo roda a 60
fps, os mtodos Update e Draw sero executados sessenta vezes a cada um segundo.

OBS: Alm dos mtodos citados, o mtodo construtor de uma classe do tipo Game1 utilizado
para carregar informaes e inicializar dispositivos relacionados execuo do jogo (Game
Loop), como resoluo da tela, inicializao da placa de vdeo, definio do diretrio para os
arquivos externos utilizados no jogo, etc.

Planejamento de um jogo:

O planejamento de um jogo se divide basicamente em 3 etapas, sendo estas divididas e


definidas da seguinte forma:

Mercado alvo: Nesta etapa escolhida a plataforma, categoria e gnero de seu jogo;
se ele ser feito para algum console ou pc, qual ser o seu pblico alvo (casuais,
hardcores) e o estilo de jogabilidade (plataforma, ao, point-&-click, esportes, etc).

Equipe de desenvolvimento: importante possuir uma boa equipe para desenvolver o


seu projeto, a quantidade pode variar de acordo com as suas requisies e
disponibilidades, podendo a mesma ter de duas ou trs at centenas de pessoas. Os
cargos se dividem entre os mais abrangentes ramos, como: Game Designer, Artistas,
Modeladores, Animadores, Msicos, Programadores, Testers, Etc.

Documentao e Metodologia: Um jogo um software assim como qualquer outra


aplicao para um sistema operacional, e como tal, requer a criao de um documento
(documento de game design) para descrio de todas as informaes necessrias para
sua elaborao. Porm, este documento ainda maior, j que envolve informaes
alm das que so requisitadas pelos processos de engenharia de software, como guias
de identidade visual, informaes de jogabilidade, storyline, ecossistema do jogo, etc.

15
Funcionalidades bsicas do XNA

16
Criando um projeto XNA:

Para comear, dever ser criado um novo projeto que utilize o framework XNA no
Visual Studio C# 2010. Para isto, v em File > New Project e selecione o Template: Windows
Game em Project Types: Visual C#, conforme mostrado na imagem abaixo:

Janela para escolha do tipo de projeto no Visual C# 2010

Content Pipeline:

O Content Pipeline um dos recursos mais interessantes do XNA, pois reune e trata
em um nico espao todos os arquivos externos utilizados em um projeto, ou seja, no precisa
se preocupar em adaptar arquivos de udio, vdeos, imagens e etc para os tornar compatveis
com o seu jogo.

So suportados no XNA, por meio do Content Pipeline, os seguintes formatos de


arquivos:

Formatos 2D: DDS, BMP, JPG, PNG e TGA

Formatos de udio: XAP (projeto de audio gerado pelo XACT Tool), WAV, MP3 e WMA

Formatos 3D: X (DirectX) e FBX (Formato do Autodesk, porm j suportado por


alguns softwares free e pela maioria dos softwares de modelagem comercial)

Fontes: SpriteFont (Arquivo XML usado pelo XNA onde voc pode descrever as
caracteristicas da fonte usada, como: tamanho, tipo, etc)

XML: Suporta arquivos .XML que podem ser usados para armazernar dados do jogo

Shaders: Suporta arquivos .FX que so usados para descrever efeitos na renderizao

17
de modelos 3D ou 2D.

Vdeo (a partir do XNA 3.1): .WMV devidamente codificado no Main Profile Series 9.

A forma de utilizao do content pipeline em XNA se d de maneira bem simples. No


comeo do cdigo, no mtodo construtor, deve-se definir o diretrio em que se localiza os
arquivos do content, no qual por padro criado em um projeto do Visual C# como Content
e inicializado da seguinte forma:

Content.RootDirectory = "Content";

A varivel (objeto de uma classe) Content uma das variveis globais nativas do XNA,
sendo ela a responsvel pelo tratamento dos arquivos utilizados.

Para carregar um certo dado presente no seu Content, utiliza-se o mtodo Load da
seguinte maneira:

variavel = Content.Load<Tipo>(@"Caminho");

O Tipo representa o tipo (o mesmo da varivel) para o qual o objeto Contet dever
carregar o arquivo, podendo esse ser uma Texture2D, Song, SoundEffect, dentre outros os
quais sero citados mais abaixo. O Caminho nada mais do que o caminho at o arquivo que
deseja carregar, sendo a extenso do arquivo (.jpg, .png, .mp3, etc) no descrita, pois ao
compilar o jogo, o compilador converte todos os arquivos do content para o formato .xnb.

Um exemplo funcional do mtodo Load seria como o abaixo:

textura = Content.Load<Texture2D>(@"Sprites\ball");

Janela do Visual C# para exibio das classes e arquivos (content) de um projeto

18
Mtodos de inicializao e finalizao:

Como j citado anteriormente, no XNA utiliza-se basicamente dois mtodos para


inicializao dos recursos de um jogo, o Initialize e o LoadContent. No Initialize se inicia os
recursos no grficos, como objetos, variveis, etc; No LoadContent se inicializam todos os
recursos externos que o seu jogo ir utilizar, como imagens, sons, fontes e etc.

Alm dos dois mtodos citados, as classes que herdam a Game do XNA possuem um
mtodo construtor, no qual geralmente se define a resoluo e ttulo da janela do jogo, se
inicializa a interao com a placa de vdeo e define o diretrio raiz para os arquivos externos
de um projeto, dentre outras opes.

Para a liberao na memria dos recursos carregados, utiliza-se o mtodo


UnloadContent. Caso o projeto seja voltado plataforma Windows, o seu uso torna-se
opcional, pois a mquina virtual do C# oferece o garbage collector para liberao de recursos
no utilizados na memria. Porm, caso o projeto seja para outras plataformas, este mtodo
torna-se essencial e de extrema importncia, podendo o seu mal uso gerar diversos problemas
inesperados.

GameTime:

O parmetro GameTime, utilizado pelos mtodos Update(GameTime gameTime) e


Draw(GameTime gameTime), crucial para a execuo de cada lao de um jogo, pois ele,
dentre outras coisas, exibe o tempo passado desde o ltimo lao executado para fazer as
operaes lgicas e desenhos de forma correta, como por exemplo, seguindo a taxa de frames
por segundo definida.

Abaixo esto listados os mtodos presentes no objeto gameTime:

ElapsedGameTime: Representa o montante de tempo desde a execuo do lao


anterior de um game loop.

TotalGameTime: Representa o montante de tempo desde a execuo do primeiro lao


de um game loop.

IsRunningSlowly: Caso a chamada ao mtodo Update esteja demorando mais do que o


tempo definido pelo sistema, este mtodo retorna um valor afirmativo (True).

19
Sistema de coordenadas 2D de uma tela:

Diferente do senso comum, em um jogo as coordenadas nos eixos X e Y so crescentes


para a direita e para baixo, conforme a imagem abaixo:

Sistema de coordenadas nos eixos X e Y de um jogo

O ponto (0,0) dos eixos representa o canto esquerdo superior da janela e os seus
limites so representados pela resoluo, em pixels, do jogo. Ou seja, caso o seu jogo seja
executado em uma resoluo de 800x600, voc estar em uma tela com o limite em X de 800 e
em Y de 600.

Texturas e Sprites:

Antes de tratarmos deste assunto, voc pode conferir abaixo o significado de alguns
jarges que utilizamos com frequncia para grficos em 2D:

Sprite: O Sprite uma imagem objeto em 2D que pode ser manipulada


independentemente do restante da cena do jogo. O termo utilizado para descrever
tanto somente a imagem mostrada como uma classe usada pelo jogo para mostrar a
imagem (a qual contm propriedades como velocidade, posio, largura, altura e etc).

Texturas: A textura o nome dado imagem 2D carregada e utilizada tanto em um


modelo 3D como em um Sprite.

Background: O Background a cena de fundo de um jogo, que pode ser composta por
uma ou diversas imagens sobrepostas de forma esttica ou em movimento, gerando
um efeito de scrolling (rolamento).

Tiles: Os Tiles so pequenas imagens (partes de um cenrio maior), que so dispostas


de forma a gerarem um estgio ou mapa de uma fase do jogo.

20
Agora ser mostrado um simples exemplo de como criar e exibir na tela um sprite com sua
respectiva textura. Com o seu projeto criado, crie uma nova classe (clicando com o boto
direito sobre o nome do projeto e seguindo por Add / New Item...). Aqui utilizaremos o
nome clsSprite.

Sero utilizados trs tipos bsicos do XNA para carregar e desenhar a imagem na tela:

SpriteBatch: Utilizado para desenhar sprites e textos.

Texture2D: Armazena uma imagem de textura 2D.

Vector2: Utilizado para armazenar informaes dos eixos da tela, um ponto em X e Y.

Antes de prosseguir, certifique-se de inserir as seguintes linhas com as bibliotecas do XNA


Framework:

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

Criaremos uma classe para armazenar a textura, posio, velocidade e direo de um


determinado sprite. A mesma dever possuir um mtodo construtor para inicializar suas
variveis globais, como abaixo:

class clsSprite
{
//Textura do Sprite
public Texture2D textura;
//Varivel para armazenar a posio do sprite na janela
public Vector2 posicao;
//Varivel para armazenar a velocidade de locomoo do sprite
public Vector2 velocidade;

public clsSprite (Texture2D textura, Vector2 posicao, Vector2 velocidade)


{
this.textura = textura;
this.posicao = posicao;
this.velocidade = velocidade;
}
}

O prximo passo ser adicionar uma imagem ao projeto. Para isso, clique com o boto
direito do mouse sobre o Content de seu projeto e v por Add / Existing Item.... Em
seguida, selecione a imagem desejada e clique em ok.

Feito isto, vamos criar na classe clsSprite um mtodo Draw para desenhar o sprite na
tela.

public void Draw(SpriteBatch renderizador)


{
renderizador.Draw(textura, posicao, Color.White);
}

Este mtodo recebe como paramtro uma vriavel do tipo SpriteBatch, e utiliza a sua
funo Draw para desenhar o sprite.

21
OBS: A funo Draw() do SpriteBatch, alm do bsico (textura, posio e cor base) suporta
alguns parmetro em sua declarao, como escala, rotao, origem, orientao, etc...

Com a classe para tratamento do sprite pronta e com a imagem importada para o
Content do projeto, vamos a classe Game1.cs para renderizar a imagem na tela.
Primeiramente, declaramos as variveis globais do tipo clsSprite e SpritBatch:

//Renderizador de texturas 2D (desenhas as imagens na tela)


SpriteBatch renderizador;
//Objeto da classe clsSprite
clsSprite jogador;

Depois deve-se inicializar as variveis no mtodo LoadContent(), passando ao objeto


do tipo clsSprite a imagem, a posio e a velocidade de locomoo, conforme abaixo
(lembrando que no deve-se utilizar a extenso da imagem ao adicion-la pelo Content):

renderizador = new SpriteBatch(GraphicsDevice);


jogador = new clsSprite(Content.Load<Texture2D>("Imagem"), Vector2.Zero, new
Vector2(1, 1));

A funo Zero do Vector2 retorna o ponto inicial de uma janela (0,0) e os parmetros
(1,1) so usados para que a velocidade de locomoo da imagem seja de uma posio no eixo
X e Y a cada execuo do Game Loop (a ser explicado melhor mais a frente). Por fim, se
adiciona ao mtodo Draw as linhas responsveis por renderizar a imagem na tela:

renderizador.Begin();
jogador.Draw(renderizador);
renderizador.End();

Como mostrado acima, para renderizar-se algo na tela, este trecho do cdigo dever
comear com Begin() e terminar com End(), e ser implementado na classe Game1.cs, a
qual far o processo de renderizao do jogo, neste exemplo.

Se tudo ocorreu bem, ao iniciar o Debugging do seu projeto, dever aparecer uma
janela e uma imagem desenhada na posio inicial da tela.

Exemplo de uma janela durante a execuo do projeto

22
Escrevendo textos na tela:

O processo de escrita e renderizao de textos se d de maneira bem simples no XNA.


Para comear, voc dever criar em seu content um arquivo do tipo spritefont indo em Add
/ New Item... / Sprite Font. O Visual Studio criar um arquivo do tipo citado com cdigo em
XML.

Imagem do XML gerado automaticamente pela IDE

Com o arquivo aberto, basta configur-lo de acordo com seus critrios, editando os
campos que estiverem entre os <> </>, como nome da fonte, tamanho, negrito, etc.

Utilizaremos aqui para carregar o arquivo uma varivel do tipo SpriteFont (armazena
uma fonte de texto (xml criado no projeto) . Sendo assim, devemos declarar a varivel global
na classe Game1.cs:

//Fonte para o texto to jogo


SpriteFont fonte;

E em seguida carregar o XML na varivel no mtodo LoadContent:

fonte = Content.Load<SpriteFont>(@"Verdana");

Feito isto, basta ir ao mtodo Draw e adicionar uma linha para renderizar o seu
texto, conforme abaixo:

//Escreve uma string na tela


renderizador.DrawString(fonte, "Texto aqui", new Vector2(205, 10), Color.White);

O prprio renderizador j possui um mtodo para desenhar strings (DrawString), basta


passar os parmetros corretos (fonte, string, posio e cor) para ele renderizar na tela.

23
Controlando um sprite:

Para movimentar um sprite utilizando o teclado, iremos utilizar trs tipos bsicos do
XNA, sendo eles:

KeyboardState: Armazena o estado atual do teclado, com mtodos para


verificao de teclas pressionadas, e etc.

Keyboard: Utilizado para retornar status do teclado e fazer operaes lgicas.

Keys: Utilizado para retornar o valor de cada uma das teclas do teclado (para
verificaes).

Para trabalhar com estes mtodos, criaremos uma nova classe, chamada de
clsKeyboard, a qual precisa utilizar a seguinte biblioteca (alm da padro
Microsoft.Xna.Framework):

using Microsoft.Xna.Framework.Input;

A classe clsKeyboard armazenar o estado do teclado em um determinado momento e


possuir um mtodo Update para retornar um Vector2 com as novas posies do sprite.
Abaixo, segue o modelo da classe:

class clsKeyboard
{
KeyboardState k;

public clsKeyboard()
{
k = new KeyboardState();
}

public Vector2 Update(KeyboardState teclado, Vector2 posicao, Vector2


velocidade)
{
k = teclado;

if (k.IsKeyDown(Keys.Up))
{
posicao.Y -= velocidade.Y;
}
else if (k.IsKeyDown(Keys.Down))
{
posicao.Y += velocidade.Y;
}

return posicao;
}
}

Como visto acima, a classe possui uma varivel global do tipo KeyboardState, a qual
serve para armazenar o estado do teclado em um determinado momento, e um mtodo
construtor para inicializar a varivel.

O mtodo Update tem como parmetro de entrada trs variveis para realizar suas
operaes. O teclado contm uma captura do teclado naquele momento, a posio a

24
posio na qual o sprite se encontra na janela, e a velocidade a velocidade com que o
sprite se movimenta pela tela.

Alm do que j foi citado acima, o mtodo Update trabalha com a funo
IsKeyDown da varivel do tipo KeyboardState e com o objeto Keys.

O mtodo IsKeyDown verifica se naquele determinado momento uma determinada


tecla est pressionada. Caso a resposta seja positiva, o mtodo retorna true, caso contrrio
retorna false, ou seja, uma resposta do tipo bool. O objeto Keys contm variveis com os
valores de todas as teclas de um teclado. Sendo assim, para fazer uma verificao de uma tecla
pressionada, basta inserir o objeto Keys acompanhado da varivel que representa a tecla a
ser verificada dentro da funo IsKeyDown de uma varivel do tipo KeyboardState.

A movimentao do sprite d-se de acordo com a alterao da velocidade sobre a


posio em que ele est. Caso o sprite se movimente a uma velocidade de dois pixels a cada
game loop, basta subtrair ou somar essa velocidade da posio (no eixo X ou Y de uma varivel
Vector2) em que ele se encontra para obter sua nova posio.

Com a classe pronta, podemos voltar a classe Game1.cs e criar um objeto (global) do
tipo clsKeyboard para ento o utilizarmos.

//Objeto para tratamento do teclado


clsKeyboard k;

Feito isto, vamos ao mtodo Initialize e inicializamos a varivel como abaixo:

k = new clsKeyboard();

No mtodo Update e acrescentamos a seguinte linha:

jogador.posicao = k.Update(Keyboard.GetState(),jogador.posicao,
jogador.velocidade);

A linha acima modifica a posio do sprite de acordo com a posio (Vector2)


retornada pelo mtodo que criamos. O objeto Keyboard com a funo GetState() retorna
uma captura do teclado naquele instante, sendo esta do tipo KeyboardState.

Caso tudo tenha ocorrido bem, basta pressionar F5 para executar o seu projeto e
testar a movimentao do sprite (neste caso somente no eixo Y).

Debug do cdigo:

Para depurar o seu jogo, alm das prprias ferramentas fornecidas pelo Visual
Studio, pode-se utilizar o console do C# da seguinte forma:

Console.WriteLine("Texto aqui");

Pode-se utilizar tambm uma varivel do tipo string como parmetro para a funo.

25
Coliso entre objetos

26
Coliso entre sprites:

A coliso entre objetos (sprites) parte essencial de um jogo. Com ela, evitamos que
um objeto sobreponha o outro, fazendo assim com que eles colidam. Primeiramente
ensinaremos aqui duas tcnicas de coliso entre sprites, a Bounding Box e Bounding Sphere.

Coliso Bounding Box:

O Bouding Box um sistema de coliso bem simples, no qual ele analisa se o retngulo
(dimenses da imagem) est sendo sobreposto ou sobrepondo um outro retngulo de uma
mesma janela.

O sistema de coliso trabalha inclusive com elementos em 3D, utilizando variveis do


tipo Vector3 (que no estudaremos aqui, nos limitando a considerar o eixo Z como valor 0).
Este sistema pode no ser muito eficaz em alguns casos, pois se os pixels de uma determinada
imagem no ocuparem todo o espao de seu retngulo (dimenses), poder ocorrer uma
falsa impresso de coliso, onde uma imagem no chega a encostar na outra.

Imagem da coliso entre 2 retngulos (fonte: kleberandrade.wordpress.com)

A classe Bounding Box (a qual faz parte do XNA framework) contm diversas funes,
mas nos restringiremos a utilizar a Intersects, sendo esta responsvel por verificar se est
havendo coliso entre dois objetos e nos retornar uma varivel do tipo bool como resultado.

Primeiramente iremos criar um mtodo para verificar os limites do retngulo (Bouding


Box) de um sprite na classe clsSprite:

public BoundingBox getBoundingBox()


{
//Retorna os limites (BoudingBox) do sprite
return new BoundingBox(new Vector3(this.posicao.X, this.posicao.Y,
0), new Vector3(this.posicao.X + this.textura.Width, this.posicao.Y +
this.textura.Height, 0));
}

27
O mtodo acima retorna os limites do sprite com base na sua posio atual. O primeiro
parmetro passado ao mtodo construtor do BoundingBox trata-se de um Vector3 com a
posio atual do sprite, o segundo trata-se do tamanho nos trs eixos do retngulo que
representa a imagem a partir de sua posio, por isso soma-se a posio atual com as
dimenses da imagem, que podem ser obtidas por meio do mtodo Width e Height de um
Texture2D.

Para utilizar o cdigo, basta ir a classe Game1.cs, criar um novo sprite (ex: jogador2)
e repetir os passos que foram citados acima (no necessariamente precisa utilizar a
movimentao para este segundo, ele pode ficar fixo em um ponto qualquer da janela a sua
escolha). Com o segundo sprite criado, crie tambm um varivel global colisao do tipo bool
(a inicialize como false no mtodo Initialize) e no mtodo Update faa a verificao:

if (jogador.getBoundingBox().Intersects(jogador2.getBoundingBox()))
{
colisao = true;
}
else
{
colisao = false;
}

Estamos utilizando esse verificador para mudar a cor do fundo da janela ao ocorrer a
coliso. Para modificarmos a cor do fundo, vamos at o mtodo Draw da classe Game1.cs
e substitumos a linha que contm o GraphicsDevice.Clear pelas linhas abaixo:

//Caso acontecer a coliso, pinta a tela de vermelho, caso no, pinta de azul
if (colisao)
GraphicsDevice.Clear(Color.Red);
else
GraphicsDevice.Clear(Color.CornflowerBlue);

OBS: O GraphicsDevice.Clear(Color.XXX) limpa todos os objetos da janela a cada game loop,


por isso est sempre no comeo do mtodo, antes das operaes de renderizao. Isso evita
com que a cena anterior ao game loop atual continue visvel e seja sobreposta pela mais
recente.

Imagem do fundo da janela antes e depois da coliso (fonte: kleberandrade.wordpress.com)

28
Coliso Bounding Sphere:

O sistema de coliso Bounding Sphere simples e bem semelhante ao sistema de


coliso Bounding Box. Como seu nome sugere, ao invs de verificar a coliso por meio de um
retngulo, ele faz verificao de coliso entre esferas considerando o raio e o centro delas a
partir de um determinado ponto da janela.

Imagem da coliso entre 2 crculos (fonte: kleberandrade.wordpress.com)

Imagem da circunferncia a ser verificada (fonte: kleberandrade.wordpress.com)

Aqui novamente utilizaremos apenas a funo Intersect, mas o BoundingSphere


tambm possui diversas outras funes que podem ser utilizadas em outras ocasies (como
Radius para informar o raio e Center para informar o ponto central de um Bounding
Sphere).

Retornaremos classe clsSprite para criar um novo mtodo responsvel pela


verificao dos limites da circunferncia do sprite:

29
public BoundingSphere getBoundingSphere()
{
//Retorna os limites (BoudingSphere) do sprite
return new BoundingSphere(new Vector3(this.posicao.X +
this.textura.Width/2, this.posicao.Y + this.textura.Height/2, 0),
this.textura.Width/2);
}

Os parmetros passados ao mtodo construtor do BoundingSphere so o ponto


central da imagem (que pode ser obtido com o clculo acima) e o seu raio.

Para utilizar o cdigo do Bounding Sphere, basta repetir os mesmos procedimentos


utilizados no Bounding Box. A funo Intersect funciona da mesma forma para ambos, e
inclusive pode ser utilizada entre os dois tipos (verificar a coliso entre um crculo e um
retngulo).

OBS: As variveis do tipo BoundingSphere e BoundigBox tambm possuem uma funo que
pode ser til em diversas outras ocasies. O mtodo em questo o Contains, que retorna
uma enumerao denominada ContainmentType (Contains, Disjoint ou Intersect).

Imagem ilustrando as trs possveis ocorrncias entre dois sprites (fonte: kleberandrade.wordpress.com)

30
Msicas de fundo e efeitos sonoros

31
Msicas de fundo e efeitos sonoros:

Para reproduo de msicas de fundo e efeitos sonoros, utilizaremos os seguintes tipos e


objetos:

Songs: Utilizado para armazenar arquivos de udio.


SoundEffect: Utilizado para armazenar e reproduzir efeitos sonoros.
MediaPlayer: Utilizada para reproduo (play, pause, stop) e controle de Songs.
MediaState: Armazena o estado atual do MediaPlayer (parado, tocando, pausado...).

A reproduo de udio no XNA d-se de forma bem simples. Basta importar ao seu
Content um arquivo de udio (dos tipos suportados pelo XNA), os carregar nas variveis e
executar o som de acordo com o seu critrio.

Segue um exemplo simples de reproduo de uma msica de fundo em um jogo:


Inicialmente, preciso criar uma varivel global do tipo Songs na classe Game1.cs.

//Msica de fundo do jogo


Song musica;

E logo aps carregar a msica de seu Content e a reproduzir com o MediaPlayer no


mtodo LoadContent.

//Carrega a msica de fundo na varivel musica


musica = Content.Load<Song>(@"Sons\fundo");
//Inicia a reproduo da msica por meio do MediaPlayer
MediaPlayer.Play(musica);

Para parar a reproduo em um determinado momento, somente chamar a funo


Stop() do MediaPlayer, o qual tambm possui diversas outras funes.

Tabela de funes e variveis do MediaPlayer:

Mtodo / Variveis Paramtro Retorno Descrio


Mediaplayer.Play Song void Executa um som
Mediaplayer.Stop void void Para uma execuo
Mediaplayer.Pause void void Pausa uma execuo
Mediaplayer.Resume void void Reinicia a exeuo a
partir de um ponto
Mediaplayer.PlayPosition No funo TimeSpan Retorna o tempo de
execuo de um som
Mediaplayer.State No funo MediaState Retorna o estado do
MediaPlayer (pausado,
parado ou tocando)
Mediaplayer.IsMuted No funo / bool void Se definido como
true fica mudo, se
com false no fica
mudo
Mediaplayer.IsRepeating No funo / bool void Se definido como
true repete, se como
false no repete

32
Com os efeitos sonoros, a reproduo ocorre um pouco diferente, pois no precisa-se
do MediaPlayer. Primeiramente declaramos a varivel global:

//Efeitos sonoros
SoundEffect efeito;

E, aps inicializar a varivel no mtodo LoadContent()


(Content.Load<SoundEffect>(@"Sons\efeito")), executa-se o efeito em um determinado
local do cdigo (a seu critrio, geralmente no mtodo Update), fazendo com que ele seja
tocado somente uma vez e a partir daquele instante, chamando o mtodo Play() do
SoundEffect:

efeito.Play();

Alm da forma citada acima, existe uma sobrecarga da funo Play(), onde voc
pode ajustar o volume, velocidade e balano do efeito:

efeito.Play(volume, velocidade, balanco); //Todos os parmetros so em float

O campo volume varia entre 0f e 1f (aceita valores intermedirios em float, assim


como os outros);
O campo velocidade e o balano variam entre -1f (lento e com balano para esquerda)
at 1f (rpido e com balano para a direita).

importante citar que as variveis do tipo SoundEffect suportam apenas arquivos de


udio do tipo .wav.

OBS: Para saber mais detalhadamente a respeito das funes e variveis presentes em cada
objeto ou tipo, basta utilizar o prprio Visual Studio C#, o qual auto-completa mostrando todas
as opes disponveis para aquele objeto.

33
Dicas para criao de um projeto

34
Dicas para criao de um projeto:

Ao elaborar um projeto de um jogo, tente ser o mais organizado possvel. Alguns


detalhes podem fazer toda a diferena para a eficcia do seu desenvolvimento. Procure dividir
seus arquivos e classes por pastas e utilizar o mximo possvel do conceito de orientao a
objeto, de forma a segregar o cdigo e evitar sua aglomerao em poucas classes.

A utilizao de conceitos de Engenharia de Software tambm so essenciais para um


projeto ser bem sucedido, afinal um jogo no deixa de ser um programa. Alm da engenharia
de software, necessrio o estudo e elaborao de um documento de game design.

O bom senso deve ser bem utilizado. Casos como a construo de inteligncias
artificiais extremamente difceis, ou mesmo invencveis devem ser evitados, alm de definir
controles confortveis e compatveis com o estilo de jogo proposto.

Na construo de seu projeto essencial a definio de alguns detalhes bsicos (parte


do game design) como a resoluo em que o jogo ser executado, o ttulo de sua janela, se o
jogo suportar a opo de ser fullscreen, entre outros.

Para configurar as opes de vdeo, basta utilizar a varivel do tipo


GraphicsDeviceManager (em nosso caso a graphics, declarada por padro no XNA). No
mtodo construtor da classe Game1.cs inserimos as seguintes linhas aps a inicializao da
varavel graphics:

//Define a largura da janela. OBS: Valores aceitos com tipo int


graphics.PreferredBackBufferWidth = 800;
//Define a altura da janela
graphics.PreferredBackBufferHeight = 600;
//Define se o jogo sera em full screen
graphics.IsFullScreen = true;

As definies acima faro com que o jogo tenha uma resoluo de 800x600 e seja
executado com a tela em full screen.

Outra modificao que pode ser til a nomeao da janela do jogo em reproduo.
Para modificar o nome basta utilizar a linha abaixo:

Window.Title = "Seu Game";

O objeto Window faz parte da biblioteca padro do framework, ento ele no precisa
ser declarado ou inicializado por voc, basta modificar seus parmetros para obter o resultado
esperado.

Lembre-se tambm de separar em seu projeto as inicializaes do sistema ao mtodo


construtor, as inicializaes lgicas ao mtodo Initialize e o carregamento de recursos ao
mtodo LoadContent de sua classe principal.

35
Tcnicas de Scrolling

36
Tcnica de Side Scrolling:

O scrolling uma tcnica utilizada para gerar a sensao de se estar deslizando sobre o
contedo de uma imagem, ou seja, enquanto voc joga um game como Mario, o fundo se
move de acordo com a sua movimentao, o que passa a impresso de se estar caminhando
pelo cenrio.

Para gerarmos este efeito, vamos utilizar um dos mtodos de sobrecarga do mtodo
SpriteBatch.Draw, que nos possibilite a especificao de uma determinada rea da textura
que iremos renderizar. A sobrecarga em questo utiliza o seguinte cabealho:

public void Draw (


Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color
)

onde o paramtro sourceRectangle determina uma rea retangular da textura a ser


renderizada na tela. Se o paramtro passado for null, a textura ser renderizada por
completa.

Na varivel sourceRectangle, neste exemplo, os campos X e Y especificam o ponto


de origem da rea, enquanto os campos Width e Height especificam o tamanho da origem
at o ponto final da rea da textura a ser renderizada.

Vamos comear declarando as variveis para a textura e o eixo X (em nosso caso
faremos apenas o scrolling horizontal, para fazer o vertical precisaramos de uma varivel Y):

Texture2D fundo;
//Para determinar o Rectangle.X
int x = 0;

No mtodo LoadContent carregamos a textura para nossa varivel:

fundo = Content.Load<Texture2D>(@"fundo");

E no mtodo Update fazemos a incrementao/decrementao da varivel X para


gerar o scrolling:

KeyboardState k = Keyboard.GetState();
if (k.IsKeyDown(Keys.Left))
{
x -= 2; //Valor aleatrio. Jamais atribua valores aleatrios a um jogo final.
}
else if (k.IsKeyDown(Keys.Right))
{
x += 2;
}

Aqui devemos ter cautela, pois o valor da varivel X cresce muito rapidamente,
podendo assim ultrapassar o valor suportado por nmeros do tipo inteiro e gerar efeitos
indesejveis ao seu jogo.

37
Abaixo ser sugerido uma forma simples e eficiente para evitar que esse tipo de
problema ocorra:

//Verificao feita para recomear a varivel e evitar que extrapole o limite dos
inteiros
if (x > fundo.Width || x < -fundo.Width)
{
x = 0;
}

O cdigo acima faz a seguinte verificao: quando o valor de X se torna maior (positiva
e negativamente) que a largura da textura, ele volta a ser zero, evitando que o mesmo
extrapole o limite dos inteiros e gerando um resultado visual satisfatrio.

Para finalizar, vamos renderizar a textura na tela, mas para que o side scrolling de fato
ocorra, precisamos utilizar, alm da sobrecarga do mtodo SpriteBatch.Draw, uma das
sobrecargas do mtodo SpriteBatch.Begin no mtodo Draw de sua classe, conforme
abaixo:

renderizador.Begin(
SpriteSortMode.FrontToBack,
BlendState.Opaque,
SamplerState.LinearWrap,
DepthStencilState.Default,
RasterizerState.CullNone
);

Um dos parmetros importantes o SpriteSortMode.FrontToBack, o qual define a


ordem em que sero desenhados os sprites. No entanto, o que realmente nos importa neste
caso o parmetro SamplerState.LinearWarp, pois este gera a continuidade da textura, ou
seja, quando o scrolling atinge o fim da textura, ele desenha o seu comeo logo em seguida.

OBS: O parmetro BlendState.Opaque faz com que as imagens percam sua transparncia,
tornando-as opacas.

Por padro, o XNA mantm o processo de renderizao com o mtodo


SamplerState.LinearClamp. Caso voc utilize apenas a chamada Begin(), ao executar o
cdigo, quando passar dos limites definidos pelo rectangle, o renderizador comear a borrar a
tela com os ltimos pixels da textura.

Com o renderizador inicializado, utilizamos o Draw para desenhar o fundo e o


End() para finalizar o processo de renderizao:

renderizador.Draw(fundo,Vector2.Zero,new Rectangle(x,0,fundo.Width,
fundo.Height),Color.White);

renderizador.End();

A posio inicial da textura a ser desenhada varia de acordo com o X, e logo ao ser
finalizada, o parmetro SamplerState.LinearWarp faz com que o mtodo desenhe o seu
comeo e ligue ao seu final.

38
Imagem com a textura do fundo completa e fixa

Imagem com a textura do fundo deslocada pelo processo de scrolling

Imagem com a textura deslocada e o parametro SamplerState.LinearClamp ativo

39
Tcnica de Parallax Scrolling:

A tcnica de Parallax Scrolling no se difere muito do Scrolling comum. A diferena se


d na sensao de profundidade gerada, o que torna o seu jogo mais interessante e
visualmente bonito.

No Scrolling comum, ao movimentar-se, o fundo se move em uma nica velocidade. J


no Parallax Scrolling, o fundo composto por duas ou mais camadas, sendo que cada uma se
movimenta em uma velocidade diferente.

Imagem ilustrando as diversas camadas de um cenrio com Parallax Scrolling

Para gerarmos este efeito, vamos utilizar a mesma tcnica utilizada no Scrolling com
algumas pequenas alteraes.

Vamos comear declarando as variveis que sero utilizadas:

//Desta vez utilizaremos dois fundos


Texture2D fundo;
Texture2D fundo2;

//Precisaremos de duas variveis para controle da movimentao no eixo x


int x = 0;
int x2 = 0;

Logo aps, carregamos as texturas nas variveis no mtodo LoadContent e definimos


a movimentao no mtodo Update:

//Inserir no mtodo LoadContent


fundo = Content.Load<Texture2D>(@"fundo");
fundo2 = Content.Load<Texture2D>(@"fundo2");

40
//Inserir no mtodo Update
KeyboardState k = Keyboard.GetState();

if (k.IsKeyDown(Keys.Left))
{
x -= 2; //Valores aleatrios
x2 -= 1;
}
else if (k.IsKeyDown(Keys.Right))
{
x += 2;
x2 += 1;
}

//Verificao feita para recomear a varivel e evitar que extrapole o limite dos
inteiros
if (x > fundo.Width || x < -fundo.Width)
{
x = 0;
}

if (x2 > fundo2.Width || x2 < -fundo2.Width)


{
x2 = 0;
}

A diferena de velocidade de X e X2 gera o efeito de profundidade na execuo do jogo


( recomendado que as cenas mais a fundo tenham velocidade menor que as da frente para o
efeito ficar mais prximo do real).

Com o mtodo Update pronto, vamos ao processo de renderizao das cenas no


mtodo Draw:

renderizador.Begin(
SpriteSortMode.FrontToBack,
BlendState.AlphaBlend,
SamplerState.LinearWrap,
DepthStencilState.Default,
RasterizerState.CullNone
);

renderizador.Draw(fundo,Vector2.Zero,new Rectangle(x,0,fundo.Width,
fundo.Height),Color.White);

renderizador.Draw(fundo2, Vector2.Zero, new Rectangle(x2, 0, fundo2.Width,


fundo2.Height), Color.White);

renderizador.End();

A diferena com relao renderizao do Scrolling citada aqui surge na mudana do


BlendState.Opaque para BlendState.AlphaBlend. Este parmetro responsvel por
conservar a transparncia das imagens, de forma que elas no se sobreponham
completamente.

41
Imagem fixa das duas texturas sobrepostas

Imagem deslocada com a tcnica de Parallax Scrolling

Ao observar as imagens, possvel ver que ambas as texturas se movimentaram, mas


devido diferena de velocidade, e tomando como ponto de referncia o fim da nuvem
marcada nas imagens, percebemos que a imagem mais frente se movimentou mais que a de
trs. O efeito melhor visualizado durante a execuo do cdigo.

42
Coliso por Pixels

43
Coliso por Pixels:

A coliso por pixels a forma mais precisa de se verificar a coliso entre duas texturas
em um jogo, porm a mais complexa, pois a verificao feita a partir da transparncia das
imagens utilizadas, analisando pixel por pixel.

Para criarmos o processo de verificao, vamos utilizar um vetor do tipo Color (tipo
responsvel por armazenar a cor de um ou mais pixels) e o mtodo GetData do tipo
Texture2D, para repassar os pixels da textura para a varivel do tipo Color.

Comearemos por declarar as variveis que vamos utilizar em nosso jogo:

//Textura do sprite
Texture2D jogador;
Texture2D jogador2;

//Variveis para armazenar os pixels das texturas


//Matriz em forma de vetor
Color[] matrizTexturaJogador;
Color[] matrizTexturaJogador2;

//Posio dos nossos 2 personagens


Vector2 posicao;
Vector2 posicao2;

//Constante de velocidade para movimentao dos sprites


const int velocidade = 2;

//Varivel para verificar a coliso


bool colisao = false;

Os vetores do tipo Color declarados acima sero utilizados como matriz. No


utilizaremos matrizes reais, pois o mtodo GetData nos retorna um array com as
informaes.

OBS: Procure sempre criar variveis constantes para servirem de parmetro na verificao da
movimentao dos sprites, evitando assim inserir diretamente o valor nos algortimos para
movimentao das texturas.

A inicializao das posies fica a seu critrio, utilize os valores que achar melhor (no
mtodo Initialize, com new Vector(x,y)). No mtodo LoadContent, inicialize as texturas e
as demais variveis:

//Carrega a textura dos sprites


jogador = Content.Load<Texture2D>("jogador");
jogador2 = Content.Load<Texture2D>("jogador2");

//Cria um vetor/matriz com as dimenses da imagem


matrizTexturaJogador =
new Color[jogador.Width * jogador.Height];
matrizTexturaJogador2 =
new Color[jogador2.Width * jogador2.Height];

//Popula a matriz com os pixels das texturas


jogador.GetData(matrizTexturaJogador);
jogador2.GetData(matrizTexturaJogador2);

44
A matriz de pixels ser armazenada no vetor do tipo Color, de forma que a
quantidade de linhas da matriz ser definida pela quantidade de pixels verticais da textura, e a
quantidade de colunas ser a mesma que a de pixels horizontais.

Devemos criar tambm um mtodo para fazer a verificao se de fato os pixels esto
colidindo. O chamaremos de ColisaoPorPixel, conforme abaixo:

public bool ColisaoPorPixel(Rectangle retangulo1, Color[] dados1,


Rectangle retangulo2, Color[] dados2)
{
//Encontra os limites do retngulo de interseo

//Captura o ponto acima de quem estiver mais distante da origem


int cima = Math.Max(retangulo1.Top, retangulo2.Top);

//Captura o ponto abaixo de quem estiver mais prximo da origem


int baixo = Math.Min(retangulo1.Bottom, retangulo2.Bottom);

//Captura o ponto esquerdo de quem estiver mais distante da origem


int esquerda = Math.Max(retangulo1.Left, retangulo2.Left);

//Captura o ponto direito de quem estiver mais prximo da origem


int direita = Math.Min(retangulo1.Right, retangulo2.Right);

//Verifica todos os pontos dentro do limite da interseo


//Varre o retngulo de cima para baixo e da esquerda para direita
for (int y = cima; y < baixo; y++)
{
for (int x = esquerda; x < direita; x++)
{
//Verifica a cor dos pixels no mesmo ponto da interseo
Color color1 = dados1[(x - retangulo1.Left) +
(y - retangulo1.Top) * retangulo1.Width];

Color color2 = dados2[(x - retangulo2.Left) +


(y - retangulo2.Top) * retangulo2.Width];

//Se ambos os pixels no so transparentes


if (color1.A != 0 && color2.A != 0)
{
//Uma coliso foi encontrada
return true;
}
}
}
//No foi encontrada uma coliso
return false;
}
}

O mtodo faz a verificao se houve uma sobreposio de pixels no transparentes


entre as duas texturas no retngulo de interseo.

A posio a ser verificada encontrada pela seguinte expresso:

[(x - retangulo1.Left) + (y - retangulo1.Top) * retangulo1.Width]

Ou seja, pela soma da diferena entre o ponto horizontal esquerdo de cada sprite com
o ponto horizontal da posio de verificao em que est o retngulo da interseo (x -

45
retangulo1.Left), com a diferena do ponto vertical no topo do sprite com o ponto vertical
da posio de verificao do retngulo de interseo, vezes a largura do sprite ((y -
retangulo1.Top) * retangulo1.Width), para saber em que linha da matriz imaginria ele se
encontra.

Com as variveis inicializadas e o mtodo criado, vamos ao mtodo Update e


fazemos as rotinas para movimentao dos personagens e a chamada para a funo
ColisaoPorPixel:

// Detectando o estado do teclado


KeyboardState teclado = Keyboard.GetState();

//Jogador 1
if (teclado.IsKeyDown(Keys.D))
posicao.X += velocidade;
if (teclado.IsKeyDown(Keys.A))
posicao.X -= velocidade;
if (teclado.IsKeyDown(Keys.W))
posicao.Y -= velocidade;
if (teclado.IsKeyDown(Keys.S))
posicao.Y += velocidade;

//Jogador 2
if (teclado.IsKeyDown(Keys.Right))
posicao2.X += velocidade;
if (teclado.IsKeyDown(Keys.Left))
posicao2.X -= velocidade;
if (teclado.IsKeyDown(Keys.Up))
posicao2.Y -= velocidade;
if (teclado.IsKeyDown(Keys.Down))
posicao2.Y += velocidade;

//Define o retngulo delimitador do jogador 1


Rectangle RetanguloPersonagem1 =
new Rectangle((int)posicao.X, (int)posicao.Y,
jogador.Width, jogador.Height);

//Define o retngulo delimitador do jogador 2


Rectangle RetanguloPersonagem2 =
new Rectangle((int)posicao2.X, (int)posicao2.Y,
jogador2.Width, jogador2.Height);

colisao = false;

if (ColisaoPorPixel(RetanguloPersonagem1, matrizTexturaJogador,
RetanguloPersonagem2, matrizTexturaJogador2))
{
colisao = true;
}

Passamos como parmetro retngulos e matrizes dos jogadores para o mtodo realizar
a verificao e retornar um booleano como resposta.

Para finalizar, inserimos no mtodo Draw uma verificao para mudar a cor do fundo
quando ocorrer coliso e renderizamos os personagens:

46
//Muda a cor do fundo para vermelho quando uma coliso for detectada
if(colisao)
GraphicsDevice.Clear(Color.Red);
else
GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin();

spriteBatch.Draw(jogador, posicao, Color.White);

spriteBatch.Draw(jogador2, posicao2, Color.White);

spriteBatch.End();

Imagem ilustrando o retngulo de interseo entre os dois sprites e quando de fato h coliso

Observando as imagens acima, vemos que neste caso no poderamos utilizar a tcnica
com Bounding Box, pois seria detectada uma coliso onde, de fato, no ocorreu.

47
Animao 2D

48
Animao 2D:

Uma animao em um jogo de duas dimenses representada por sprites exibidos em


sequncia, assim como a forma de animao tradicional para desenhos animados.

Em jogos normalmente utilizam-se folhas de sprite. So imagens com as posies a


serem usadas no processo de animao de forma a evitar a utilizao de diversas texturas,
reduzindo o gasto excessivo de recursos do sistema e deixando a execuo do seu jogo mais
eficiente.

Folha de sprites com posies do jogo Super Mario World para Super Nintendo

Folha de sprites com posies laterais do Mario utilizada por ns

A imagem acima tem 230x34 pixels, sendo assim temos dez frames de 23x34 pixels.
importante que os quadros estejam bem definidos para o processo de animao ocorrer
de forma satisfatria.

Neste caso criaremos duas novas classes para efetuarmos a animao. Uma classe ser
responsvel por armazenar e realizar a animao de um sprite, enquanto a outra servir para
gerenciar os objetos animados criados com a classe anterior.

Vamos criar ento a classe para representar cada animao de um sprite. A


chamaremos de Animacao e a daremos o seguinte corpo:

public class Animacao


{

public Texture2D textura;


//Quantidade de frames a serem vistos por segundo
public int framesPorSegundo;
//Tempo gasto para exibio de um frame
public float tempoPorFrame;
//Tempo total decorrido desde a exibio do frame anterior
public float tempoTotal;
//Variavel Point semelhante a um Vector2, porm com menos mtodos
//Dimenses do frame
public Point tamanhoFrame;
//Armazena a coluna que est sendo utilizada
public int frameAtual;
//Armazena quantidade de colunas da imagem
public int tamanhoImagem;

49
//Posio da imagem onde deve comear a animao (primeiro frame)
public Point posicaoInicial;
//Se igual a true ativa o Loop da animao
public bool isLoop;

public Animacao(Texture2D textura, int framesPorSegundo, int colunas,


int larguraFrame, int alturaFrame, Point posicaoInicial)
{

this.textura = textura;
this.framesPorSegundo = framesPorSegundo;

//Converte para float para manter a preciso


//Para chegar ao resultado, dividimos 1 segundo pelo fps
this.tempoPorFrame = 1 / (float) framesPorSegundo;

this.tamanhoImagem = colunas;
this.tamanhoFrame = new Point(larguraFrame, alturaFrame);

//Para comear da primeira coluna


this.frameAtual = 0;

this.isLoop = false;

this.posicaoInicial = posicaoInicial;

public void Update(GameTime gameTime)


{
//Converte para float pois o valor recebido em double
//O tempoTotal incrementado at atingir o tempoPorFrame
tempoTotal += (float)gameTime.ElapsedGameTime.TotalSeconds;

if (tempoTotal > tempoPorFrame)


{
//Avana para o proximo frame na coluna
frameAtual++;

//Caso o frame atual passe da quantidade existente, ele recomea


//Se no, ele decrementado e fica preso ao ltimo frame
if (frameAtual >= tamanhoImagem)
{
if (isLoop)
{
frameAtual = 0;
}
else
{
frameAtual--;
}
}

//Reseta a varivel para que ela recomece a contagem de tempo


tempoTotal = 0;
}

50
public void Draw(ref SpriteBatch renderizador, Vector2 posicao)
{
renderizador.Draw(textura,posicao,new Rectangle(posicaoInicial.X +
(frameAtual * tamanhoFrame.X), posicaoInicial.Y,
tamanhoFrame.X, tamanhoFrame.Y),
Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0);
//Os valores 0, 1 e 0 so referentes a rotao, escala e profundidade
}

O cdigo foi bem comentado para melhor entender seus procedimentos. No mtodo
Draw utilizamos uma de suas sobrecargas para definir que rea da folha de sprites desenhar
com base nos parmetros repassados pelo mtodo construtor.

OBS: Como puderam observar, estamos passando o renderizador por referncia ao mtodo
Draw da classe Animacao. A referncia utilizada para economizar espao na memria e
deixar a execuo do mtodo mais eficiente, alm de utilizar exatamente a mesma varivel
utilizada pela classe principal (referncia direta ao endereo de memria em que se encontra).

Resumidamente esta classe fica responsvel por criar na tela uma animao de um
sprite com base na posio inicial e dimenses do frame encontrado na textura e a quantidade
de colunas que existem aps ele (ambas definidas por voc).

Agora iremos criar a classe responsvel por gerenciar as animaes criadas com a
classe acima. A chamaremos de AnimacaoCollectione a daremos o seguinte corpo:

public class AnimacaoCollection


{

//Cria um Dictionary com uma string como chave e uma Animacao como valor
public Dictionary<string, Animacao> animacoes =
new Dictionary<string, Animacao>();

public string animacaoAtual;

public Vector2 posicao = Vector2.Zero;

public AnimacaoCollection() { }

public void AddAnimacao(Animacao animacao, string nome)


{
animacoes.Add(nome, animacao);
}

public void trocaAnimacao(string nome)


{
animacaoAtual = nome;
}

public void Update(GameTime gameTime)


{
animacoes[animacaoAtual].Update(gameTime);
}

51
public void Draw(ref SpriteBatch renderizador)
{
animacoes[animacaoAtual].Draw(ref renderizador, posicao);
}
}

A classe acima fica responsvel por registrar novas animaes, trocar a animao em
execuo com base em uma chave (string) e chamar seus mtodos Update e Draw.

Com as classes criadas, vamos a classe Game1 para as inserir em nosso jogo.

Primeiramente declaramos as variveis:

AnimacaoCollection marioAnimacoes;

Texture2D marioImagem;

No mtodo Initialize inicializamos a varivel marioAnimacoes:

marioAnimacoes = new AnimacaoCollection();

No mtodo LoadContent carregamos a textura e criamos as animaes:

//Carrega a textura
marioImagem = Content.Load<Texture2D>(@"mario");

//Cria a animao correr para direita rodando a 10 fps com 5 frames


Animacao correDir = new Animacao(marioImagem, 10, 5,
23,34,new Point(0,0));

//Ativa o Loop da animao


correDir.isLoop = true;

marioAnimacoes.AddAnimacao(correDir, "correDir");

//Cria a animao correr para a esquerda, tendo seu frame inicial a partir do
ponto 115 no eixo x
Animacao correEsq = new Animacao(marioImagem, 10, 5,
23, 34, new Point(115, 0));

//Ativa o Loop da animao


correEsq.isLoop = true;

marioAnimacoes.AddAnimacao(correEsq, "correEsq");

//Define uma posio qualquer na janela


marioAnimacoes.posicao = new Vector2(200, 200);

//Define a animao inicial do sprite ao iniciar o jogo


marioAnimacoes.animacaoAtual = "correDir";

52
No mtodo Update fazemos a troca entre as animaes de acordo com as teclas
pressionadas e chamamos o Update da varivel do tipo AnimacaoCollection:

KeyboardState k = Keyboard.GetState();

if (k.IsKeyDown(Keys.Left))
{
marioAnimacoes.trocaAnimacao("correEsq");
marioAnimacoes.posicao.X -= 3;
}
else if (k.IsKeyDown(Keys.Right))
{
marioAnimacoes.trocaAnimacao("correDir");
marioAnimacoes.posicao.X += 3;
}

marioAnimacoes.Update(gameTime);

Feito isto basta adicionarmos a chamada para o mtodo Draw da varivel no mtodo
Draw da classe Game1:

renderizador.Begin();

marioAnimacoes.Draw(ref renderizador);

renderizador.End();

Se o seu programa foi codificado corretamente, ao executa-lo ser iniciada a animao


do sprite, tendo suas trocas alteradas de acordo com a movimentao definida pelas teclas
que forem pressionadas.

53
Fluxo de Telas

54
Criao e gerenciamento do fluxo de telas:

O fluxo de telas indispensvel em qualquer jogo, ele que define e realiza a troca
entre a tela de iniciar e o comeo do jogo ao pressionar Enter por exemplo.

Para aplicar o gerenciamento do fluxo de telas so utilizados conceitos de herana,


polimorfismo, override e enumeradores.

Neste exemplo trabalharemos com duas telas, uma tela de introduo e a outra do
jogo, sendo que ambas sero derivadas (herana) de uma classe chamada Tela.

Imagem ilustrativa da comunicao entre as telas Intro e Game que herdam a classe Tela

Alm das trs classes citadas, utilizaremos uma classe contendo um enumerador para
identificar em que estado se encontra a cena atual.

Antes de comearmos, importante saber a respeito do funcionamento de variveis


do tipo Dictionary. De forma simplificada, podemos dizer que um Dictionary trata-se de
uma lista indexada (assim como o HashMap, do Java). Ele nos permite armazenar uma chave e
um valor de qualquer tipo por posio.

Ex: Dictionary<chave,valor> lista;

Primeiramente, crie uma classe chamada Estado e crie um enum conforme abaixo:

public enum Estado


{
//Vazio
None,
//Introduo
Intro,
//Game
Game
}

Vale ressaltar que o arquivo no deve contar nenhuma classe definida (public class),
tendo ento apenas o enum.

Agora iremos criar a classe Tela como uma classe abstrata (pode ser herdada mas no
instanciada) que ter dois mtodos virtuais (que podem ser sobrepostos pelas suas classes
derivadas com override) Update e Draw:

55
public abstract class Tela
{
// Referncia para o jogo que chamou a cena
protected Game1 game;
protected SpriteBatch renderizador;

public Tela(Game1 game)


{
this.game = game;
// Recebe o renderizador do jogo via services do XNA
this.renderizador =
(SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
}

public virtual void Update(GameTime gameTime)


{
}

public virtual void Draw(GameTime gameTime)


{
}
}

A classe tem a funo de receber o objeto do tipo Game1 para ento utilizar os
servios do jogo, neste caso o renderizador.

OBS: O objeto Services faz parte das classes derivadas de Game, que faz parte da estrutura
do XNA. Ele permite a interao de componentes entre as classes que de outras formas seriam
difceis ou impossveis.

Feito isto, criaremos agora as duas classes herdeiras da classe Tela e iremos sobrepor
os mtodos Update e Draw. A primeira chamaremos de Intro e a segunda de Game, conforme
abaixo:

public class Intro : Tela // Classe para a cena de introduo


{
private Texture2D textura;

// O commando base abaixo chama o mtodo construtor da classe Tela e


repassa a varivel game
public Intro (Game1 game)
: base(game)
{
this.texture = game.Content.Load<Texture2D>(@"intro");
}

public override void Update(GameTime gameTime)


{
KeyboardState keyState = Keyboard.GetState();
// Verifica se foi pressionado a tecla Enter
if (keyState.IsKeyDown(Keys.Enter))
{
//Troca de Cena (para o game)
}
base.Update(gameTime);
}

public override void Draw(GameTime gameTime)


{
renderizador.Draw(textura, Vector2.Zero, Color.White);

56
base.Draw(gameTime);
}
}
---
public class Game : Tela // Classe para o game
{
private Texture2D textura;

public Game(Game1 game)


: base(game)
{
this.texture = game.Content.Load<Texture2D>(@"game");
}
public override void Update(GameTime gameTime)
{
KeyboardState keyState = Keyboard.GetState();

// Verifica se foi pressionado a tecla ESC


if (keyState.IsKeyDown(Keys.Escape))
{
// Troca de Cena (para a introduo)
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
renderizador.Draw(textura, Vector2.Zero, Color.White);
base.Draw(gameTime);
}
}

Neste caso, as duas classes so semelhantes, alterando apenas as imagens e a


verificao da tecla pressionada para alterar a tela do jogo, mas em seu projeto voc pode
modificar de acordo com as suas necessidades, desde que a lgica do fluxo se mantenha.

Com as classes criadas, vamos a classe Game1 e declaramos duas novas variveis
globais, sendo uma do tipo Dictionary e outra do tipo Estado (criado por ns):

//Dicionrio de cenas
Dictionary<Estado, Tela> telas;
//Cena atual do jogo
Estado telaAtual;

Agora iremos criar na classe Game1 um mtodo para fazer a troca entre as telas do
jogo:

public void TrocarTela(Estado proximaTela)


{
//Caso a cena atual seja diferente de vazia, remove do dicionrio
if (this.telaAtual != Estado.None)
this.telas.Remove(telaAtual);
// A cena atual recebe a prxima cena desejada
telaAtual = proximaTela;
// Caso a cena seja alguma especificada realiza a insero da mesma
no dicionrio
switch (proximaTela)
{
case Estado.Intro:
// Adiciona a cena de introduo ao dicionrio
this.telas.Add(Estado.Intro, new Intro(this));
break;

57
case Estado.Game:
// Adiciona a cena de jogo ao dicionrio
this.telas.Add(Estado.Game, new Game(this));
break;
}
}

Com o mtodo pronto, inicializamos o nosso Dictionary no mtodo Initialize e


adicionamos na funo LoadContent o renderizador do Game1 aos servios
disponibilizados e a troca inicial para a cena de introduo:

telas = new Dictionary<Estado, Tela>(); // Inicializa o dictionary telas

// Adicionando sprite batch aos servios do jogo


Services.AddService(typeof(SpriteBatch), renderizador);
// Trocando a cena para a cena de introduo
TrocarTela(Estado.Intro);

Nos mtodos Draw e Update, adicionamos uma chamada para atualizar e


desenhar a tela atual do jogo:

protected override void Update(GameTime gameTime)


{
this.telas[telaAtual].Update(gameTime); // Atualizando a cena atual
base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)


{
GraphicsDevice.Clear(Color.CornflowerBlue);
renderizador.Begin();
this.telas[telaAtual].Draw(gameTime); // Desenhando a cena atual
renderizador.End();
base.Draw(gameTime);
}

E por fim, adicionamos as trocas a serem realizadas pelas classes Intro e Game ao
pressionar as teclas Enter e Esc:

// Troca de Cena (cena de jogo) Inserir na verificao do Update do Intro


game.TrocarTela(GameState.Game);
// Troca de Cena (cena de introduo) Inserir na verificao do Update do Game
game.TrocarTela(GameState.Intro);

Transio entre duas telas utilizando o algoritimo ensinado acima

Feito o passo a passo, basta compilar e executar o cdigo para testar o resultado final.

58
Contruo de fases

59
Definio de Tiles:

Os Tiles so pequenas imagens (em blocos, parte de um cenrio maior), que so


dispostas de forma a gerarem um estgio ou mapa de uma fase em um jogo, com uma ou mais
camadas (layers).

Imagem ilustrando um cenrio formado por tiles (fonte: http://www.tonypa.pri.ee)

Essa tcnica conhecida como TileMap, e foi desenvolvida devido a falta de espao
nas mdias de armazenamento e poder de processamento na poca de seu surgimento, onde
utilizar grandes imagens como cenrios era invivel.

Abaixo alguns jarges utilizados para o trabalho com tiles:

Tileset: Paleta de imagens contendo diversos tiles. Utilizada para construo dos
mapas.

Tilemap: Mapa formado por tiles.

Layer: Cada layer representa uma camada de tiles, ou seja, em um jogo podemos ter
mais de um layer (tiles sobrepondo outros tiles).

Descrio do funcionamento da tnica:

Primeiramente define-se os tiles que sero utilizados, com seu tamanho, imagem e
posio:

Imagem com tiles e suas respectivas numeraes (fonte: http://kleberandrade.wordpress.com)

60
Logo aps se monta um tilemap com especificaes da posio de cada tile (com base
em sua numerao e/ou posio) que compor o cenrio:

Imagem de um tilemap, composto por uma matriz de nmeros


(fonte: http://kleberandrade.wordpress.com)

E por fim construmos um algortimo que substitua o nmero da matriz no tilemap


pelos seus tiles equivalentes:

Imagem com um mapa construdo por tiles (fonte: http://kleberandrade.wordpress.com)

61
Criao do projeto:

Para criarmos e desenharmos um mapa com tiles criaremos duas classes, uma
chamada Tiles e outra chamada Mapa.

Tileset utilizada em nosso exemplo

A imagem utilizada em nosso exemplo contm duzentos e cinquenta e seis tiles, cada
um com dimenses de 32x32. Criaremos um tilemap (matriz) com 15x15 tiles, ou seja, ter 480
pixels de largura e altura.

A comear pela classe Tile, criaremos trs variveis globais, um mtodo construtor
para inicializ-las e um mtodo para verificar se houve coliso entre o tile e um outro
retngulo qualquer:

public class Tile


{
public Rectangle retangulo;
public int tipo;
public bool andavel;

public Tile(int tipo, bool andavel, int x, int y, int larguraTile,


int alturaTile)
{
this.tipo = tipo;
this.andavel = andavel;
this.retangulo = new Rectangle(x, y, larguraTile, alturaTile);
}

public bool Collide(Rectangle box)


{
return this.retangulo.Intersects(box);
}
}

Cada tile formado por um retngulo de uma imagem maior, sendo este inicializado
no mtodo construtor com os pontos x e y em que se encontra o tile na tileset e a sua
largura e altura.

62
As variveis tipo e andavel vo servir para verificar se podemos passar ou no
sobre o tile, sendo que o tipo a numerao que utilizamos em nossa matriz (tilemap).

O mtodo Collide far a verificao de uma possvel coliso entre dois retngulos
(semelhante ao Bounding Box).

O mapa que utilizaremos uma matriz com dimenses de 15x15, disposta da seguinte
forma:

{ 147, 148, 148, 148, 148, 148, 145, 147, 148, 148, 148, 148, 148, 145, 146 },
{ 86, 86, 142, 86, 142, 86, 86, 86, 86, 86, 142, 86, 86, 142, 87 },
{ 86, 86, 86, 86, 142, 86, 86, 86, 86, 159, 86, 86, 86, 86, 87 },
{ 0, 0, 85, 86, 86, 87, 0, 0, 85, 86, 86, 86, 87, 0, 0 },
{ 0, 0, 85, 142, 86, 103, 0, 0, 101, 86, 142, 86, 103, 0, 0 },
{ 0, 0, 101, 102, 103, 0, 0, 0, 0, 101, 102, 103, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 70, 72, 0, 0, 0 },
{ 0, 221, 0, 0, 223, 0, 0, 0, 119, 119, 142, 121, 122, 0, 0 },
{ 0, 221, 0, 0, 223, 0, 0, 0, 139, 86, 159, 86, 144, 0, 0 },
{ 0, 237, 238, 238, 239, 0, 0, 0, 139, 86, 86, 137, 103, 0, 0 },
{ 0, 237, 238, 238, 239, 0, 0, 0, 101, 102, 102, 138, 0, 0, 0 },
{ 0, 253, 254, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }

Cada nmero da matriz acima representa um dos tiles de nossa tileset, a comear do
0 e finalizando com o 255 (256 no total).

Criaremos a classe Mapa para realizar a leitura da matriz e desenhar os tiles em


nossa janela. Primeiro declaramos as suas variveis e o seu mtodo construtor:

//Imagem com os tiles


private Texture2D tileset;
//Vetor com as posies de cada tile do tileset
private Rectangle[] tileFrames;
//Matriz de tiles
private Tile[,] tiles;
//Nmero de linhas da matriz de tiles
private int linhas;
//Nmero de colunas da matriz de tiles
private int colunas;
//Altura do tile
private int tileAltura;
//Largura do tile
private int tileLargura;
//Cria uma constante inteira para representar o tipo de um tile no andavel
private const int TIPO_NAO_ANDAVEL = 0;

63
//Mtodo construtor para inicializar as variaveis
public Mapa(Texture2D tileset, int linhas, int colunas,
int tileLargura, int tileAltura)
{
this.linhas = linhas;
this.colunas = colunas;
this.tileLargura = tileLargura;
this.tileAltura = tileAltura;
this.tiles = new Tile[linhas, colunas];

this.tileset = tileset;
}

Agora criaremos um mtodo para armazenar um retngulo com a posio (frame) de


cada tile da tileset utilizada, o chamaremos de GerarTileFrames:

public void GerarTileFrames()


{
//Armazena a quantidade de tiles da tileset
int tileSprites = (this.tileset.Width / this.tileLargura) *
(this.tileset.Height / this.tileAltura);

this.tileFrames = new Rectangle[tileSprites];


//Indice para contar as posies do array tileFrames
int indice = 0;

//Largura da imagem do tile


int width = this.tileset.Width;
//Altura da imagem do tile
int height = this.tileset.Height;

//Percorre todos os tiles da tileset


for (int y = 0; y < height; y += this.tileAltura)
{
for (int x = 0; x < width; x += this.tileLargura)
{
//Armazena as posies (rectangle) correspondentes a cada
tipo de tile
this.tileFrames[indice] = new Rectangle(x, y,
this.tileLargura, this.tileAltura);
indice++;
}
}
}

O mtodo acima inicializa nosso array e passa a ele todas as posies dos tiles de nossa
tileset.

Criaremos mais dois mtodos, desta vez para carregar nosso mapa (matriz) e definir a
posio de cada tile em nossa tela e um outro para percorrer a nova matriz gerada e desenhar
um de cada vez em nossa janela:

public void CarregarMapa(int[,] matriz)


{
/* testa se a matriz parmetro tem
* dimenses diferentes da matriz de tiles
*/
if (matriz.GetLength(0) != this.tiles.GetLength(0) ||
matriz.GetLength(1) != this.tiles.GetLength(1))
{

64
throw new Exception("Matriz no possui a mesma dimenso do
Mapa");
}
//testa se a imagem dos tiles foi carregada
if (this.tileset == null)
{
throw new Exception("Imagem do mapa no foi carregada");
}
//gera os frames dos tiles
this.GerarTileFrames();

//posio x do tile
int x = 0;
//posio y do tile
int y = 0;
//Tipo do tile que vai ser lido da matriz (inicializa com -1)
int tipo = -1;
//indica se o tile pode ser atravessado
bool andavel = true;

for (int i = 0; i < this.linhas; i++)


{
for (int j = 0; j < this.colunas; j++)
{
//Atribui a "tipo" o tipo (nmero) do tile
tipo = matriz[i, j];

//Verifica se a posio da matriz menor que zero ou maior


que a quantidade de tiles ("frames")
if (tipo < 0 || tipo > this.tileFrames.Length)
{
throw new Exception("Tipo de tile invlido");
}
else
{
//Testa se o tipo lido no-andvel
//Em nosso exemplo temos apenas um, porm poderia-se
criar um vetor com os tiles que no so andveis e realizar a verificao
if (tipo == TIPO_NAO_ANDAVEL)
andavel = false;
//Cria o tile na posio x, y de nossa janela com as suas
dimenses
this.tiles[i, j] = new Tile(tipo, andavel, x, y,
this.tileLargura, this.tileAltura);
}
//"Reseta" a varivel para true
andavel = true;
//Soma em X a largura do tile
x += this.tileLargura;
}
//"Reseta" a posio no eixo X
x = 0;
//Soma em Y a altura do tile
y += this.tileAltura;
}
}

65
public void Draw(ref SpriteBatch renderizador)
{
for (int i = 0; i < this.linhas; i++)
{
for (int j = 0; j < this.colunas; j++)
{
Rectangle destino = new Rectangle(
this.tiles[i, j].retangulo.X,
this.tiles[i, j].retangulo.Y,
this.tileLargura,
this.tileAltura);

int frame = this.tiles[i, j].tipo;

renderizador.Draw(this.tileset, destino,
this.tileFrames[frame], Color.White);
}
}
}

Para renderizarmos os tiles, um de cada vez, utilizamos uma sobrecarga do mtodo


Draw onde passamos como parmetro a imagem, o local da tela onde ela ser desenhada, a
rea da imagem que ser desenhada e a cor.

Com as classes finalizadas, vamos a classe Game1 utiliz-las em nosso projeto.


Primeiramente declaramos as variveis necessrias:

Mapa mapa;
//estrutura do mapa
int[,] matrizMapa =
{
{ 148, 149, 149, 149, 149, 149, 146, 148, 149, 149, 149, 149, 149, 146, 147 },
{ 87, 87, 143, 87, 143, 87, 87, 87, 87, 87, 143, 87, 87, 143, 87 },
{ 87, 87, 87, 87, 143, 87, 87, 87, 87, 159, 87, 87, 87, 87, 87 },
{ 1, 1, 86, 87, 87, 88, 1, 1, 86, 87, 87, 87, 88, 1, 1 },
{ 1, 1, 86, 143, 87, 104, 1, 1, 102, 87, 143, 87, 104, 1, 1 },
{ 1, 1, 102, 103, 104, 1, 1, 1, 1, 102, 103, 104, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 70, 71, 72, 1, 1, 1 },
{ 1, 222, 1, 1, 224, 1, 1, 1, 120, 121, 143, 122, 123, 1, 1 },
{ 1, 222, 1, 1, 224, 1, 1, 1, 141, 87, 159, 87, 144, 1, 1 },
{ 1, 238, 239, 239, 240, 1, 1, 1, 141, 87, 87, 138, 104, 1, 1 },
{ 1, 238, 239, 239, 240, 1, 1, 1, 102, 103, 103, 139, 1, 1, 1 },
{ 1, 254, 255, 255, 256, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//largura do tile
const int LARGURA_TILE = 32;
//altura do tile
const int ALTURA_TILE = 32;
//largura da tela
const int LARGURA_TELA;
//altura da tela
const int ALTURA_TELA;

Como mencionado anteriormente, nosso mapa representado por uma matriz de


15x15, sendo que cada tile possui 32x32 pixels, sendo assim nosso mapa tem uma extenso
total de 480x480.

66
No mtodo construtor (Game1) calculamos a resoluo da tela de acordo com o
tamanho da matriz e dos pixels (dimenso do mapa) e com base no resultado mudamos a
resoluo da janela:

//Multiplica a largura do tile pelo numero de colunas da matriz


LARGURA_TELA = LARGURA_TILE * matrizMapa.GetLength(0);
//Multiplica a altura do tile pelo numero de linhas da matriz
ALTURA_TELA = ALTURA_TILE * matrizMapa.GetLength(0);
//define a largura da tela
this.graphics.PreferredBackBufferWidth = LARGURA_TELA;
//define a altura da tela
this.graphics.PreferredBackBufferHeight = ALTURA_TELA;

No mtodo Initialize inicializamos a nossa varivel mapa e chamamos o mtodo


CarregarMapa da mesma:

//Cria o mapa
this.mapa = new Mapa(Content.Load<Texture2D>("tiles"),matrizMapa.GetLength(0),
matrizMapa.GetLength(1), LARGURA_TILE, ALTURA_TILE);

//Carrega o mapa
this.mapa.CarregarMapa(this.matrizMapa);

E por fim, no mtodo Draw desenhamos nosso mapa:

this.spriteBatch.Begin();
//Desenha o mapa na tela
this.mapa.Draw(ref this.spriteBatch);
this.spriteBatch.End();

Imagem com os tiles desenhados utilizando a tcnica ensinada acima.

OBS: A tcnica ensinada nos permite trabalhar com apenas um layer de tiles. Para criarmos um
segundo ou terceiro layer precisaramos construir uma nova matriz com o novo layer, declarar
um novo objeto do tipo Mapa e o desenhar sob ou sobre o layer atual no mtodo Draw,
ou ento modificar a classe Mapa para que suporte mais que um.

67
Utilizao de arquivos .XML:

Antes de continuarmos com o estudo dos tiles, precisamos saber melhor a respeito da
utilizao de arquivos .XML em um projeto no XNA (O editor de mapa que usaremos mais
adiante trabalha com este formato).

Para realizarmos a leitura direta de um arquivo .xml (que no seja no formato


estabelecido pelo xna) utilizamos uma varivel do tipo XmlTextReader. Vale ressaltar que
no devemos inserir o arquivo no Content de nosso projeto, j que ele ser carregado
externamente sem a utilizao do framework XNA.

OBS: O xna tem suporte nativo a arquivos .xml, desde que sigam um padro pr-estabelecido
em sua escrita, o que no o caso do arquivo gerado pelo editor de mapas que usaremos.

Com o nosso arquivo .xml gerado, o copiamos para a pasta Debug de nosso projeto.
Vamos agora a nossa classe que desejamos realizar a leitura e declaramos uma varivel para a
operao (aps importar a biblioteca System.Xml):

XmlTextReader leitorxml;

Agora iremos a um mtodo qualquer que desejemos utilizar para a inicializao, e


carregamos nosso arquivo para a varivel da seguinte forma:

leitorxml = new XmlTextReader("arquivo.xml");

Com a varivel declarada e inicializada podemos utilizar os mtodos para manipulao do


arquivo (somente leitura). Abaixo pode-se conferir alguns dos principais:

.Read(): Utilizado para avanar at a prxima informao do arquivo. Geralmente


inserido dentro de laos while para realizar a leitura do arquivo at o seu fim;

.NodeType: Retorna o tipo do n atual;

.Name: Retorna o nome do campo atual;

.Value: Retorna o valor (em string) da linha atual;

.GetAttribute((int) index): Utilizado para obter o atributo da declarao do campo


atual.

Alm da leitura convencional podemos ler e salvar aquivos serializveis, o que facilita
bastante todo o processo. Abaixo uma pequena descrio a respeito da serializao:

De forma genrica a serializao uma tcnica usada para persistir objetos , ou seja :
gravar objetos em disco , fazer a transmisso remota de objetos via rede , armazenar os
objetos em um banco de dados e/ou arquivos (binrios , xml, etc.)

Serializar nada mais do que colocar os valores que o objeto est utilizando juntamente
com suas propriedades de uma forma que fique em srie (sequencial) . Fazendo isto estamos
tornando o objeto Serializable, e, tornando um objeto Serializable, estamos atribuindo essa
qualidade a ele, e dando privilgios para que o mesmo possa ser gravado em disco ou enviado
por rede.

68
A serializao o processo de armazenar um objeto , incluindo todos os atributos pblicos
e privados para um stream. (http://www.macoratti.net/vbn_seri.htm)

No C# para realizarmos o processo de serializao em xml utilizamos uma varivel do tipo


XmlSerializer (precisamos importar primeiramente a biblioteca System.Xml.Serialization).

Sua utilizao se da de maneira bem simples. Primeiramente declaramos uma varivel e a


inicializamos:

XmlSerializer serializer = new XmlSerializer(typeof(TIPO));

A nossa varivel inicializada com o tipo do arquivo que queremos serializar


(geralmente do tipo de uma struct ou classe). E por fim ns o serializamos com o mtodo
Serialize():

serializer.Serialize(stream, data);

O objeto data do tipo que deseja-se serializar, o stream do tipo FileStream,


utilizado para fluxo de dados em um arquivo carregado por meio do File.Open().

OBS: Para carregar um arquivo utilizando o File.Open, primeiramente devemos importar a


bibliotca System.IO para nosso projeto, e em uma sobrecarga do mtodo passamos dois
paramtros bsicos (possui outras sobrecargas), o caminho para o arquivo e o modo da
operao, por exemplo:

File.Open(filename, FileMode.OpenOrCreate);

Onde o filename uma string, e o modo da operao de abertura e criao (caso no exista).

Para realizarmos o processo inverso (desserializar) ns utilizamos o mtodo


Deserialize(), como abaixo:

data = (TIPO)serializer.Deserialize(stream);

Passamos como parmetro um objeto do tipo FileStream (com o fluxo para um


determinado arquivo) e fazemos um cast para o tipo de nosso objeto na hora de atribui-lo a
uma varivel.

69
Construindo mapas com o XNA Tile Map Editor:

O XNA Tile Map Editor uma ferramente livre desenvolvida pelo programador
Armando Alva da desenvolvedora NandoSoft e pode ser baixada gratuitamente pelo seu site
(http://www.nandosoft.com/).

OBS: Para utilizar o programa necessrio instalar o Microsoft XNA Framework


Redistributable 3.1 (apenas para execuo), pois o mesmo foi feito com o XNA 3.1.

Tela inicial do XNA Tile Map Editor

Ao iniciar o programa, ser aberta a tela acima com um espao para criarmos nosso
mapa. Para modificarmos as dimenses do mapa e a quantidade de layers que utilizaremos,
vamos a janela Resize Map atrves do caminho Edit/Resize Map.

Janela Resize Map

Em nosso exemplo criaremos um mapa de 15 tiles de largura por 10 tiles de altura.

70
Para modificarmos o tamanho de um tile, vamos em Edit / Change Tile Size (em
nosso mapa manteremos o tamanho padro de uma tileset, ou seja, 32):

Janela Change Tile Size

Com o tamanho dos tiles e a dimenso definidos, vamos importar uma tileset para
desenharmos nosso mapa. Para importar uma tileset, vamos em File / Load Tileset e
selecionamos a tileset desejada.

Tileset utilizada para construo do nosso mapa

Com a tileset carregada voc pode desenhar seu mapa utilizando as opes na barra
abaixo do menu, dentre as quais voc pode definir o layer que deseja desenhar, se utilizar o
pincel, o marcador, o balde de tinta ou a borracha.

Mapa desenhado em trs layers diferentes

71
Com o mapa pronto, basta clicar em Options / Export to XML e selecionar o diretrio
que deseja salvar o arquivo. Lembrando que para utilizar esse arquivo em seu projeto, o
mesmo deve ser posto dentro da pasta Debug.

OBS: O XNA Tile Map Editor conta com uma opo para selecionar os locais em que haver
coliso no seu mapa, o mesmo utilizado como o marcador do pincel e pode ser selecionado
na barra de opes.

Para utilizarmos o arquivo xml gerado pelo programa criaremos uma nova classe
CarregarMapaXML e faremos algumas alteraes nas classe Mapa e Tile mostradas
anteriormente.

Primeiramente alteramos a classe Tile acrescentando uma varivel booleana para


definir se o tile deve ser desenhado:

public class Tile


{
public Rectangle retangulo;
//numerao usada na matriz
public int tipo;
public bool andavel;
//Atributo para definir se o tile vai ser desenhado (para caso o tile
seja transparente ou no exista)
public bool transparente;

public Tile(int tipo, bool andavel, bool transparente, int x, int y, int
larguraTile,
int alturaTile)
{
this.tipo = tipo;
this.andavel = andavel;
this.retangulo = new Rectangle(x, y, larguraTile, alturaTile);
this.transparente = transparente;
}

public bool Collide(Rectangle box)


{
return this.retangulo.Intersects(box);
}
}

Agora modificaremos a classe Mapa para que aceite mais de um layer, dentre outras
pequenas alteraes que podem ser conferidas abaixo:

public class Mapa


{

//Imagem com os tiles


public Texture2D tileset;
//Vetor com as posies de cada tile do tileset
public Rectangle[] tileFrames;
//Lista de matrizes de tiles
public List<Tile[,]> tiles;
//Nmero de linhas da matriz de tiles
public int linhas;
//Nmero de colunas da matriz de tiles
public int colunas;
//Altura do tile
public int tileAltura;

72
//Largura do tile
public int tileLargura;
//Lista de matrizes de inteiros (para os layers)
public List<int[,]> matrizLayer;
//Matriz para locais com coliso ativa
public int[,] matrizColisao;

//Mtodo construtor para inicializar algumas variaveis


public Mapa(Texture2D tileset)
{
this.tileset = tileset;

this.tiles = new List<Tile[,]>();


this.matrizLayer = new List<int[,]>();
}

public void GerarTileFrames()


{
//Armazena a quantidade de tiles da tileset
int tileSprites = (this.tileset.Width / this.tileLargura) *
(this.tileset.Height / this.tileAltura);

this.tileFrames = new Rectangle[tileSprites];


//Indice para contar as posies do array tileFrames
int indice = 0;
//Largura da imagem do tile
int width = this.tileset.Width;
//Altura da imagem do tile
int height = this.tileset.Height;

//Percorre todos os tiles da tileset


for (int y = 0; y < height; y += this.tileAltura)
{
for (int x = 0; x < width; x += this.tileLargura)
{
//Armazena as posies (rectangle) correspondentes a cada tipo de
tile
this.tileFrames[indice] = new Rectangle(x, y, this.tileLargura,
this.tileAltura);
indice++;
}
}
}

public void CarregarMapa(int[,] matriz, int layer)


{
/* Testa se a matriz coliso tem
dimenses diferentes da matriz de tiles */
if (matrizColisao.GetLength(0) != this.tiles[layer].GetLength(0) ||
matrizColisao.GetLength(1) != this.tiles[layer].GetLength(1))
{
throw new Exception("Matriz de Coliso no possui a mesma dimenso do
Mapa");
}
//Gera os frames dos tiles
this.GerarTileFrames();

//posio x do tile
int x = 0;
//posio y do tile
int y = 0;
//Tipo do tile que vai ser lido da matriz (inicializa com -1)
int tipo = -1;
//indica se o tile pode ser atravessado
bool andavel = true;
//Indica se o espao do tile vai ser desenhado

73
bool transparente = false;

for (int i = 0; i < this.linhas; i++)


{
for (int j = 0; j < this.colunas; j++)
{
//Atribui a "tipo" o tipo (nmero) do tile
tipo = matriz[i, j];
//Verifica se a posio da matriz menor que -1 ou maior que a
quantidade de tiles ("frames")
if (tipo < -1 || tipo > this.tileFrames.Length)
{
throw new Exception("Tipo de tile invlido");
}
else
{
//Testa se o tipo lido deve ser desenhado
if (tipo == -1)
transparente = true;
//Testa se o tipo lido no-andvel
//Em nosso exemplo temos apenas um, porm poderia-se criar um
vetor com os tiles que no so andveis e realizar a verificao
if (matrizColisao[i, j] == 0)
andavel = false;
//Cria o tile na posio x, y de nossa janela com as suas
dimenses
this.tiles[layer][i, j] = new Tile(tipo, andavel,
transparente, x, y, this.tileLargura, this.tileAltura);
}
//"Reseta" as variveis
andavel = true;
transparente = false;
//Soma em X a largura do tile
x += this.tileLargura;
}
//"Reseta" a posio no eixo X
x = 0;
//Soma em Y a altura do tile
y += this.tileAltura;
}
}

//O mtodo Draw desenha apenas o layer que for passado como paramtro
public void Draw(ref SpriteBatch batch, int layer)
{
for (int i = 0; i < this.linhas; i++)
{
for (int j = 0; j < this.colunas; j++)
{
Vector2 destino =
new Vector2(this.tiles[layer][i, j].retangulo.X,
this.tiles[layer][i, j].retangulo.Y);

int frame = this.tiles[layer][i, j].tipo;

//Caso o tile no seja transparente, o desenhar


if (this.tiles[layer][i, j].transparente == false)
batch.Draw(this.tileset, destino, this.tileFrames[frame],
Color.White);
}
}
}

74
Os objetos do tipo List<> utilizados funcionam como um vetor dinmico, ou seja,
criam uma lista indexada que pode receber novos valores em novas posies. Em nosso caso
declaramos listas de matrizes.

Com as classes alteradas, criaremos agora a CarregarMapaXML. Como no temos


acesso ao modelo serilizavel do xml gerado pelo editor (o que pode ser obtido estudando o
seu cdigo fonte), ns criaremos um modelo bem rudimentar para realizar a leitura do
arquivo:
//Biblioteca extras utilizadas
using System.Xml;
using Microsoft.Xna.Framework.Graphics;

class CarregarMapaXML
{
#region Variveis Globais
XmlTextReader mapaxml;
bool layer1Ativo;
bool layer2Ativo;
bool layer3Ativo;
bool layerColisaoAtivo;

List<List<int>> listaLayer1X = new List<List<int>>();


List<int> listaLayer1Y = new List<int>();

List<List<int>> listaLayer2X = new List<List<int>>();


List<int> listaLayer2Y = new List<int>();

List<List<int>> listaLayer3X = new List<List<int>>();


List<int> listaLayer3Y = new List<int>();

List<List<int>> listaLayerColisaoX = new List<List<int>>();


List<int> listaLayerColisaoY = new List<int>();

Mapa mapa;
int contadorLayer;
#endregion

#region Mtodos para Inicializao


public CarregarMapaXML(string ARQUIVO, Texture2D tileset)
{
//Carrega o arquivo xml para a varivel
this.mapaxml = new XmlTextReader(ARQUIVO);
//Armazena a quantidade de layers existentes
this.contadorLayer = getQuantidadeLayer(new XmlTextReader(ARQUIVO));
//Verifica se a quantidade maior que o mximo de layers suportados
if (this.contadorLayer > 3 || this.contadorLayer <= 0)
throw new Exception("Erro! Numero de layers nao suportado.");
//Inicializa o mapa
this.mapa = new Mapa(tileset);
//Inicializa os boleanos
this.layer1Ativo = false;
this.layer2Ativo = false;
this.layer3Ativo = false;
this.layerColisaoAtivo = false;
}

//Retorna a quantidade de layers do arquivo xml


public int getQuantidadeLayer(XmlTextReader arquivo)
{
while (arquivo.Read())
{
if (arquivo.Name == "Layers")
{
arquivo.Read();
return Convert.ToInt32(arquivo.Value);
}
}
//Caso no encontre nada, retorna 0
return 0;
}

75
#endregion

#region Mtodos Pblicos (Carregar informaes do .xml)

//L o arquivo xml e nos retorna um objeto do tipo mapa inicializado com os valores
carregados
public Mapa lerMapa()
{
while (mapaxml.Read())
{
//Verifica se o n em que se encontra a leitra igual a uma declarao
<declarao>
if (mapaxml.NodeType == XmlNodeType.Element)
{
switchLinha(mapaxml);
}
}
//Carrega para a matriz de inteiros nossa lista dinmica de coliso
carregarMatrizColisao(listaLayerColisaoX);

for (int i = 0; i < this.contadorLayer; i++)


{
//Adiciona a lista uma nova matriz de tiles referente ao layer atual
this.mapa.tiles.Add(new Tile[this.mapa.linhas, this.mapa.colunas]);
//Carrega para a matriz de inteiros nossa lista dinmica do primeiro layer
if (i == 0)
carregarMatrizLayer(listaLayer1X, 0);
//Carrega para a matriz de inteiros nossa lista dinmica do segundo layer
else if (i == 1)
carregarMatrizLayer(listaLayer2X, 1);
//Carrega para a matriz de inteiros nossa lista dinmica do terceiro layer
else if (i == 2)
carregarMatrizLayer(listaLayer3X, 2);
//Carrega o mapa na classe Mapa (para a matriz de tiles)
this.mapa.CarregarMapa(this.mapa.matrizLayer[i],i);
}

return mapa;

public void switchLinha(XmlTextReader mapaxml)


{
switch (mapaxml.Name)
{
case "Width":
//Passa para a prxima linha
mapaxml.Read();
//Inicializa a quantidade de colunas
this.mapa.colunas = Convert.ToInt32(mapaxml.Value);
break;

case "Height":
mapaxml.Read();
//Inicializa a quantidade de linhas
this.mapa.linhas = Convert.ToInt32(mapaxml.Value);
break;

case "Tile_Size":
mapaxml.Read();
//Inicializa as dimenses do tile (somente quadrados)
this.mapa.tileAltura = this.mapa.tileLargura =
Convert.ToInt32(mapaxml.Value);
break;

case "Layer":
//Caso seja o layer 1
if (Convert.ToInt32(mapaxml.GetAttribute(0)) == 0)
{
this.layer1Ativo = true;
}

76
//Caso seja o layer 2
else if (Convert.ToInt32(mapaxml.GetAttribute(0)) == 1)
{
this.layer1Ativo = false;
this.layer2Ativo = true;
}
//Caso seja o layer 3
else if (Convert.ToInt32(mapaxml.GetAttribute(0)) == 2)
{
this.layer2Ativo = false;
this.layer3Ativo = true;
}
break;

case "CollisionLayer":
//Caso seja o layer de coliso
this.layer1Ativo = false;
this.layer2Ativo = false;
this.layer3Ativo = false;
this.layerColisaoAtivo = true;
break;

case "RowInfo":
mapaxml.Read();
String linha = mapaxml.Value;
//Captura os elementos entre as virgulas da linha
string[] coluna = linha.Split(',');

//Caso seja uma linha do layer 1


if (layer1Ativo)
{
for (int i = 0; i < coluna.Length; i++)
{
//Subtrai 1 pois o Map Editor gera uma matriz com os tiles a partir
do 1 e no do 0
//O valor -1 utilizado para espaos sem tiles, ou seja, com fundo
transparente
if (Convert.ToInt32(coluna[i]) != -1)
this.listaLayer1Y.Add(Convert.ToInt32(coluna[i]) - 1);
else
this.listaLayer1Y.Add(Convert.ToInt32(coluna[i]));
}
//Adiciona a lista de colunas na lista de linhas
this.listaLayer1X.Add(listaLayer1Y);
//Reseta a lista de colunas
this.listaLayer1Y = new List<int>();

else if (layer2Ativo)
{
for (int i = 0; i < coluna.Length; i++)
{
//Subtrai 1 pois o Map Editor gera uma matriz com os tiles a partir
do 1 e no do 0
if (Convert.ToInt32(coluna[i]) != -1)
this.listaLayer2Y.Add(Convert.ToInt32(coluna[i]) - 1);
else
this.listaLayer2Y.Add(Convert.ToInt32(coluna[i]));
}
//Adiciona a lista de colunas na lista de linhas
this.listaLayer2X.Add(listaLayer2Y);
//Reseta a lista de colunas
this.listaLayer2Y = new List<int>();

else if (layer3Ativo)
{
for (int i = 0; i < coluna.Length; i++)
{

77
//Subtrai 1 pois o Map Editor gera uma matriz com os tiles a partir
do 1 e no do 0
if (Convert.ToInt32(coluna[i]) != -1)
this.listaLayer3Y.Add(Convert.ToInt32(coluna[i]) - 1);
else
this.listaLayer3Y.Add(Convert.ToInt32(coluna[i]));
}
//Adiciona a lista de colunas na lista de linhas
this.listaLayer3X.Add(listaLayer3Y);
//Reseta a lista de colunas
this.listaLayer3Y = new List<int>();

else if (layerColisaoAtivo)
{
for (int i = 0; i < coluna.Length; i++)
{
this.listaLayerColisaoY.Add(Convert.ToInt32(coluna[i]));

}
//Adiciona a lista de colunas na lista de linhas
this.listaLayerColisaoX.Add(listaLayerColisaoY);
//Reseta a lista de colunas
this.listaLayerColisaoY = new List<int>();

break;
}
}

//Repassa os valores da lista dinmica para a matriz de layers do Mapa


public void carregarMatrizLayer(List<List<int>> mLayer, int layer)
{
//Adiciona a lista uma nova matriz com os dados do layer passado
this.mapa.matrizLayer.Add(new int[this.mapa.linhas, this.mapa.colunas]);

for (int x = 0; x < this.mapa.linhas; x++)


{
for (int y = 0; y < this.mapa.colunas; y++)
{
this.mapa.matrizLayer[layer][x, y] = mLayer[x][y];
}
}
}

//Repassa os valores da lista dinmica para a matriz de coliso do Mapa


public void carregarMatrizColisao(List<List<int>> listaLayerColisao)
{
//Adiciona a lista uma nova matriz com os dados do layer de coliso passado
this.mapa.matrizColisao = new int[this.mapa.linhas, this.mapa.colunas];

for (int x = 0; x < this.mapa.linhas; x++)


{
for (int y = 0; y < this.mapa.colunas; y++)
{
this.mapa.matrizColisao[x, y] = listaLayerColisao[x][y];
}
}
}

#endregion

A classe foi devidamente comentada para seu melhor entendimento. Como puderam
observar, a classe foi criada para aceitar at no mximo trs layers, o que pode ser modificado
de acordo com as suas preferncias.

78
Aps finalizar as classes, vamos a classe Game1 para utiliz-las em nosso jogo.
Primeiramente iremos declarar uma varivel global do tipo Mapa para armazenas as
informaes de nosso mapa e as desenhar na janela.

Mapa mapa;

Com a varivel declarada, vamos ao mtodo Initialize e carregamos o arquivo xml


por meio da classe CarregarMapaXML:

CarregarMapaXML mapaxml = new CarregarMapaXML("arquivo.xml",


Content.Load<Texture2D>("tileset"));
mapa = mapaxml.lerMapa();

O primeiro parmetro uma string com o nosso arquivo xml e o segundo a nossa
tileset carregada pelo Contet. Chamamos o mtodo lerMapa() e atribumos o seu retorno
a nossa varivel mapa.

Por fim vamos ao mtodo Draw e desenhamos nosso mapa na janela:

mapa.Draw(ref this.spriteBatch,0);
mapa.Draw(ref this.spriteBatch,1);
mapa.Draw(ref this.spriteBatch,2);

Como nosso mapa possui trs layers, fazemos trs chamadas ao mtodo Draw do
nosso mapa. Deixamos desta forma pois podemos gerar efeitos como desenhar nossos
personagens por trs de um determinado layer, fazendo com que, por exemplo, ele passe por
trs de uma rvore

Imagens com mapa gerado em trs layers e o efeito de nosso personagem se posicionar atrs da cabana

79
Cmera 2D

80
Criao de uma cmera 2D:

A cmera 2D um elemento indispensvel em diversos estilos de jogos. Ela


geralmente acompanha nosso personagem durante sua movimentao por um determinado
cenrio, tornando possvel a utilizao de fases que ocupem um espao maior que a janela de
exibio do jogo.

Para implementarmos a cmera 2D no XNA, criaremos uma classe chamada


Camera2D na qual iremos declarar trs variveis globais e dois mtodos (alm do
construtor), conforme abaixo:

public class Camera2D


{
//Posio da camera
public Vector2 posicao;
//Largura da janela
float largura;
//Altura da janela
float altura;

//Inicializa as variveis
//Recebe o objeto graphics para capturar a altura e largura da janela
public Camera2D(GraphicsDeviceManager graphics, Vector2 posicao_inicial)
{
this.largura = graphics.GraphicsDevice.Viewport.Width;
this.altura = graphics.GraphicsDevice.Viewport.Height;

this.posicao = posicao_inicial;

//Altera a posio da camera subtraindo metade da altura e larguda da


janela da posio do jogador
//Faz com que o jogador fique sempre nos pontos centrais da tela
public void Update(Vector2 posicaoJogador)
{

this.posicao.X = posicaoJogador.X - largura / 2f;


this.posicao.Y = posicaoJogador.Y - altura / 2f;

//Transforma a posio passada para ficar de acordo com a origem da


camera
//Recebe como parametro a posio original do objeto
public Vector2 Transform(Vector2 transformar)
{
return new Vector2(transformar.X - posicao.X,
transformar.Y - posicao.Y);
}

81
No mtodo Update ns alteramos a posio da cmera de forma que ela acompanhe
a movimentao do personagem, sempre o colocando ao centro da tela.

No mtodo Transform ns alteramos a posio recebida como parmetro


(necessrio utilizao em todos os objetos renderizveis do jogo), a qual se encontra
localizada em relao a posio zero original de nosso jogo, para com relao a posio zero de
nossa cmera.

Imagem ilustrando a posio da camra 2D e de seu objeto alvo (geralmente o jogador).

Ou seja, caso a cmera esteja posicionada nos pontos 200 e 200 de um determinado
cenrio teramos que subtrair da posio original do objeto a posio da nossa camra
(200,200), para ento obtermos o ponto em que ele deve ser desenhado na janela do jogo.

Para finalizarmos, devemos inserir algumas linhas na classe Game1, comeando pela
declarao das variveis globais:

Texture2D jogador;
Vector2 posicao;
Camera2D camera;

Inicializamos as variveis posicao e camera no mtodo Initialize:

//Declara a posio inicial no centro da tela


posicao = new Vector2((GraphicsDevice.Viewport.Width / 2),
(GraphicsDevice.Viewport.Height / 2));
camera = new Camera2D(graphics, Vector2.Zero);

No LoadContent carregamos uma textura:

jogador = Content.Load<Texture2D>("jogador");

82
No mtodo Update ns atualizamos a posio com base nas teclas pressionadas e
chamamos o mtodo Update da classe Camera2D:

KeyboardState teclado = Keyboard.GetState();

if (teclado.IsKeyDown(Keys.Right))
posicao.X += 5;
if (teclado.IsKeyDown(Keys.Left))
posicao.X -= 5;
if (teclado.IsKeyDown(Keys.Down))
posicao.Y += 5;
if (teclado.IsKeyDown(Keys.Up))
posicao.Y -= 5;

camera.Update(posicao);

E por fim, no mtodo Draw, ns utilizamos o mtodo Transform do objeto


camera para atualizar as posies de todos os objetos que formos renderizar no jogo, em
nosso caso o nosso jogador:

this.spriteBatch.Begin();

spriteBatch.Draw(jogador, camera.Transform(posicao), Color.White);

this.spriteBatch.End();

OBS: Para melhor visualizao do efeito, crie e desenhe um outro objeto na tela e verifique a
alterao de sua posio conforme mover o jogador.

83
Sistema de save e load

84
Salvar e carregar dados de um arquivo:

O sistema de save comumente utilizado em jogos, principalmente em jogos longos


que precisem mais que algumas poucas horas para serem concludos.

Criaremos aqui um sistema para salvar e carregar uma estrutura serializvel em um


arquivo externo com xml.

Comearemos por declarar uma estrutura que utilizaremos para armazenar as


informaes que achamos necessrios para nosso jogo. Utilizaremos uma struct e a
definiremos como serializvel:

[Serializable]
public struct SaveGameData
{
public Vector2 posicao;
public int score;
}

Feito isto vamos criar um mtodo para realizar o processo de escrita (salvar) de nossas
informaes. O mesmo utilizar um FileStream com o fluxo para um arquivo e salvar nossas
informaes de forma serializada:

public static void Save(SaveGameData data, string filename)


{
//Abre o arquivo, criando ele caso no exista
FileStream stream = File.Open(filename, FileMode.OpenOrCreate);

try
{
//Converte a varivel para xml e o salva em um arquivo
XmlSerializer serializer =
new XmlSerializer(typeof(SaveGameData));
serializer.Serialize(stream, data);
}
finally
{
//Encerra o arquivo
stream.Close();
}
}

Criaremos tambm um mtodo para carregar as informaes do arquivo (Load) em um


objeto do nosso tipo serializvel e o retornaremos:

public static SaveGameData Load(string filename)


{
SaveGameData data;

//Pode-se fazer a unio do nome do arquivo com um determinado caminho


para uma pasta
//string fullpath = Path.Combine(caminho,filename);
FileStream stream;

try
{
//Abre o arquivo em modo de leitura
stream = File.Open(filename, FileMode.Open, FileAccess.Read);

85
//Carrega as informaes do arquivo para a varivel
XmlSerializer serializer = new
XmlSerializer(typeof(SaveGameData));
data = (SaveGameData)serializer.Deserialize(stream);

//Encerra o arquivo
stream.Close();
}
catch
{
//Caso o arquivo no exista, retornar um Exception
throw new Exception("Arquivo no existente");
}

return (data);
}

Com os mtodos criados, declaramos as variveis globais que utilizaremos e no


mtodo Update definimos a verificao para chamada das funes:

// Variveis globais
SaveGameData data;
KeyboardState teclado;

// No mtodo Update
KeyboardState tecladoAnterior = teclado;
teclado = Keyboard.GetState();

if (teclado.IsKeyDown(Keys.S) && tecladoAnterior.IsKeyUp(Keys.S))


{
Save(data, "save.sav");
}

if (teclado.IsKeyDown(Keys.L) && tecladoAnterior.IsKeyUp(Keys.L))


{
data = Load("save.sav");
}

OBS: A varivel tecladoAnterior utilizada com o mtodo IsKeyUp para impedir que os
mtodos Save e Load sejam chamados repetidas vezes, caso o jogador mantenha a tecla
pressionada.

86
Fsica

87
Movimento Retilneo Uniforme (MRU):

No movimento retilneo uniforme(MRU), o vetor velocidade constante no decorrer


do tempo (no varia em mdulo, sentido ou direo), e portanto a acelerao nula. O corpo
ou ponto material se desloca distncias iguais em intervalos de tempo iguais, vale lembrar que,
uma vez que no se tem acelerao, sobre qualquer corpo ou ponto material em MRU a
resultante das foras aplicadas nula (primeira lei de Newton - Lei da Inrcia). Uma das
caractersticas dele que sua velocidade em qualquer instante igual velocidade mdia.
(Wikipedia.org)

Para gerarmos um MRU em nosso jogo precisamos aplicar a frmula fsica para
calcular a nova posio de nosso objeto (que aprendemos no ensino mdio). A frmula em
questo a seguinte:

S = So + v * t

Onde o S a nova posio em que o nosso objeto deve ser desenhado, So a


posio em que ele se encontra e v * t o espao a ser percorrido com uma velocidade em
um determinado espao de tempo.

Em nosso cdigo os valores de S sero do tipo Vector2 (posicao), o valor de v


ser do tipo float (uma velocidade continua definida por ns) e o t ser o tempo em
segundos, obtido pelo GameTime, desde a execuo do lao anterior (tempo e velocidade
que podem ser utilizados conforme suas preferncias).

Comeamos declarando as variveis globais que utilizaremos:

private Vector2 posicao;


private float velocidade;
private Texture2D imagem;

No mtodo Initialize inicializamos as nossas variveis posicao e velocidade:

posicao = Vector2.Zero;
velocidade = 200;

No LoadContent carregamos a textura para nossa varivel imagem:

imagem = Content.Load<Texture2D>(@"imagem");

No Update carregamos as informaes a respeito do tempo e aplicamos a frmula


para alterar a posio do jogador continuamente:

//Tempo passado desde a chamada anterior ao mtodo "Update"


float tempo = (float)(gameTime.ElapsedGameTime.TotalSeconds);
//S = So + v * t
posicao.X = posicao.X + velocidade * tempo;

Com os clculos acima fazemos com que o nosso objeto se mova uma distncia
aproximada de 3,2 pixels no eixo X a cada lao, pois a velocidade igual a 200 e o tempo entre
uma execuo e outra aproximadamente 1,6% de um segundo (0,016).

OBS: No esquea de utilizar o mtodo Draw para desenhar seu objeto.

88
Lanamento vertical com movimento retilneo uniformemente variado (MRUV):

O MRUV funciona de forma semelhante a um MRU, porm com a ao de uma


determinada acelerao (constante), conforme a frmula fsica abaixo:

s = so + (vo * t) + (a * t)/2

Onde a velocidade varia conforme a acelerao e o tempo, seguindo a seguinte


frmula (vo define a velocidade atual):

v = vo + a * t

Para criarmos uma simulao de um lanamento vertical precisamos primeiramente


gerar um Movimento Retilneo Uniformemente Retardado na vertical para cima, fazendo com
que ao a velocidade atingir um valor igual a zero inverta sua trajetria e se movimente para
baixo em um Movimento Retilneo Uniformemente Acelerado.

A frmula para clculo de um MRUV e a alterao da velocidade j nos garantem a


criao do efeito, basta apenas declararmos inicialmente uma velocidade negativa
(movimenta-se para cima no eixo da janela) com uma acelerao positiva.

Com o passar do tempo a velocidade vai se aproximando de zero at que em um


determinado momento passa a crescer positivamente, movimentando nosso objeto para
baixo.

Comeamos declarando as variveis que utilizaremos em nosso projeto:

Texture2D textura;
Vector2 posicao;
Vector2 velocidade;
float gravidade;

No mtodo Initialize inicializamos nossas variveis:

posicao = new Vector2(500, 500);


//Inicializa a gravidade com 400 pixels por segundo
gravidade = 400;
//Inicializa a velocidade no eixo Y (no utilizaremos o x)
velocidade = new Vector2(0, -600);

No LoadContent carregamos nossa textura:

textura = Content.Load<Texture2D>(@"imagem");

E no Update inserimos a lgica para utilizao das frmulas:

//Tempo passado desde a ultima atualizao


float tempo = (float)(gameTime.ElapsedGameTime.TotalSeconds);

//s = so + (vo*t) + (a*t)/2 (onde a acelerao ser a da gravidade)


posicao.Y = posicao.Y + (velocidade.Y * tempo) + (gravidade *
(float)Math.Pow(tempo, 2)) / 2;

//Atualiza a velocidade Y do objeto ( v = vo + a * t )


velocidade.Y = velocidade.Y + gravidade * tempo;

89
Lanamento oblquo (deslocamento parablico):

O movimento parablico caracterizado por dois movimentos simultneos em


direes perpendiculares, mais especificamente um deles um Movimento Retilneo Uniforme e
outro um Movimento Retilneo Uniformemente Variado. Dadas essas circunstncias o mvel
se desloca segundo uma parbola.

Tais circunstncias podem ser observadas num simples lanamento oblquo, onde,
desprezando o atrito do ar e demais efeitos o objeto se desloca verticalmente acelerado pela
ao da gravidade local, e, horizontalmente se desloca seguindo velocidade constante.
(Wikipedia.org)

Imagem de um lanamento oblquo (Fonte: http://www.ciencia-


cultura.com/Pagina_Fis/vestibular00/vestibular_CinematEscalar010.html)

A anlise direta do movimento algo extremamente complexo, envolve clculo de


derivadas e integrais, ento para facilitarmos a sua implementao utilizaremos o princpio da
simultaneidade de Galileu, o qual afirma o seguinte:

Se um corpo apresenta um movimento composto, cada um dos movimentos


componentes se realiza como se os demais no existissem e no mesmo intervalo de tempo.

Sendo assim utilizaremos um Movimento Retilneo Uniforme no eixo X e um


Movimento Retilneo Uniformemente Variado (retardado e acelerado) no eixo Y da
movimentao de nosso objeto.

Declaramos inicialmente as variveis globais que utilizaremos:

Texture2D imagem;
Vector2 posicao;
Vector2 velocidade;
float gravidade;

No mtodo Initialize inicializamos a posio, velocidade e gravidade:

posicao = new Vector2(0, 600);


gravidade = 600;
velocidade = new Vector2(300, -800);

No LoadContent carregamos a imagem que utilizaremos para a nossa textura:

imagem = Content.Load<Texture2D>(@"imagem");

90
E por fim adicionamos a lgica para utilizao das frmulas no mtodo Update:

//Tempo passado desde a ultima atualizao


float tempo = (float)gameTime.ElapsedGameTime.TotalSeconds;

//Atualiza a movimentao no eixo X (s = so + (v * t) MRU)


posicao.X = posicao.X + (velocidade.X * tempo);

//Atualiza a movimentao no eixo Y (s = so * t + (a * t)/2 MRUV)


posicao.Y = posicao.Y + (velocidade.Y * tempo) + (gravidade *
(float)Math.Pow(tempo, 2)) / 2;

//Atualiza a velocidade Y do objeto ( v = vo + a * t )


velocidade.Y = velocidade.Y + gravidade * tempo;

OBS: No esquea de utilizar o mtodo Draw para desenhar seu objeto.

OBS: Para utilizao de outros elementos fsicos, basta aplicar suas frmulas diretamente no
cdigo de seu jogo.

91

Das könnte Ihnen auch gefallen