Qt: Эффективно обрабатываете элементы QGraphicsItems, которые имеют «много растровых изображений»? (РТС) - PullRequest
12 голосов
/ 04 апреля 2011

Я сейчас создаю небольшой движок Real Time Strategy 2D.И Интересно, как справиться со многими постоянно меняющимися спрайтами, которые в конечном итоге засорят мой экран .

К вашему сведению, я не стремлюсь ни к какому уровню ААА, я просто пытаюсь реализовать некоторые методы машинного обучения.Таким образом, я выбрал оставленные из Warcraft II ISO-образы, бесстыдно взял некоторые графические элементы и выпал на первые выпуски.Пехотинец Warcraft II получил около 50 спрайтов для своей анимации.Который много.И это очень часто меняет спрайты.(Черная линия просто проверяла правильность моего альфа-канала)

Таким образом, последний вопрос: Как эффективно реализовать объект QGraphicsObject, который продолжает изменяться?Как эффективно реализовать QGraphicsItem, который несколько раз меняет свой внешний вид?

Просто перегрузить метод paint() QGraphicsPixmapItem и продолжать изменять Pixmap, используемый на экране?Это вызовет некоторое «заикание»?Я слышал, что иногда разумно / можно создать все растровые изображения, спрятать их все и дублировать, когда это необходимо.(Копирование обходится дешевле, чем другие операции). Есть ли какая-нибудь другая разумная идея?

Спасибо за любой вклад!(учебник по движкам RTS, сложностям и т. д.)

1 Ответ

15 голосов
/ 22 апреля 2011

(сначала я начну с общей идеи, за которой последует возможная реализация 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)

...