Анимация все еще может быть идеально разделена между логикой и рендерингом. Абстрактное состояние данных анимации будет той информацией, которая необходима вашему графическому 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 ()) - он делает код длиннее и немного сложнее для чтения, а также медленнее - даже если об этом позаботится ваш компилятор, который оптимизирует множество подобных вещей.
Я могу развить это, если хотите, но у меня есть центральный рендер, которому говорят рисовать в цикле. Скорее, чем
У меня система больше похожа
Класс рендерера просто содержит список ссылок на рисуемые компоненты объектов. Они назначены в конструкторах для простоты.
Для вашего примера у меня будет класс GameBoard с несколькими тайлами. Каждая плитка, очевидно, знает свою позицию, и я предполагаю некоторую анимацию. Разложите это в некоторый класс Animation, которым владеет плитка, и пусть он передает ссылку на себя в класс Renderer. Там все разделено. Когда вы обновляете Tile, он вызывает Update для анимации .. или обновляет его сам. Когда
Renderer.Draw()
вызывается, он рисует анимацию.Не зависящая от кадра анимация не должна слишком сильно зависеть от цикла рисования.
источник
В последнее время я сам изучал парадигмы, поэтому, если этот ответ неполон, я уверен, что кто-то добавит к нему.
Методология, которая кажется наиболее подходящей для игрового дизайна, заключается в отделении логики от вывода на экран.
В большинстве случаев вы хотели бы использовать многопоточный подход, если вы не знакомы с этой темой, это вопрос сам по себе, вот учебник для начинающих вики . По сути, вы хотите, чтобы игровая логика выполнялась в одном потоке, блокируя переменные, к которым она должна обращаться для обеспечения целостности данных. Если ваш логический цикл невероятно быстр (супер-мега-анимированный 3d-понг?), Вы можете попытаться зафиксировать частоту, которую выполняет цикл, засыпая поток на небольшие промежутки времени (на этом форуме для игровых циклов физики было предложено 120 Гц). Одновременно другой поток перерисовывает экран (60 Гц было предложено в других разделах) с обновленными переменными, снова запрашивая блокировку переменных до того, как он получит к ним доступ.
В этом случае анимация или переходы и т. Д. Попадают в поток рисования, но вы должны сигнализировать через какой-то флаг (возможно, глобальную переменную состояния), что поток логики игры не должен делать ничего (или делать что-то другое ...). новые параметры карты возможно).
Как только вы осознаете параллелизм, все остальное становится вполне понятным. Если у вас нет опыта работы с параллелизмом, я настоятельно рекомендую вам написать несколько простых тестовых программ, чтобы вы могли понять, как происходит процесс.
Надеюсь это поможет :)
[edit] В системах, которые не поддерживают многопоточность, анимация все еще может переходить в цикл рисования, но вы хотите установить состояние таким образом, чтобы сигнализировать логике, что происходит что-то другое, и не продолжать обрабатывать текущий уровень / карта / и т.д ...
источник