← к содержанию

Урок 18. Инфопанели

В 3D-играх обычно используется так называемый интерфейс – некие изображения и надписи, отображаемые в 2D-режиме поверх сцены. Также в играх должны отображаться такие сущности, как меню, статистика, настройки и т. д. Для всего этого в DDD предназначены инфопанели. Фактически, инфопанели – это контейнеры для мешей-прямоугольников (которые, соответственно, называются элементами инфопанелей) с нулевой координатой Z, которые отображаются поверх всей остальной сцены.

В этом уроке мы добавим одну простую инфопанель к сцене с персонажем Кленовый лист, построенной в двух предыдущих уроках.

  1. Откройте модуль scena.d и добавьте импорт модулей, связанных с инфопанелями:

import ddd.infopanel, ddd.text_infopaneli;
  1. добавьте эти строки в конец функции сцена():

    Инфопанель инфопанель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]
		}
	]
}

Как вы можете тут видель, у инфопанели должны быть прописаны несколько полей:

В нашем случае мы используем тип координат "Относительно", т. е. все значения (и у самой инфопанели, и у её элементов) задаются в диапазоне от 0 до 1. Координаты считаются от левого нижнего угла экрана. По поводу остальных значений поля тип_координат смотрите документацию модуля ddd.infopanel.

Поля элемента (объекта класса ЭлементИнфопанели):

Дочерние элементы (в нашем случае их нет) всегда отображаются поверх родительского элемента, отсчитываются в относительных координатах и не могут выходить за его границы.


В продолжении урока добавим на нашу инфопанель статистику – отображение частоты кадров.

  1. Добавьте в модуль константу и несколько глобальных переменных:

const float ИНТЕРВАЛ_ОБНОВЛЕНИЯ_СТАТИСТИКИ = 0.333; // 3 раза в секунду

ТекстовыйЭлементИнфопанели текст_ип;
float длительность_показа_статистики = 0;
int кадров_в_интервале = 0;

Класс ТекстовыйЭлементИнфопанели является подклассом от ЭлементИнфопанели и позволяет размещать на инфопанели текст, чем мы и воспользуемся.

  1. Добавьте внутрь оператора 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. Добавьте эти строки в конец функции обновление_сцены():

    длительность_показа_статистики += длительность_кадра;
    кадров_в_интервале += 1;
    if (!(текст_ип is null) && (длительность_показа_статистики>ИНТЕРВАЛ_ОБНОВЛЕНИЯ_СТАТИСТИКИ)) {
        float частота_кадров = кадров_в_интервале / длительность_показа_статистики; 
        string строка_частота_кадров = format("частота кадров: %.1f", частота_кадров);
        текст_ип.задать_текст(менеджер, строка_частота_кадров, "LiberationSerif-Italic_32", [0.8,0,0.4] );
        текст_ип.обновить_размеры();
        длительность_показа_статистики = 0;
        кадров_в_интервале = 0;
    }

Здесь мы в каждом кадре суммируем длительности всех предыдущих кадров, и если эта сумма превысила заданную нами константу размера интервала, то вычисляем частоту кадров и выводим её на нашу инфопанель.

Если всё сделано правильно, то сейчас окно нашей программы должно выглядеть так:


  1. Чтобы наша вычисленная частота не обрезалась до системной, отключите синхронизацию экрана, для этого добавьте такую строку в файл настройки_ddd.cfg (см. Урок 2):

синхронизация_экрана = 0

Если сейчас походить/попрыгать/покрутиться нашим персонажем, то будет виден хорошо заметный эффект: при поворотах частота кадров гораздо выше, чем во всех остальных состояниях. Так получается потому, что только при поворотах мы не задействуем скелетную анимацию, а она очень вычислительно ёмкая. Добавив в программу одну переменную-флаг (в котором запоминать, установили мы уже наш меш в «Стояние2», или ещё нет), несложно добиться, чтобы во время стояния скелетная анимация не вызывалась постоянно, но, естественно, при движении и прыжках она всё равно никуда не денется. Если есть желание поэкспериментировать с ускорением работы программы, смотрите Дополнение к Уроку 1.