(Nota: molto belli sono quelli rippati dai vecchi giochi per il SNES, che potete trovare su alcuni siti, i cui link metterò alla fine del post).
Vediamo un piccolo esempio su come possono essere utilizzati.
Il codice sarà allegato alla fine del post!
La strutturazione del codice per come l'ho pensata io è la seguente:
- TextureCollection: un catalogo contenente tutte le texture2D caricate nel gioco, accedibili tramite un id unico
- SpriteSheetCollection: un catalogo contenente gli SpriteSheet definiti per il gioco, anche questi accedibili tramite un id unico
- AnimationDescCollection: un catalogo contenente la descrizione delle animazioni definite per il gioco, anche queste accedibili trami un id unico
Le collezioni sopra definite sono definite come statiche, in modo che possano essere accedibili da ogni parte del codice.
VI sono poi altre classi:
- SpriteSheet: rappresenta un'immagine divisa in frame. Contiene l'identificatore della texture all'interno della TextureCollection, il numero di frame sull'asse X e sull'asse Y, oltre che un metodo per calcolare il rettangolo relativo al frammento di immagine che si vuole utilizzare.
-AnimationDesc: contiene una collezione di frame che specificano l'animazione, oltre a dire se l'animazione deve essere ripetuta all'infinito o meno.
- Frame: speficifica quale SpriteSheet utilizzare e quale è l'id del frammento da utilizzare (NOTA: I frammenti contentuni all'interno di uno spriteSheet sono numerati da 0 al numero massimo -1, partendo dall'angolo in alto a sinistra e procedendo da destra verso sinistra, e dall'alto verso il basso).
- Animation: è l'oggetto che implementa l'animazione vera e propria. Contiene l'id della descrizione dell'animazione che si sta utilizzando, il frame corrente (cioè dove siamo arrivati a riprodurre l'animazione), il tempo passato dall'ultimo aggiornamento (in modo da poter avanzare il frame corrente al momento giusto), e la posizione dell'animazione nella vista del gioco.
Infine arriviamo al componente principale:
- AnimationManager: è il gameComponent che si preoccupa di gestire ed animare gli oggetti Animation che gli vengono dati in pasto.
Ecco il codice per le classi principali:
Show∇
1: /// <summary>
2: /// rappresenta un singolo frame dell'animazione
3: /// Si caratterizza per l'id dello spriteSheet e per il frammento di immagine utilizzato dal frame
4: /// </summary>
5: public class Frame
6: {
7: /// <summary>
8: /// Id dello spriteSheet che deve essere presente nella spriteSheetCollection
9: /// </summary>
10: public long IdSpriteSheet { get; private set; }
11: /// <summary>
12: /// Id del frammento dell'immagine che si utilizza per il particolare frame
13: /// </summary>
14: public int IdFragment { get; private set; }
15:
16: /// <summary>
17: /// Costruisce un frame per l'animazione
18: /// </summary>
19: /// <param name="idSpriteSheet">Id di riferimento per lo spriteSheet che deve essere presente nella spriteSheetCollection</param>
20: /// <param name="idFragment">Id del frammento dell'immagine che si utilizza per il particolare frame</param>
21: public Frame(long idSpriteSheet, int idFragment)
22: {
23: this.IdSpriteSheet = idSpriteSheet;
24: this.IdFragment = idFragment;
25: }
26: }
27:
28: /// <summary>
29: /// Descrive come è fatta un'animazione
30: /// </summary>
31: public class AnimationDesc
32: {
33: /// <summary>
34: /// Lista dei frame che compongono l'animazione
35: /// </summary>
36: public List<Frame> FrameList { get; set; }
37:
38: /// <summary>
39: /// Vale true se l'animazione deve ripetersi
40: /// False altrimenti
41: /// </summary>
42: public bool Loop { get; set; }
43:
44: /// <summary>
45: /// Crea la descrizione di un'animazione che non si ripete dopo la fine
46: /// </summary>
47: public AnimationDesc()
48: : this(false)
49: {
50: }
51:
52: /// <summary>
53: /// Crea la descrizione di un'animazione permettendo di configurare la ripetizione
54: /// </summary>
55: /// <param name="loop">True se si vuol far ripetere l'animazione, false altrimenti</param>
56: public AnimationDesc(bool loop)
57: {
58: FrameList = new List<Frame>();
59: Loop = loop;
60: }
61: }
62:
63: /// <summary>
64: /// Definisce un'animazione da visualizzare a schermo dall'AnimationManager
65: /// </summary>
66: public class Animation
67: {
68: /// <summary>
69: /// Posizione (nel range dello schermo) in cui viene visualizzata l'animazione
70: /// </summary>
71: public Vector2 Position { get; set; }
72:
73: /// <summary>
74: /// Frame corrente dell'animazione
75: /// </summary>
76: public int CurrentFrame { get; set; }
77:
78: /// <summary>
79: /// Id dell'AnimationDesc utilizzata
80: /// </summary>
81: public long IdAnimationDesc { get; private set; }
82:
83: /// <summary>
84: /// Tempo passato dall'ultimo cambio di frame
85: /// </summary>
86: public int ElapsedTime { get; set; }
87:
88: /// <summary>
89: /// Crea una nuova Animazione con riferimento all'AnimationDesc con id passato. Creata in posizione (0,0)
90: /// </summary>
91: /// <param name="idAnimation">Id dell'AnimationDesc presente nella AnimationsDescCollection</param>
92: public Animation(long idAnimationDesc)
93: : this(idAnimationDesc, Vector2.Zero)
94: {
95: }
96:
97: /// <summary>
98: /// Crea una nuova Animazione con riferimento all'AnimationDesc con id passato.
99: /// </summary>
100: /// <param name="idAnimation">Id dell'AnimationDesc presente nella AnimationsDescCollection</param>
101: /// <param name="position">Posizione dell'Animazione</param>
102: public Animation(long idAnimationDesc, Vector2 position)
103: {
104: this.IdAnimationDesc = idAnimationDesc;
105: this.Position = position;
106: ElapsedTime = 0;
107: }
108: }
109:
110: /// <summary>
111: /// Component per il rendering della Animation a schermo
112: /// </summary>
113: public class AnimationManager : Microsoft.Xna.Framework.DrawableGameComponent
114: {
115: //spritebatch su cui disegnare
116: SpriteBatch spriteBatch = null;
117:
118: /// <summary>
119: /// lista delle animazioni da eseguire. Un'animazione viene automaticamente rimossa al termine della sua animazione
120: /// </summary>
121: public List<Animation> AnimationList { get; set; }
122:
123: public AnimationManager(Game game)
124: : base(game)
125: {
126: AnimationList = new List<Animation>();
127: }
128:
129:
130: protected override void LoadContent()
131: {
132: spriteBatch = new SpriteBatch(Game.GraphicsDevice);
133: base.LoadContent();
134: }
135:
136: public override void Initialize()
137: {
138: base.Initialize();
139: }
140:
141: public override void Update(GameTime gameTime)
142: {
143: int delta = gameTime.ElapsedGameTime.Milliseconds;
144:
145: AnimationDesc desc = null;
146: for (int i = AnimationList.Count - 1; i >= 0; i--)
147: {
148: desc = AnimationsDescCollection.AnimationCollection[AnimationList[i].IdAnimationDesc];
149: if (!desc.Loop && AnimationList[i].CurrentFrame == desc.FrameList.Count - 1)
150: AnimationList.RemoveAt(i);
151: else if (desc.Loop && AnimationList[i].CurrentFrame == desc.FrameList.Count - 1)
152: AnimationList[i].CurrentFrame = 0;
153: }
154:
155: for (int i = 0; i < AnimationList.Count; i++)
156: {
157: desc = AnimationsDescCollection.AnimationCollection[AnimationList[i].IdAnimationDesc];
158: AnimationList[i].ElapsedTime += delta;
159: if (AnimationList[i].ElapsedTime >= 40)
160: {
161: AnimationList[i].ElapsedTime -= 40;
162: AnimationList[i].CurrentFrame++;
163: }
164: }
165:
166: base.Update(gameTime);
167: }
168:
169: public override void Draw(GameTime gameTime)
170: {
171: spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
172: AnimationDesc desc = null;
173: for (int i = 0; i < AnimationList.Count; i++)
174: {
175: desc = AnimationsDescCollection.AnimationCollection[AnimationList[i].IdAnimationDesc];
176: Frame current = desc.FrameList[AnimationList[i].CurrentFrame];
177: SpriteSheet spriteSheet = SpriteSheetCollection.SpritesSheetCollection[current.IdSpriteSheet];
178:
179: Texture2D img = TextColl.TextureCollection[spriteSheet.IdTexture];
180: Rectangle sRec = spriteSheet.GetRectangle(current.IdFragment);
181: spriteBatch.Draw(img, AnimationList[i].Position - new Vector2(sRec.Width / 2, sRec.Height / 2),
182: sRec, Color.White);
183: }
184: spriteBatch.End();
185:
186: base.Draw(gameTime);
187: }
188: }
HideΔ
E questo è il codice per il Game:
Show∇
1: public class Game1 : Microsoft.Xna.Framework.Game
2: {
3: GraphicsDeviceManager graphics;
4: SpriteBatch spriteBatch;
5:
6: SpriteFont font;
7:
8: AnimationManager animMng;
9:
10: //stato del mouse al ciclo precedente
11: MouseState lastMS;
12:
13: public Game1()
14: {
15: graphics = new GraphicsDeviceManager(this);
16: Content.RootDirectory = "Content";
17:
18: graphics.PreferredBackBufferWidth = 800;
19: graphics.PreferredBackBufferHeight = 600;
20:
21: TextColl.Initialize();
22: SpriteSheetCollection.Initialize();
23: AnimationsDescCollection.Initialize();
24:
25: Components.Add((animMng = new AnimationManager(this)));
26:
27: IsMouseVisible = true;
28: }
29:
30: protected override void Initialize()
31: {
32: base.Initialize();
33: }
34:
35: protected override void LoadContent()
36: {
37: spriteBatch = new SpriteBatch(GraphicsDevice);
38:
39: font = Content.Load<SpriteFont>("font");
40:
41: TextColl.TextureCollection.Add(0, Content.Load<Texture2D>("prova"));
42:
43: SpriteSheet ss = new SpriteSheet(0, 5, 2);
44: SpriteSheetCollection.SpritesSheetCollection.Add(0, ss);
45:
46: AnimationDesc ad = new AnimationDesc();
47: ad.FrameList.Add(new Frame(0, 0));
48: ad.FrameList.Add(new Frame(0, 1));
49: ad.FrameList.Add(new Frame(0, 2));
50: ad.FrameList.Add(new Frame(0, 3));
51: ad.FrameList.Add(new Frame(0, 4));
52: AnimationsDescCollection.AnimationCollection.Add(0, ad);
53:
54: lastMS = Mouse.GetState();
55: }
56:
57: protected override void UnloadContent()
58: {
59: }
60:
61: protected override void Update(GameTime gameTime)
62: {
63: if (Keyboard.GetState().IsKeyDown(Keys.Escape))
64: this.Exit();
65:
66: if (lastMS.LeftButton == ButtonState.Released && Mouse.GetState().LeftButton == ButtonState.Pressed)
67: {
68: MouseState ms = Mouse.GetState();
69: Animation anim = new Animation(0, new Vector2(ms.X, ms.Y));
70: animMng.AnimationList.Add(anim);
71: }
72: lastMS = Mouse.GetState();
73: base.Update(gameTime);
74: }
75:
76: protected override void Draw(GameTime gameTime)
77: {
78: GraphicsDevice.Clear(Color.CornflowerBlue);
79:
80: base.Draw(gameTime);
81: }
82: }
HideΔ
Come potete vedere non è nulla di trascendentale o di così complicato come poteva sembrare.
Vi allego anche la soluzione per Xna4 (se il link non funziona contattatemi):
Soluzione Visual Sutdio
Ed ecco un buonissimo sito con tantissimi spriteSheet!
Sprite Database
Alla prossima!
Odino
Continua a leggere!