Animację nadal można idealnie podzielić na logikę i rendering. Stan abstrakcyjnych danych animacji byłby informacją niezbędną do graficznego interfejsu API do renderowania animacji.
Na przykład w grach 2D może to być obszar prostokąta, który oznacza obszar, który wyświetla bieżącą część arkusza duszka, który należy narysować (gdy masz arkusz składający się z powiedzmy 30 rysunków 80x80 zawierających różne kroki twojej postaci skakanie, siadanie, poruszanie się itp.). Mogą to być również wszelkiego rodzaju dane, których nie potrzebujesz do renderowania, ale być może do zarządzania samymi stanami animacji, na przykład czas pozostały do wygaśnięcia bieżącego kroku animacji lub nazwa animacji („chodzenie”, „stój” itp.) Wszystko to można przedstawić w dowolny sposób. To część logiki.
W części dotyczącej renderowania po prostu wykonaj to tak, jak zwykle, pobierz prostokąt z modelu i użyj renderera, aby faktycznie wywoływać interfejs API grafiki.
W kodzie (używając tutaj składni C ++):
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.
};
To są dane. Twój renderer weźmie te dane i narysuje je. Ponieważ zarówno normalne, jak i animowane duszki są rysowane w ten sam sposób, możesz tutaj użyć polimorfii!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
TMV:
Wymyśliłem inny przykład. Powiedz, że masz RPG. Na przykład model reprezentujący mapę świata prawdopodobnie musiałby przechowywać pozycję postaci w świecie jako współrzędne kafelków na mapie. Jednak gdy poruszasz postacią, idą one o kilka pikseli na raz do następnego kwadratu. Czy przechowujesz tę pozycję „między płytkami” w obiekcie animacji? Jak zaktualizować model, kiedy postać wreszcie „dotrze” do następnej współrzędnej kafelka na mapie?
Mapa świata nie wie bezpośrednio o pozycji graczy (nie ma Vector2f lub czegoś takiego, który bezpośrednio przechowuje pozycję gracza =, zamiast tego ma bezpośrednie odniesienie do samego obiektu gracza, co z kolei pochodzi od AnimatedSprite dzięki czemu możesz łatwo przekazać go do renderera i uzyskać z niego wszystkie niezbędne dane.
Ogólnie rzecz biorąc, twoja mapa tilemap nie powinna być w stanie zrobić wszystkiego - miałbym klasę „TileMap”, która zajmuje się zarządzaniem wszystkimi kafelkami, a może także wykrywa kolizję między obiektami, które przekazuję jej i kafelki na mapie. Następnie miałbym inną klasę „RPGMap” lub jakkolwiek chcesz ją nazwać, która zawiera zarówno odniesienie do twojej mapy til, jak i odniesienie do odtwarzacza oraz wykonuje rzeczywiste wywołania Update () do twojego odtwarzacza i do twojego tilemap.
To, jak chcesz zaktualizować model, gdy gracz się porusza, zależy od tego, co chcesz zrobić.
Czy twój gracz może poruszać się pomiędzy płytkami niezależnie (styl Zelda)? Wystarczy poradzić sobie z wejściem i odpowiednio przesunąć odtwarzacz w każdej klatce. A może chcesz, aby gracz nacisnął „w prawo”, a twoja postać automatycznie przesuwa jeden kafelek w prawo? Pozwól swojej klasie RPGMap interpolować pozycję graczy, dopóki nie dotrze do miejsca docelowego, a tymczasem zablokuj całą obsługę klawiszy ruchu.
Tak czy inaczej, jeśli chcesz ułatwić sobie życie, wszystkie twoje modele będą miały metody Update (), jeśli faktycznie potrzebują trochę logiki, aby się zaktualizować (zamiast po prostu zmieniać wartości zmiennych) - Nie oddajesz kontrolera w ten sposób we wzorcu MVC wystarczy przenieść kod z „jednego kroku powyżej” (kontrolera) do modelu, a wszystko, co robi kontroler, to wywołać metodę Update () modelu (w naszym przypadku kontroler byłby RPGMap). Nadal możesz łatwo zamienić kod logiczny - możesz po prostu bezpośrednio zmienić kod klasy lub jeśli potrzebujesz zupełnie innego zachowania, możesz po prostu wyprowadzić się z klasy modelu i zastąpić tylko metodę Update ().
Takie podejście znacznie redukuje wywołania metod i takie rzeczy - co kiedyś było jedną z głównych wad czystego wzorca MVC (w końcu bardzo często wywoływano funkcję GetThis () GetThat ()) - powoduje to, że kod jest zarówno dłuższy, jak i trochę trudniejsze do odczytania, a także wolniejsze - nawet jeśli może to zająć się twój kompilator, który optymalizuje wiele takich rzeczy.