3

Делаю кроссплатформенную игру с использованием Qt (из Линукса). Именно Qt выбрал, потому что в перспективе понадобится графический интерфейс для всяких редакторов, а также он предустановлен практически во все Linux'ы.

Пробовал использовать стандартный цикл QApplication::exec() и сделать таймер с нулевым интервалом. Но всё это сильно дёргалось, хотя в полноэкранном режиме было ещё нормально. Вот код:

QApplication app;
QTimer t;
t.setSingleShot(false);
t.start(0);
app.exec();

Потом попробовал сделать свой бесконечный цикл, вызывая processEvents, а после него обновляя игру. Стало получше, но всё равно иногда дёргается в оконном режиме. Выдаёт FPS от 50 до 70, хотя в Windows версии на WinAPI было больше 200. Код:

while(running)
{
    QApplication::processEvents();
    engine->Step();
}

Код, который я пробовал на WinAPI, когда ещё не перешёл на Linux:

while(GetMessage(...))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    engine->Step();
}

Вроде то же самое, что и на Qt, но на Qt тормозит, а на WinAPI нет. Хотя эксперимент был не совсем чистым, так FPS на WinAPI я замерял на Windows, а FPS на Qt я замерял в Linux.

devoln
  • 5,441

2 Answers2

3

Обычно пишут для каждой платформы пачку платформозависимых файлов, которые обрабатывают ввод, собирают информацию о системе, инициализируют отдельные потоки для звука и приводят это все к форме принятой в движке. Затем где-нибудь в одном из файлов для каждой системы вставляют:

int main(int argc, char *argv[])
{
    core.init();
    while(1) //Специальная олимпиада открыта вдруг for(;;) быстрее
    {
        core.drawFrame();
    }
    return 0; //should never get here
}

При этом сам кросс-платформенный код отрисовки инкапсулирован в core. Сам этот метод может быть достаточно сложен. Там могут быть счетчики FPS, работа с вводом , обработка AI, столкновений, логики и лишь в конце рендеринг/очистка памяти. При этом рендеринг осуществляется разными техниками для разных объектов/материалов в несколько проходов с разными шейдерами в разные буферы.

По поводу редакторов для игры, очень сомнительно что ему реально нужна кросс-платформенность и непонятно что он будет делать. В лучшем случае комбинировать меши выгруженные из нормального 3D редактора в уровень. Но если игра маленькая это можно сделать и в любом другом 3D редакторе. А файл с описанием уровня/активов просто автоматически сгенерировать скриптом.

По поводу core.init() это тоже зависит от реализации. Если игра маленькая она просто загружает все что можно загрузить. Если большая то грузится основной GUI, шейдеры, метафайл с описанием остальных активов, пользовательский кэш, загрузочный экран/заставка и.т.д. Геометрия и текстуры уровня же подгружаются отдельно по сигналу от GUI из обработчика ввода внутри drawFrame если пользователь решил таки загрузить очередной уровнеь.

По поводу скачков FPS то непонятно каким образом вы его считаете. Взвешенное(weighted) скользящее среднее по 20ке последних тиков вроде неплохой метод. http://en.wikipedia.org/wiki/Moving_average

P.S.Код Doom 3, на который я ориентируюсь сделан таким образом, если смотреть на то как он справляется с покрытием нескольких платформ.

igumnov
  • 7,806
  • Ответ совсем не по теме. Я не спрашивал, как реализовать кроссплатформенность. Кроссплатформенная игра уже есть, но она тормозит из-за того, что стандартный цикл обработки сообщений Qt не предназначен для игр. Вопрос в том, как сделать свой цикл? – devoln Feb 10 '13 at 13:04
  • А зачем вам вообще Qt? – igumnov Feb 10 '13 at 13:06
  • Я к тому что проще вручную отладить пару тысяч строк кода которые создают окно с игрой для MacOS, Linux, Windows чем пытаться использовать "стандартный цикл обработки сообщений Qt не предназначенный для игр" – igumnov Feb 10 '13 at 13:24
  • Кроссплатформенное создание окна, клавиатура (правда с ней отдельная проблема в другом моём вопросе на Хешкоде), мышь, MessageBox для вывода ошибок. Неохота делать всё это вручную через XLib. SDL или glut тоже не хочу таскать вместе с игрой. А Qt удобен, есть практически во всех Linux"ах при установке.

    И почему это редактор не зависит от движка? Редактор уровней будет рендерить уровень. Он должен уметь управлять объектами и так далее. Короче, нужно объединить интерфейс и движок в одно целое.

    – devoln Feb 10 '13 at 13:26
  • чем пытаться использовать "стандартный цикл обработки сообщений Qt не предназначенный для игр"

    Я и спрашиваю, как сделать свой средствами Qt? Там наверняка всего будет 10 строк вместо пары тысяч, к тому же сразу под все существующие платформы.

    – devoln Feb 10 '13 at 13:29
  • 1
    Еще непонятно почему у вас обработка сообщений находится внутри петли отдельно?
    while(GetMessage(...)) //Паршивая идея
    while(running) //running. Там что где-то есть глобальная переменная running = true ?
    
    

    Не проще ли создать класс UserInput обрабатывающий все сообщения от всех систем во внутреннюю модель ввода сделанную специально для игры(один буфер клавиатуры и курсора мыши). После чего условно компилировать его через макросы и вставить его сразу в engine->step(). Тогда главная петля станет значительно проще.

    – igumnov Feb 10 '13 at 13:35
  • Ну тогда вы будете постоянно обрекать себя на все существующие Qt проблемы. Она слишком огромна для компьютерной игры. Сейчас померил Doom3 если выкинуть оттуда враперы для OpenGL и сторонние библиотеки там около 15000 строк платформозависимого кода. – igumnov Feb 10 '13 at 13:46
  • Не понял. Что значит внутри петли отдельно?

    //Паршивая идея

    Так все WinAPI приложения делают. Что в этом паршивого?

    Да есть переменная, которую в любой момент можно установить в false, и после обработки всех приложений цикл прервётся. В WinAPI случае при получении сообщения WM_QUIT GetMessage вернёт 0 и цикл тоже прервётся.

    Какая мне разница, какого размера Qt, если она встроена во все дистрибутивы? Поэтому я его использую как удобный LinuxAPI. А для редактора по-любому понадобится. Пусть лучше всё будет на одном коде.

    И что? У меня весь движок занимает меньше 15000 строк.

    – devoln Feb 24 '13 at 10:35
0

Оказалось, что дело не в главном цикле Qt. Попробовал перекомпилировать под Windows, где у меня уже был реализован WinAPI-вариант, там тот же самый низкий FPS. Сделал через XLib на Linux'е - вроде чуть плавнее, но FPS вроде не увеличился. Единственное что изменилось при портировании и не было проверено - это компилятор MSVC сменился на g++. Я пробовал также llvm-g++ и clang++, но на производительности никак не отражается. Да и дебаг от релиза по производительности ничем почти не отличается, а в студии сильно отличалось. Вообще не могу понять, что же такое произошло во время портирования. Были подозрения на некоторые изменения, но я попробовал их повторить в старой версии движка в студии и они никак не повлияли на высокий FPS.

Так что буду учиться пользоваться Linux'овским профайлером. Ещё попробую создать отладочный контекст OpenGL и задействовать GL_AMD_debug_output. Может он что-нибудь скажет. Если нет, то попробую скомпилировать проект в студии.

devoln
  • 5,441
  • Попробуйте для начала уменьшить работу, которая делается в основном цикле. Скажем, убрать рендеринг объектов, но считать FPS'ы. А то смены библиотек и компиляторов похожи на пляски с бубном, так Вы проблему не локализуете – Михаил М Aug 10 '13 at 09:36
  • Пробовал уже удалять объекты. В итоге в пустой сцене FPS еле-еле достиг того уровня, который был в полной сцене до портирования. Так что вообще какие-то непонятные результаты. – devoln Aug 10 '13 at 10:21