L'animazione può ancora essere perfettamente divisa tra logica e rendering. Lo stato astratto dei dati di animazione sarebbe l'informazione necessaria affinché l'API grafica esegua il rendering dell'animazione.
Nei giochi 2D, ad esempio, quella potrebbe essere un'area rettangolare che segna l'area che mostra la parte corrente del tuo foglio sprite che deve essere disegnata (quando hai un foglio composto da diciamo 30 disegni 80x80 contenenti le varie fasi del tuo personaggio saltare, sedersi, muoversi ecc.). Può anche essere qualsiasi tipo di dati che non è necessario per il rendering, ma forse per gestire gli stati di animazione stessi, come il tempo rimanente fino alla scadenza del passaggio di animazione corrente o il nome dell'animazione ("walking", "standing" ecc.) Tutto ciò può essere rappresentato nel modo desiderato. Questa è la parte della logica.
Nella parte di rendering, lo fai come al solito, ottieni quel rettangolo dal tuo modello e usa il tuo renderer per fare effettivamente le chiamate all'API grafica.
Nel codice (usando la sintassi C ++ qui):
class Sprite //Model
{
private:
Rectangle subrect;
Vector2f position;
//etc.
public:
Rectangle GetSubrect()
{
return subrect;
}
//etc.
};
class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
AnimationController animation_controller;
//etc.
public:
void Update()
{
animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
}
//etc.
};
Questi sono i dati. Il tuo renderer prenderà quei dati e li disegnerà. Poiché sia gli Sprite normali che quelli animati sono disegnati allo stesso modo, puoi usare la polimorfia qui!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
TMV:
Ho trovato un altro esempio. Supponi di avere un gioco di ruolo. Il tuo modello che rappresenta la mappa del mondo, ad esempio, probabilmente avrebbe bisogno di memorizzare la posizione del personaggio nel mondo come coordinate della tessera sulla mappa. Tuttavia, quando sposti il personaggio, camminano di alcuni pixel alla volta fino al quadrato successivo. Memorizzi questa posizione "tra riquadri" in un oggetto animazione? Come si aggiorna il modello quando il personaggio è finalmente "arrivato" alla coordinata successiva della tessera sulla mappa?
La mappa del mondo non conosce direttamente la posizione dei giocatori (non ha un Vector2f o qualcosa del genere che memorizza direttamente la posizione dei giocatori =, invece ha un riferimento diretto all'oggetto giocatore stesso, che a sua volta deriva da AnimatedSprite così puoi passarlo facilmente al renderer e ottenere tutti i dati necessari da esso.
In generale, tuttavia, la tua tilemap non dovrebbe essere in grado di fare proprio tutto: avrei una classe "TileMap" che si occupa della gestione di tutte le tessere, e forse fa anche il rilevamento delle collisioni tra oggetti che gli passo e le tessere sulla mappa. Quindi, avrei un'altra classe "RPGMap", o comunque ti piacerebbe chiamarla, che ha sia un riferimento alla tua piastrella che il riferimento al giocatore ed effettua le effettive chiamate Update () al tuo giocatore e al tuo tilemap.
Il modo in cui vuoi aggiornare il modello quando il giocatore si muove dipende da cosa vuoi fare.
Il tuo giocatore può muoversi tra le tessere in modo indipendente (stile Zelda)? Gestisci semplicemente l'input e sposta il giocatore di conseguenza in ogni frame. Oppure vuoi che il giocatore prema "a destra" e il tuo personaggio sposta automaticamente una tessera a destra? Lascia che la tua classe RPGMap interpoli la posizione dei giocatori fino a quando non arriva a destinazione e nel frattempo blocca tutte le operazioni di immissione dei tasti di movimento.
In entrambi i casi, se vuoi semplificarti, tutti i tuoi modelli avranno metodi Update () se in realtà hanno bisogno di un po 'di logica per aggiornarsi (invece di cambiare solo i valori delle variabili) - Non dai via il controller nel modello MVC in quel modo, basta spostare il codice da "un passo sopra" (il controller) verso il basso al modello, e tutto ciò che il controller fa è chiamare questo metodo Update () del modello (Il controller nel nostro caso sarebbe la RPGMap). Puoi ancora scambiare facilmente il codice della logica: puoi semplicemente modificare direttamente il codice della classe o se hai bisogno di un comportamento completamente diverso, puoi semplicemente derivare dalla tua classe del modello e sovrascrivere solo il metodo Update ().
Questo approccio riduce molto le chiamate ai metodi e cose del genere - che erano uno dei principali svantaggi del modello MVC puro (finisci per chiamare GetThis () GetThat () molto molto spesso) - rende il codice più lungo e un un po 'più difficile da leggere e anche più lento - anche se potrebbe essere curato dal tuo compilatore che ottimizza molte cose del genere.