Подтвердить что ты не робот

Qt: Эффективно обрабатывать QGraphicsItems, которые имеют "много pixmaps"? (РТС)

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

FYI, я не нацелен ни на что из уровня AAA, я просто пытаюсь внедрить некоторые методы машинного обучения. Таким образом, я выбрал отказоустойчивые ISO-системы Warcraft II, бесстыдно похвалил некоторые графики, и я столкнулся с первыми проблемами.

http://img263.imageshack.us/img263/1480/footman.png

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

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

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

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

4b9b3361

Ответ 1

(Сначала я начну с общей идеи, после нее будет реализована возможная реализация Qt)

Я не знаю, как хранятся спрайты WCII, но вы должны использовать лист спрайтов (при необходимости создайте его самостоятельно). Связанный с этим листом, вы получите некоторое описание спрайта, содержащего, по крайней мере, список анимаций, а также для каждой анимации, идентификатора/имени, а также список кадров.

Уровень детализации, который вы вводите при описании этих кадров анимации, зависит от вас, но должен содержать, по крайней мере, прямоугольник отображаемого листа спрайтов.

В качестве примера рассмотрим этот лист спрайтов (явно не оптимизированный, но, например, это нормально:)). Здесь приведено описание описаний анимаций (строки с 12 по 39). Все анимации не включены, но вы получите эту идею.

Вы можете видеть, что анимация "без дела" состоит из 3 кадров, которые соответствуют прямым трем первым трем кадрам на листе спрайта. Связанный с субреклом, в этом примере есть еще две информации:

  • продолжительность: как долго должен отображаться кадр, прежде чем перейти к следующему?
  • origin: какова опорная точка кадра?

Теперь, как бы вы перешли к реализации этого в Qt?

Формат файла описания анимации полностью зависит от вас, но я рекомендую использовать формат файла иерархии. Поскольку Qt предоставляет синтаксический анализатор XML, он может быть идеальным. Если вы привыкли повышать и предпочитаете легкий формат, такой как JSon, вы можете использовать boost.ptree для равномерного анализа файлов XML/JSon и иметь общий интерфейс для извлечения данных из них.

Для графического представления вам нужно будет использовать несколько классов:

  • QPixmap: из которого мы будем рисовать суб-прямоугольники, соответствующие рамкам анимации,
  • QTimer для обновления ваших спрайтов,
  • ваш собственный класс отображения, скажем, AnimatedSprite (полученный из QGraphicsObject, вам понадобится поддержка сигналов/слотов),
  • и класс поддержки, например TimerProxy (полученный из QObject).

Я начну с описания роли TimerProxy. Это роль отправки сообщений об обновлении времени. Это здесь, потому что объекты Qt Graphics не предоставляют никакого "временного" обновления (т.е. Обновление (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, содержащий спрайт,
  • и описания анимаций

Во время выполнения методы обновления будут выглядеть так:

  • личный временной интервал (интервал), который будет проверять, обновляется ли текущий кадр анимации, и обновлять его соответствующим образом,
  • общедоступный метод анимации, такой как startAnim (const QString & animName), который изменит текущую анимацию, reset счетчик прошедшего времени и обновит текущую субрекцию для рисования в соответствии с первым кадром новой анимации.

В слоте timeUpdated (int) вы хотите обновить прошедшее время и проверить, должно ли оно сделать анимацию для перехода к следующему кадру. Если это так, просто обновите текущий указатель кадра на новый кадр.

Наконец, для рендеринга вы просто переопределяете метод QGraphicsItem:: paint (...), чтобы нарисовать текущий пример, который может выглядеть так:

void AnimatedSprite::paint(QPainter *painter,
                           const QStyleOptionGraphicsItem * /*option*/,
                           QWidget * /*widget*/)
{
    painter->drawImage(mCurrentAnim.mCurrentFrame->mOrigin,
                       mSpriteSheet,
                       mCurrentAnim.mCurrentFrame->mSubRect);
}

Надеюсь, что помогает (и не слишком большой, ничего себе: s)