Olá pessoal, tudo certo?!
Depois de alguns dias falando sobre fractais, html5 e muito JavaScript, volto a escrever código em C# seguindo esta série sobre XNA. Hoje, nada de HLSL, só C#!
Neste post, mostro uma técnica simples de animação (sem intevenção do usuário). Embora esteja utilizando modelos 3D, a mesma técnica pode ser aplicada a sprites.
O código-fonte completo dessa série está disponível em https://github.com/ElemarJR/VamosAprenderXNA
O que vamos fazer?
No post de hoje, uso como ponto de partida o código da parte 07. O objetivo é fazer com que nossa “nave ande de um lado para o outro” repetidamente. Observe:
Fica muito mais fácil de entender o que está ocorrendo se você baixar os fontes e rodar o programa.
A classe ModelAnimation
Para realizar a animação, bastava “cuidar da atualização” das propriedades Position e Rotation do modelo 3d (a nave). Por isso, precisamos de um objeto que mantenha:
- as posições inicial e final que desejamos respeitar em nossa animação;
- as rotações inicial e final que desejamos respeitar em nossa animação;
- o tempo total de animação (em um período, desconsiderando looping e reverse);
- se desejamos que a animação ocorra em “looping” (sem parar);
- se desejamos executar uma animação oposta (autoreverse).
Esse mesmo objeto deve “calcular” posição e rotação atualizadas conforme o tempo avança. Eis minha implementação:
public class ModelAnimation { public Vector3 StartPosition { get; private set; } public Vector3 StartRotation { get; private set; } public Vector3 EndPosition { get; private set; } public Vector3 EndRotation { get; private set; } public Vector3 Position { get; private set; } public Vector3 Rotation { get; private set; } public TimeSpan Duration { get; private set; } public bool Looping { get; private set; } public bool AutoReverse { get; private set; } TimeSpan elapsedTime = TimeSpan.FromSeconds(0); public ModelAnimation( Vector3 startposition, Vector3 startrotation, Vector3 endposition, Vector3 endrotation, TimeSpan duration, bool looping, bool autoreverse ) { this.StartPosition = startposition; this.StartRotation = startrotation; this.EndPosition = endposition; this.EndRotation = endrotation; this.Duration = duration; this.Looping = looping; this.AutoReverse = autoreverse; } bool reversing = false; public void Update(TimeSpan elapsed) { this.elapsedTime += elapsed; var amount = (float)this.elapsedTime.TotalSeconds / (float)Duration.TotalSeconds; if (this.Looping) { if (amount > 1) { this.elapsedTime = TimeSpan.FromSeconds(0); reversing = !reversing; while (amount > 1) amount--; } } else amount = MathHelper.Clamp(amount, 0f, 1f); if (this.AutoReverse && reversing) amount = 1 - amount; Position = Vector3.Lerp(this.StartPosition, this.EndPosition, amount); Rotation = Vector3.Lerp(this.StartRotation, this.EndRotation, amount); } }
Como você pode perceber, a “mágica” acontece dentro do método update. Perceba:
- calculo uma “quantidade” de animação considerando a razão entre o tempo total transcorrido e o duração programada;
- estando em looping , verifico a conclusão da animação (um ciclo). Em caso positivo, ligo um flag que “inverte” o amount causando a “inversão” de sentido;
- Utilizo a função utilitária Lerp para determinar a interpolação linear entre os vetores de início e fim, usando o amount calculado.
Configurando e executando a animação
Configurar a animação é muito simples. Basta criar uma instância de nossa classe no game, informando os parâmetros adequadamente. Observe:
animation = new ModelAnimation( spaceship.Position, Vector3.Zero, spaceship.Position + new Vector3(0, 0, 2600), new Vector3(0, MathHelper.Pi, 0), TimeSpan.FromSeconds(5), true, true );
Como pode perceber, determino que :
- a posição e rotação inicial devem ser iguais ao objeto que estamos animando (nossa nave);
- a posição final está a 2600 unidades na frente da posição atual;
- a rotação final será 90 graus no eixo y;
- o tempo de um ciclo será de cinco segundos;
- a animação ocorrerá em looping;
- haverá AutoRevese;
Para garantir que a animação ocorra, falta apenas atualizar o método Update. Perceba:
protected override void Update(GameTime gameTime) { base.Update(gameTime); animation.Update(gameTime.ElapsedGameTime); spaceship.Position = animation.Position; spaceship.Rotation = animation.Rotation; }
Pronto!
Era isso.
Vinicius
05/11/2011
Cara, parabéns, seus códigos são muito bem organizados e profissionais.
É difícil encontrar material bom de XNA, assim bem explicado. Com certeza esses tutoriais vão me ajudar muito a aprender XNA.
Sucesso.
elemarjr
05/11/2011
Obrigado pelo feedback. Bons estudos.