Анимация все еще может быть идеально разделена между логикой и рендерингом. Абстрактное состояние данных анимации будет той информацией, которая необходима вашему графическому API для визуализации анимации.
Например, в 2D-играх это может быть прямоугольная область, которая отмечает область, отображающую текущую часть вашего спрайт-листа, которую нужно нарисовать (если у вас есть лист, состоящий из 30 рисунков 80x80, содержащих различные шаги вашего персонажа) прыгать, садиться, двигаться и т. д.). Это также могут быть любые данные, которые вам не нужны для рендеринга, но, возможно, для управления самими состояниями анимации, например, время, оставшееся до истечения текущего шага анимации, или название анимации («ходьба», «стоя»). и т.д.) Все это можно представить так, как вы хотите. Это часть логики.
В части рендеринга вы просто делаете это как обычно, получаете этот прямоугольник из вашей модели и используете рендерер для фактических вызовов графического API.
В коде (здесь используется синтаксис 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.
};
Это данные. Ваш рендерер возьмет эти данные и нарисует их. Поскольку и обычные, и анимированные спрайты нарисованы одинаково, вы можете использовать полиморф здесь!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
ВТМ:
Я придумал другой пример. Скажем, у вас есть РПГ. Например, вашей модели, представляющей карту мира, вероятно, потребуется сохранить положение персонажа в мире в виде координат плитки на карте. Однако когда вы перемещаете персонажа, они переходят на несколько пикселей за раз к следующему квадрату. Вы сохраняете это положение «между плитками» в анимационном объекте? Как вы обновляете модель, когда персонаж, наконец, «прибыл» в следующую координату плитки на карте?
Карта мира не знает непосредственно о позиции игроков (у нее нет Vector2f или чего-то подобного, которая напрямую хранит позицию игроков =, вместо этого она имеет прямую ссылку на сам объект игрока, который, в свою очередь, происходит от AnimatedSprite так что вы можете легко передать его рендереру и получить от него все необходимые данные.
В целом, однако, ваша карта тайлов не должна быть в состоянии сделать только все - у меня был бы класс "TileMap", который заботится об управлении всеми тайлами, и, возможно, он также обнаруживает столкновения между объектами, которые я передаю ему и плитки на карте. Затем у меня был бы другой класс "RPGMap", или как бы вы его не называли, который имеет как ссылку на вашу карту тайлов, так и ссылку на игрока и выполняет фактические вызовы Update () для вашего игрока и вашего tilemap.
То, как вы хотите обновить модель, когда игрок движется, зависит от того, что вы хотите сделать.
Разрешено ли вашему игроку самостоятельно перемещаться между тайлами (стиль Zelda)? Просто обработайте ввод и переместите игрока соответственно каждый кадр. Или вы хотите, чтобы игрок нажимал «вправо», и ваш персонаж автоматически перемещал одну плитку вправо? Позвольте вашему классу RPGMap интерполировать положение игроков, пока он не прибудет в пункт назначения, и тем временем заблокировать всю обработку ввода с помощью клавиш движения.
В любом случае, если вы хотите облегчить себе задачу, все ваши модели будут иметь методы Update (), если им действительно нужна некоторая логика для обновления (вместо простого изменения значений переменных) - вы не отдаете контроллер таким образом, в шаблоне MVC вы просто перемещаете код из «одного шага выше» (контроллера) в модель, и все, что делает контроллер, - это вызывает метод Update () модели (в нашем случае контроллер будет RPGMap). Вы все еще можете легко поменять код логики - вы можете просто напрямую изменить код класса или, если вам нужно совершенно другое поведение, вы можете просто наследовать от своего класса модели и только переопределить метод Update ().
Такой подход значительно сокращает вызовы методов и тому подобное - что раньше было одним из главных недостатков чистого шаблона MVC (в конечном итоге вы часто вызываете GetThis () GetThat ()) - он делает код длиннее и немного сложнее для чтения, а также медленнее - даже если об этом позаботится ваш компилятор, который оптимизирует множество подобных вещей.