In questo script analizzeremo l'implementazione di una semplice classe per la gestione delle animazioni con le spritesheets.
Una spritesheet non è altro che una semplice immagine con all'interno in sequenza tutti gli sprites che comporranno la nostra animazione; per semplicità nel nostro esempio supporremo che abbiano tutti stessa dimensione.
L'idea di base è quella di avere una sorta di "cursore", cioè un rettangolo che identifica lo sprite da usare per il frame corrente, e di farlo scorrere ad intervalli fissi.
Il primo passaggio è procurarci gli assets, nel nostro esempio useremo un'immagine con gli sprites di un'esplosione:
Adesso procediamo con la creazione del progetto: da Visual Studio selezioniamo New Project e creiamo un Windows Phone Game (4.0). Una volta creata la soluzione, aggiungiamo al Content Project la nostra spritesheet:
Adesso aggiungiamo una nuova classe, che chiameremo AnimatedSprite. Iniziamo con la lista dei membri interni della classe:
public class AnimatedSprite { #region Members private Texture2D _spritesheet = null; private float _frameInterval = 50f; private Point _spriteSize = Point.Zero; private Vector2 _spriteCenter = Vector2.Zero; private Rectangle _sourceRect; private int _numFrames = 0; private float _timer = 0f; private int _currentFramePosX = 0; private int _currentFramePosY = 0; private int _currentFrame = 0; private bool _isAnimationEnded = false; private bool _isLooping = true; #endregion Members
Li analizzeremo passo passo. Procediamo con il costruttore:
public AnimatedSprite(Texture2D spriteSheet, Point size, float interval) { if (null == spriteSheet) throw new ArgumentNullException("Texture2D"); _spritesheet = spriteSheet; // la nostra spritesheet _frameInterval = interval; // l'intervallo in millisecondi tra i frames _spriteSize = size; // questa sarà la dimensione di un singolo sprite // memorizziamo il punto centrale di un singolo sprite _spriteCenter.X = (float)_spriteSize.X * 0.5f; _spriteCenter.Y = (float)_spriteSize.Y * 0.5f; // e calcoliamo il numero totale dei frames _numFrames = (_spritesheet.Width / _spriteSize.X) * (_spritesheet.Height / _spriteSize.Y); }
Ed ora eccoci al metodo più importante, che ci consente l'aggiornamento dell'animazione:
public void Update(GameTime gameTime) { // Controlliamo che il numero di frames sia sufficiente // e che l'animazione non sia terminata if (_numFrames < 2 || (_isAnimationEnded && !_isLooping)) return; float deltaTime = (float)gameTime.ElapsedGameTime.TotalMilliseconds; // aggiorniamo il contatore dei millisecondi _timer += deltaTime; // e se abbiamo superato la soglia di aggiornamento if (_timer > _frameInterval) { // passiamo al frame successivo _currentFrame++; // siamo giunti al termine? _isAnimationEnded = _currentFrame > _numFrames; // se l'animazione è in loop ed è terminata resettiamo tutto if (_isLooping && _isAnimationEnded) { _currentFramePosX = 0; _currentFramePosY = 0; _currentFrame = 0; _isAnimationEnded = false; } else if (!_isAnimationEnded) { // altrimenti aggiorniamo la posizione del "cursore" sulla spritesheet _currentFramePosX++; int x = _currentFramePosX * _spriteSize.X; if (x >= _spritesheet.Width) { // controlliamo che non esca fuori dai bordi della spritesheet x = 0; _currentFramePosX = 0; _currentFramePosY++; } int y = _currentFramePosY * _spriteSize.Y; _sourceRect = new Rectangle(x, y, _spriteSize.X, _spriteSize.Y); } // infine azzeriamo il timer e siamo pronti per un nuovo frame _timer = 0f; } }
Infine, ecco il metodo Render, molto semplice, che non fa altro che disegnare lo sprite corrente usando le impostazioni di posizione, rotazione e scaling passate nella firma:
public void Draw(SpriteBatch spriteBatch, Vector2 position, float angle, Vector2 scale, Color color) { spriteBatch.Draw(_spritesheet, position, _sourceRect, color, angle, _spriteCenter, scale, SpriteEffects.None, 0); }
Per poter visualizzare l'animazione, non dobbiamo far altro che creare un'istanza della classe all'interno del metodo LoadContent del nostro Game, aggiornarla nel metodo Update ed infine procedere al rendering nel metodo Draw:
_animSprite = new AnimatedSprite(Content.Load<Texture2D>(@"explosion"), new Point(64, 64), 50f); _animSprite.Update(gameTime); spriteBatch.Begin(); _animSprite.Draw(spriteBatch, _position, 0f, _scale, Color.White); spriteBatch.End();
Ed è tutto!
In allegato troverete un progetto di test creato con Visual Studio 2010 per Windows Phone ed XNA 4, ma la soluzione presentata può essere usata senza problemi anche in contesto Windows o Xbox 360.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Usare una container image come runner di GitHub Actions
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Eseguire query verso tipi non mappati in Entity Framework Core
Triggerare una pipeline su un altro repository di Azure DevOps
Criptare la comunicazione con mTLS in Azure Container Apps
Disabilitare automaticamente un workflow di GitHub
Cambiare la chiave di partizionamento di Azure Cosmos DB
Usare lo spread operator con i collection initializer in C#
Evitare il flickering dei componenti nel prerender di Blazor 8
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Assegnare un valore di default a un parametro di una lambda in C#
Routing statico e PreRendering in una Blazor Web App