[Tutorial] Renderizando e movendo Sprites animadas com XNA

Introdução

Este tutorial é uma continuação do tutorial [Tutorial] Renderizando objetos e movendo-os com XNA [Via Classe]. Caso você não tenha lido ou não tenha o projeto anterior, sugiro que vá até o tutorial e o faça.

O que iremos fazer nesta etapa do tutorial é criar uma função para animação da sprite. Um técnica muito utilizada para animação de sprites são os sprite batchs, ou lote de sprites, onde temos uma sequência de sprites na mesma imagem, e com um retângulo, fazemos a leitura de trechos desta imagem de acordo com tempo e tamanho desejado.

Variáveis

Para aplicar esta técnica, precisaremos adicionar mais algumas variáveis, sendo elas:

1
2
3
4
5
6
7
8
9
10
11
///
/// Temporizador.
///
float timer = 0f;
int frameCount = 2;
float interval = 200f;
int currentFrame = 0;
///
/// Retângulo de destino. Trecho da sprite que será exibido.
///
Rectangle sourceRectangle;

Para cada variável, precisaremos criar uma property, com exceção das variáveis timer e sourceRectangle, que não precisaram ser acessadas externamente. Sendo assim, temos as seguintes propriedades criadas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
///
/// Quantidade de frames que a sprite possui.
///
public int FrameCount
{
    get { return this.frameCount; }
    set { this.frameCount = value; }
}
///
/// Intervalo entre a exibição de um frame e outro.
///
public float Interval
{
    get { return this.interval; }
    set { this.interval = value; }
}
///
/// Frame atual da sprite.
///
public int CurrentFrame
{
    get { return this.currentFrame; }
    set { this.currentFrame = value; }
}

Os comentários das propriedades informam o que elas significam/fazem.

Sobrescrevendo o método Update()

Agora o que precisamos fazer é mover o sourceRectangle de acordo com o número de frames que informamos que a sprite tem.

Para isto, utilizaremos o temporizador.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
///
/// Atualiza a sprite na tela
///
///
Tempo de jogo
public void Update(GameTime pGameTime)
{
    timer += (float)pGameTime.ElapsedGameTime.TotalMilliseconds;
    if (timer > interval)
    {
        currentFrame++;
        if (currentFrame > frameCount - 1)
        {
            currentFrame = 0;
        }
        timer = 0f;
    }
    sourceRectangle = new Rectangle(currentFrame * (int)size.X, 0, (int)size.X, (int)size.Y);
}

Basicamente, temos o o tempo percorrido em milisegundos no jogo adicionado ao nosso temportizador. Em seguida, verificamos se este tempo é maior que o intervalo que informamos para transição de cada frame da sprite.

Caso o temporizador seja maior que o intervalor, adicionamos um frame ao nosso frameCount, que significa que andamos 1 frame na animação. Em seguida, verificamos se o frame atual é maior que o total de frames, e caso positivo, retornamos ao frame 0. O tempo então é zerado.

Por fim, criamos um novo retângulo com as informações que arrecadamos durante o tempo passado e os frames percorridos. Note que temos um cálculo (currentFrame * (int)size.X) onde teremos a o início da leitura do frame após o valor indicado. Se o frame atual for 0, ele lê a largura estabelecida a partir do ponto 0. Se o frame atual for 1, ele lê a largura estabelecida a partir de 1x a largura estabelecida.

Sendo assim, temos a animação da nossa sprite.

Sobrescrevendo o método Draw()

Para quem acompanhou o artigo anterior, no método Draw() ao invéz de informarmos o sourceRactangle, informamos NULL, pois naquele momento não havia necessidade do mesmo. Agora como temos o nosso sourceRectangle definido, precisamos alterar o parâmetro do método Draw() de NULL para sourceReactangle.

1
2
3
4
5
6
7
8
9
10
11
12
///
/// Desenha a sprite na tela
///
///
Lote de Sprites
public void Draw(SpriteBatch spriteBatch)
{
    spriteBatch.Draw(texture,
        new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y),
        sourceRectangle,
        Color.White, rotation, new Vector2((int)size.X / 2, (int)size.Y / 2), effect, 0);
}

Utilizando a classe Sprite

Agora que temos os métodos alterados, podemos utilizar a classe. No arquivo Game1.cs, atualize o método LoadContent() deixando como abaixo:

1
2
3
4
5
6
7
8
9
10
protected override void LoadContent()
{
    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
 
    // TODO: use this.Content to load your game content here
    minhaSprite = new Sprite(Content.Load(SPRITE_ASSET), new Vector2(64f, 64f), new Vector2(100f, 100f), 2, 0, SpriteEffects.None);
    minhaSprite.FrameCount = 2;
    minhaSprite.Interval = 250f;
}

O que fizemos foi adicionar o FrameCount e o Interval da sprite.

Então, no método Update(), após a chamada ao método UpdatePacmanMovement(ks), chame o método Update() da sprite minhaSprite, como mostrado abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
 
    // TODO: Add your update logic here
    KeyboardState ks = new KeyboardState();
    ks = Keyboard.GetState();
    UpdatePacmanMovement(ks);
 
    minhaSprite.Update(gameTime);
    base.Update(gameTime);
}

Deste modo, temos então a Sprite animada na tela.

Download do Projeto

http://www.insidedotnet.com.br/andrebaltieri/Artigos/2009/11/xna_sprite_class/XNASprites_20091121.zip

Espero que tenham gostado. No próximo tutorial veremos como fazer a colisão desta sprite.

Até breve!

Leave a Reply