(сначала я начну с общей идеи, за которой последует возможная реализация Qt)
Я не знаю, как хранятся спрайты WCII, но вы должны использовать лист спрайтов (создайте его самостоятельно, еслитребуется).Связанный с этим листом, у вас будет некоторое описание спрайта, содержащего как минимум список анимаций, а для каждой анимации - его идентификатор / имя, а также список фреймов.
Уровеньподробностей, которые вы вкладываете в описание фреймов этих анимаций, зависит от вас, но они должны содержать как минимум прямоугольник листа спрайта для отображения.
В качестве примера рассмотрим этот лист спрайта (явно не оптимизировано, но, к примеру, все нормально :)).Вот соответствующие описания анимаций (строки с 12 по 39).Все анимации не включены, но вы поймете эту идею.
Вы можете видеть, что анимация «в режиме ожидания» состоит из 3 кадров, субрецензии которых соответствуют первым 3 кадрам в листе спрайта.,В этом примере, связанном с подпунктом, есть еще 2 информации:
- длительность: как долго должен отображаться кадр, прежде чем перейти к следующему?
- источник: чтоточка привязки кадра?
Теперь, как бы вы реализовали это в Qt?
Формат файла описания анимации полностью зависит от вас, но я рекомендую некоторыеформат файла иерархии.Поскольку Qt предоставляет парсер XML, он может быть идеальным.Если вы привыкли повышать и предпочитаете легкий формат, такой как JSon, вы можете использовать boost.ptree для безразличного анализа файлов XML / JSon и иметь общий интерфейс для извлечения данных из них.
Для графического представления:вам нужно будет использовать несколько классов:
- QPixmap : из которого мы будем рисовать под прямоугольники, соответствующие кадрам анимации,
- a QTimer для обновления ваших спрайтов,
- вашего собственного класса отображения, скажем AnimatedSprite (производного от QGraphicsObject , вам потребуется поддержка сигнала / слотов),
- и класс поддержки, скажем TimerProxy (производный от QObject ).
Я начну с описания TimerProxy роль.Его роль - отправлять сообщения об обновлении времени.Это происходит потому, что объекты Qt Graphics не предоставляют каких-либо «синхронизированных» обновлений (т. Е. update (float dt) , где указывается dt - ваша любимая единица времени).Вы можете удивиться, почему мы используем прокси-класс для обработки времени.Это для ограничения количества активных QTimer ;если у вас есть один из них на AnimatedSprite , вы можете в конечном итоге иметь тонны активных таймеров, что явно является большим нет-нет.
Так что он выполняет 2 роли:
- во время создания сцены: все AnimatedSprite зарегистрируются в сигнале, который он предоставляет, назовем его updateTime (int msecs) ,
- во время выполнения сцены, он запуститQTimer, какой тайм-аут установлен на нужную вам степень детализации (вы можете использовать 16 мс для приблизительных 60 кадров в секунду)Сигнал QTimer timeout () будет связан с частными слотами, которые будут запускать updateTime (int msecs) , где msecs устанавливается на гранулярность таймера, которую вы установили ранее.
Теперь для ядра решения: AnimatedSprite .Этот класс имеет следующие роли:
- чтение и сохранение описания анимаций, которые ему понадобятся,
- запуск, обновление и остановка анимаций
- отрисовка активного спрайтакадр в QGraphicScene
При инициализации вы должны предоставить ему следующую информацию:
- экземпляр TimerProxy (принадлежит вашей сцене или классу, владеющему сценой).При наличии этого экземпляра вы просто подключаете сигнал TimerProxy :: updateTime (int) к частным слотам, которые будут обновлять текущую анимацию,
- QPixmap , содержащую таблицу спрайтов,
- и описания анимаций
Во время выполнения методы обновления будут выглядеть так:
- частный timeUpdated (int) слот, который проверит, должен ли быть обновлен кадр текущей анимации, и соответственно обновит его,
- метод открытых анимаций, такой как startAnim (const QString & animName) , который изменит текущую анимацию, сбросит счетчик истекшего времени и обновит текущий подчиненный объект, чтобы нарисовать, чтобы он соответствовал первому кадру новая анимация.
В слоте timeUpdated (int) вы хотите обновить истекшее время и проверить, должна ли анимация перейти к следующему кадру. Если это так, просто обновите указатель текущего кадра на новый кадр.
Наконец, для рендеринга вы просто переопределите QGraphicsItem :: paint (...) метод для рисования текущего подчиненного, который может выглядеть следующим образом:
void AnimatedSprite::paint(QPainter *painter,
const QStyleOptionGraphicsItem * /*option*/,
QWidget * /*widget*/)
{
painter->drawImage(mCurrentAnim.mCurrentFrame->mOrigin,
mSpriteSheet,
mCurrentAnim.mCurrentFrame->mSubRect);
}
Надеюсь, что это помогает (и не слишком много, вау: s)