В 3D-играх обычно используется так называемый интерфейс – некие изображения и надписи, отображаемые в 2D-режиме поверх сцены. Также в играх должны отображаться такие сущности, как меню, статистика, настройки и т. д. Для всего этого в DDD предназначены инфопанели. Фактически, инфопанели – это контейнеры для мешей-прямоугольников (которые, соответственно, называются элементами инфопанелей) с нулевой координатой Z, которые отображаются поверх всей остальной сцены.
В этом уроке мы добавим одну простую инфопанель к сцене с персонажем Кленовый лист, построенной в двух предыдущих уроках.
Откройте модуль scena.d и добавьте импорт модулей, связанных с инфопанелями:
import ddd.infopanel, ddd.text_infopaneli;
добавьте эти строки в конец функции сцена():
Инфопанель инфопанель1 = менеджер.получить_инфопанель("инфопанель18"); if (!(инфопанель1 is null)) { менеджер.добавить_инфопанель(инфопанель1); }
Если всё сделано правильно, то экран должен сейчас выглядеть вот так:
Надеюсь, эти панельки выглядят не слишком уродливыми ☺. Вот, собственно и всё, что нужно сделать для добавления инфопанели. Можно заканчивать урок.
…
Ах, да! Надо же ещё и пояснить, что тут происходит…
Откройте в текстовом редакторе файл инфопанель18.ip из подкаталога ресурсы/инфопанели. Это обычный json-файл, ну а число 18 в его названии добавлено просто по номеру текущего урока.
{ "имя": "инфопанель18", "тип_координат": "Относительно", "координаты": [0.0, 0.8, 1.0, 0.2], "элементы": [ { "имя": "левая_панель", "контейнер": false, "координаты": [0.0, 0.0, 0.5, 1.0], "текстура": "рамка", "координаты_текстуры": [0.0, 0.0, 1.0, 0.1377] }, { "имя": "правая_панель", "контейнер": false, "координаты": [0.5, 0.0, 0.5, 1.0], "текстура": "рамка", "координаты_текстуры": [0.0, 0.1621, 1.0, 0.2969] } ] }
Как вы можете тут видель, у инфопанели должны быть прописаны несколько полей:
имя – желательно, чтобы оно совпадало с именем файла без расширения.
тип_координат – текстовое поле, может быть одним из 4-х значений: "Абсолютно", "Относительно", "ОтносительноX", "ОтносительноY".
координаты – координата X левой границы, координата Y нижней границы, ширина, высота.
элементы – массив объектов, из которых эта инфопанель и состоит.
В нашем случае мы используем тип координат "Относительно", т. е. все значения (и у самой инфопанели, и у её элементов) задаются в диапазоне от 0 до 1. Координаты считаются от левого нижнего угла экрана. По поводу остальных значений поля тип_координат смотрите документацию модуля ddd.infopanel.
Поля элемента (объекта класса ЭлементИнфопанели):
имя – должно быть уникальным в пределах инфопанели.
контейнер – если это поле равно true, то этот элемент сам может содержать дочерние элементы в поле элементы.
координаты – так же, как и для всей инфопанели, считаются относительно родительской инфопанели/элемента.
Текстура, координаты_текстуры – если эти поля заданы, то с элементом будет связан меш с натянутой на него соответствующей текстурой. координаты_текстуры задаются задаются следующим образом: x,y левого нижнего угла, затем x,y правого верхнего угла текстуры.
Дочерние элементы (в нашем случае их нет) всегда отображаются поверх родительского элемента, отсчитываются в относительных координатах и не могут выходить за его границы.
В продолжении урока добавим на нашу инфопанель статистику – отображение частоты кадров.
Добавьте в модуль константу и несколько глобальных переменных:
const float ИНТЕРВАЛ_ОБНОВЛЕНИЯ_СТАТИСТИКИ = 0.333; // 3 раза в секунду ТекстовыйЭлементИнфопанели текст_ип; float длительность_показа_статистики = 0; int кадров_в_интервале = 0;
Класс ТекстовыйЭлементИнфопанели является подклассом от ЭлементИнфопанели и позволяет размещать на инфопанели текст, чем мы и воспользуемся.
Добавьте внутрь оператора if, добавленного нами на втором шаге, такие строки:
текст_ип = new ТекстовыйЭлементИнфопанели("текст", false, 0.7,0.7,0.3,0.1); if (!(текст_ип is null)) { текст_ип.задать_текст(менеджер, "частота кадров: 0.0", "LiberationSerif-Italic_32", [0.8,0,0.4] ); инфопанель1.присоединить_элемент(текст_ип); текст_ип.обновить_размеры(); }
Здесь мы сначала создали объект элемента. В конструктор передаётся имя элемента, является ли он контейнером и координаты. Затем мы воспользовались методом задать_текст этого класса. В него надо передать объект менеджера (он необходим для визуализации текста через SDL), сам текст, используемый шрифт и массив из трёх чисел, представляющий цвет текста. Далее, присоединяем этот элемент к нашей инфопанели (теперь в ней три элемента). В конце мы у элемента вызываем метод обновить_размеры, его надо вызывать всегда после (пере-)присоединения к родительскому контейнеру, или при изменении каких-то координат родительского контейнера, или при изменении содержимого текста (т. к. его размеры при этом тоже могут измениться), иначе отображение элемента будет некорректным.
Если сейчас запустить программу, то надпись "частота кадров: 0.0" на инфопанели появится, но это пока не очень интересно, так что продолжим.
Добавьте эти строки в конец функции обновление_сцены():
длительность_показа_статистики += длительность_кадра; кадров_в_интервале += 1; if (!(текст_ип is null) && (длительность_показа_статистики>ИНТЕРВАЛ_ОБНОВЛЕНИЯ_СТАТИСТИКИ)) { float частота_кадров = кадров_в_интервале / длительность_показа_статистики; string строка_частота_кадров = format("частота кадров: %.1f", частота_кадров); текст_ип.задать_текст(менеджер, строка_частота_кадров, "LiberationSerif-Italic_32", [0.8,0,0.4] ); текст_ип.обновить_размеры(); длительность_показа_статистики = 0; кадров_в_интервале = 0; }
Здесь мы в каждом кадре суммируем длительности всех предыдущих кадров, и если эта сумма превысила заданную нами константу размера интервала, то вычисляем частоту кадров и выводим её на нашу инфопанель.
Если всё сделано правильно, то сейчас окно нашей программы должно выглядеть так:
Чтобы наша вычисленная частота не обрезалась до системной, отключите синхронизацию экрана, для этого добавьте такую строку в файл настройки_ddd.cfg (см. Урок 2):
синхронизация_экрана = 0
Если сейчас походить/попрыгать/покрутиться нашим персонажем, то будет виден хорошо заметный эффект: при поворотах частота кадров гораздо выше, чем во всех остальных состояниях. Так получается потому, что только при поворотах мы не задействуем скелетную анимацию, а она очень вычислительно ёмкая. Добавив в программу одну переменную-флаг (в котором запоминать, установили мы уже наш меш в «Стояние2», или ещё нет), несложно добиться, чтобы во время стояния скелетная анимация не вызывалась постоянно, но, естественно, при движении и прыжках она всё равно никуда не денется. Если есть желание поэкспериментировать с ускорением работы программы, смотрите Дополнение к Уроку 1.