Qt: Эффективно обрабатываете QGraphicsItems, которые имеют "много растровых изображений"? (РТС)
В настоящее время я создаю небольшой движок Real Time Strategy 2D. И мне интересно, как справиться со многими постоянно меняющимися спрайтами, которые в конечном итоге засорят мой экран.
К вашему сведению, я не стремлюсь ни к какому уровню ААА, я просто пытаюсь реализовать некоторые методы машинного обучения. Таким образом, я выбрал бессмысленные ISO-образы Warcraft II, взял бесстыдную графику и столкнулся с первыми проблемами.
http://img263.imageshack.us/img263/1480/footman.png
Как вы можете видеть выше, даже простой лакей Warcraft II получил около 50 спрайтов для своей анимации. Что очень много. И это очень часто меняет спрайты. (Черная линия просто проверяла, был ли мой альфа-канал правильным)
Итак, последний вопрос: как эффективно реализовать объект QGraphicsObject, который постоянно меняется? Как эффективно реализовать QGraphicsItem, который неоднократно меняет свой внешний вид?
Я просто перегружаю paint()
метод QGraphicsPixmapItem и продолжать изменять Pixmap, используемый на экране? Это вызовет некоторое "заикание"? Я слышал, что иногда разумно / можно создать все растровые изображения, спрятать их и дублировать, когда это необходимо. (Копирование обходится дешевле, чем другие операции) Есть ли еще какая-нибудь разумная идея?
Спасибо за любой вклад! (учебник по двигателям RTS, сложностям и т. д.)
1 ответ
(Сначала я начну с общей идеи, возможная реализация Qt последует)
Я не знаю, как хранятся спрайты WCII, но вы должны использовать лист спрайтов (создайте его самостоятельно, если это необходимо). С этим листом у вас будет некоторое описание спрайта, содержащего как минимум список анимаций, а для каждой анимации - его идентификатор / имя, а также список фреймов.
Уровень детализации, который вы вкладываете в описание кадров этой анимации, зависит от вас, но должен содержать как минимум прямоугольник листа спрайта для отображения.
В качестве примера взгляните на этот лист спрайтов (явно не оптимизированный, но для примера, все в порядке:)). Вот соответствующие описания анимации (строки с 12 по 39). Все анимации не включены, но вы поймете идею.
Вы можете видеть, что анимация "в режиме ожидания" состоит из 3 кадров, суб-которые соответствуют первым 3 кадрам в листе спрайта. В этом образце есть еще две информации, связанные с подправкой:
- длительность: как долго должен отображаться кадр, прежде чем переходить к следующему?
- Происхождение: какова точка привязки кадра?
Теперь, как бы вы реализовали это в Qt?
Формат файла описания анимации полностью зависит от вас, но я рекомендую использовать некоторый формат файла иерархии. Поскольку Qt предоставляет синтаксический анализатор XML, он может быть идеальным. Если вы привыкли повышать и предпочитаете легкий формат, такой как JSon, вы можете использовать boost.ptree, чтобы безразлично анализировать файлы XML/JSon и иметь общий интерфейс для извлечения данных из них.
Для графического представления вам нужно будет использовать несколько классов:
- QPixmap: из которого мы будем рисовать под прямоугольники, соответствующие кадрам анимации,
- QTimer для обновления ваших спрайтов,
- ваш собственный класс отображения, скажем, AnimatedSprite (производный от QGraphicsObject, вам понадобится поддержка сигнала / слотов),
- и класс поддержки, скажем, TimerProxy (производный от QObject).
Я начну с описания роли TimerProxy. Его роль - отправлять сообщения об обновлении времени. Это происходит потому, что объекты Qt Graphics не предоставляют какого-либо "временного" обновления (т. Е. Update(float dt), где dt указывается как ваша любимая единица времени). Вы можете удивиться, почему мы используем прокси-класс для обработки времени. Это ограничивает количество активных QTimer; если у вас есть один из них на AnimatedSprite, вы можете в конечном итоге иметь множество активных таймеров, что, безусловно, является большим нет-нет.
Таким образом, он выполняет 2 роли:
- во время создания сцены: все AnimatedSprite зарегистрируются в сигнале, который он предоставляет, назовем его updateTime(int msecs),
- во время выполнения сцены он запускает QTimer, время ожидания которого установлено на нужную вам степень детализации (вы можете продолжить с 16 мс для приблизительных 60 кадров в секунду). Тайм- аут сигнала QTimer () будет связан с частными слотами, которые будут запускать 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);
}
Надеюсь, что это помогает (и не слишком большой, вау: с)