domingo, 27 de março de 2011

XNA: Manipulação de elementos 2D – Parte 1

O framework XNA dispõem de simples mecanismos para manipulação de objetos 2D e interface com usuário através de controladores como teclados e gamepads.

Este tutorial será divido em dois post, no primeiro serão apresentadas as nomenclaturas utilizadas para referenciar elementos gráficos em um jogo, o sistema de coordenadas 2D e como imagens podem ser adicionadas na tela. Na segunda parte será mostrado como imagens podem ser movidas, como podem ser realizadas leituras de comandos do usuário por meio dispositivos de entrada e princípios básicos de detecção de colisão de objetos.


Gráficos 2D

Gráficos 2D são objetos representados em duas dimensões, X e Y (largura e comprimento). Geometricamente definidos como ventores que podem ser manipulados e/ou modificados através de transformações geométricas, como translação, rotação e escala.


Termos utilizados para designar elementos gráficos

Sprite: uma imagem 2D que pode ser manipulada independentemente de outras imagens que compõem um senário.

Textura: uma imagem 2D utilizada na composição de um modelo de 3 dimensões, que pode ser visualizada em qualquer ponto de vista e varia dependendo da posição do modelo e do ângulo da câmera utilizada para projeção.

Billboard: é uma textura utilizada geralmente em objetos planos,  que não muda conforme o ângulo da câmera utilizada na projeção. São utilizadas para compor elementos como árvores, muros e ruas em senários.

Background: imagem de fundo utilizada em um senário, geralmente os sprites são adicionados sobre esta imagem. Alguns backgrounds possuem movimentos, para dar a impressão de ambientes 3D, por exemplo enquanto o jogador move seu personagem pelo senário, montanhas desse senário podem mover-se para dar a impressão de distância.


Sistema de coordenadas 2D

O sistema de coordenadas utilizado para posicionamento de objetos em duas dimensões na computação gráfica é um pouco diferente do sistema de coordenadas cartesiano, apresentado na geometria. A diferença se encontra no ponto inicial que passa a ser o canto superior esquerdo da tela, como mostrado na imagem abaixo:


Os pontos nestas coordenas são representados por pixel, portanto uma tela de resolução 800x600 pixeis possui 800 pontos na reta X e 600 pontos na reta Y. Os objetos são posicionados sobre estes pontos e seu tamanho (largura e altura) também são representados neste sistema.


Criação de Sprites

Criaremos uma classe para a posterior criação de objetos a partir desta. Para nosso exemplo o objeto representará uma bola, que terá como atributos textura, posição e tamanho.

Para representar as propriedades de nosso sprite precisaremos de objetos do tipo Texture2D e Vector2.

Texture2D: armazena a imagem que representa o sprite  e possui métodos auxiliar para o trabalho com texturas.

Vector2: possui propriedades para representar tamanhos ou posições de objetos utilizando o sistema de coordenadas apresentado anteriormente.

Nossa classe então ficará da seguinte forma:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace WindowsGame3
{
    class Ball
    {
        private Texture2D textura;
        private Vector2 tamanho;
        private Vector2 posicao;

        public Texture2D Textura { get { return textura; } set { textura = value; } }
        public Vector2 Tamanho { get { return tamanho; } set { tamanho = value; } }
        public Vector2 Posicao { get { return posicao; } set { posicao = value; } }

        public Ball(Texture2D textura, Vector2 tamanho, Vector2 posicao)
        {
            Textura = textura;
            Tamanho = tamanho;
            Posicao = posicao;
        }

    }
}

Agora vamos adicionar uma imagem para nossa textura, utilizaremos o Content Pipeline, um recurso do XNA que converte arquivos de diferentes formatos para um formato que pode ser trabalhado em nosso projeto. A figura abaixo mostra o os procedimentos para a importação.



Após após adicionarmos a imagem poderemos visualizar suas propriedades, neste exemplo nos preocuparemos apenas com a propriedade Asset Name que será o nome utilizado para referenciarmos esta imagem dentro de nosso código fonte.


Agora voltaremos a nossa classe de sprite para criarmos o método Draw que será responsável por desenhar nossa imagem na tela, o método ficará da seguinte maneira:
public void Draw(SpriteBatch spriteBatch)
{
    spriteBatch.Draw(textura, posicao, Color.White);
}

A classe SpriteBatch já foi apresentada em um post anterior, seu método Draw recebe três parâmetros, a textura a ser desenhada, a posição desta textura e uma cor para compor a imagem, em nosso caso utilizamos a cor branca que não deixa o efeito perceptível, caso utilizássemos outra cor o perceberíamos.

Vamos criar agora adicionar nosso sprite na classe principal do projeto, por padrão chamada de Game1.

Adicionamos o sprite como um atributo da classe:

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    Ball ball; // Nosso sprite

O inicializamos no local apropriado, o método LoadContent, passamos uma referência para a imagem da textura que importamos anteriormente, tamanho e posicção de nosso sprite. Por enquanto o tamanho não é utilizado, pois o sprite é desenhado de acordo com o tamanho da imagem.

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);

    // Carregando nosso sprite
    ball = new Ball(Content.Load("ball"), new Vector2(60, 60), new Vector2(0, 0));
}

E como mencionando no post anterior, devemos liberar os recursos que utilizamos quando não são mais necessários, vamos liberar a textura no método UnloadContent.

protected override void UnloadContent()
{
    ball.Textura.Dispose();
}

Por fim vamos desenhar nosso sprite no tela, utilizando o método Draw da classe principal para chamar o método Draw de nosso sprite. Utilizamos os métodos Begin e End para indicarmos onde devemos alocar os recursos para desenhar sprites na tela e onde estes recursos devem ser fechados.

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

    spriteBatch.Begin();
    ball.Draw(spriteBatch);
    spriteBatch.End();

    base.Draw(gameTime);
}

Executando nosso projeto devemos obter o seguinte resultado:


Fim da parte 1

Nenhum comentário:

Postar um comentário