ZSCRIPT. Продвинутый HUD.

Предисловие

У вас может возникнуть вопрос, зачем мне ZScript, когда есть Sbarinfo? Ну, sbarinfo мало чего может, когда как ZScript может буквально все:

  • Вы можете делать анимированный статус бар
  • Масштабировать элементы HUD.
  • Задавать переменные и циклы.
  • И много чего еще.

Возможности Zscript, ограничиваются лишь вашей фантазией.

В ZScript все завязано на наследовании, и виртуальных функциях. С наследованием все понятно, а вот виртуальные функции, разберем подробнее.

Виртуальные функции ZSCRIPT.

Если вы изучали C++, то уже знаете что такое виртуальные функции. Для остальных объясню кратко:

Разберем пример. Условно говоря, есть класс «A«, в этом классе есть функция doSomething(), которая выводит на экран сообщение «Это класс А«.

class A 
{
  ......какой-то код......
  void doSomething()
  {
    A_Print(s:"Это класс А");
  }
}

Есть класс «B«, который наследуется от класса «A«. Здесь, уже функция doSomething() выдает строку «Это класс B«.

class B : A
{
  override void doSomething()
  {
    A_Print(s:"Это класс B");
  }
}

Мы использовали слово override (перезаписать), чтобы переписать значение функции doSomething() для данного класса. Вместо override, можно использовать слово virtual, разницы никакой.

Дальше, я буду объяснять техническую информацию, она не потребуется в изучении ZSCRIPT, а нужна лишь для справки.

Но зачем нужны виртуальные функции? Ответ кроется в типах. Каждый класс, по сути, представляет тип объекта. В нашем примере, можно задать в качестве типа переменной — класс «A«, а в качестве значения — объект класса «B«. В таком случае, doSomething() выведет на экран «Это класс B«. Если мы дополнительно создадим бесконечное множество потомков класса «A«, то у всех потомков, сможем использовать функцию doSomething(), указывая в качестве типа класс «A«, например:

A var1 = new A();
A var2 = new B();
A var3 = new C();
A var4 = new D();
var1.doSomething() //"Это класс А"
var2.doSomething() //"Это класс B"
var3.doSomething() //"Это класс C"
var4.doSomething() //"Это класс D"

Подготавливаем основу статусбара.

Для начала, подготовим основу нашего статус бара. Добавьте следующий код к себе в ZScript:

class myStatusBar : BaseStatusBar
{
	override void Init()
	{
		super.Init();
		SetSize(0,320,200);
	}
	override void Draw(int state, double TicFrac)
	{
		Super.Draw (state, TicFrac);
        }
}

В самой первой строке

class myStatusBar : BaseStatusBar

Любой статус бар должен наследоваться от класса BaseStatusBar, по аналогии с Actor, из урока ZSCRIPT. Переводим код из DECORATE в ZSCRIPT.

К слову, Init и Draw это виртуальные функции. В статус баре, в основном, используются следующие виртуальные функции:

  • Init — вызывается в начале игры, здесь производится первоначальная настройка.
  • Tick — вызывается каждый тик (1 тик = 1/35 секунды)
  • Draw — вызывается каждый кадр (60 раз в секунду при 60 fps). Здесь рисуются элементы статус бара.

Полный список виртуальных функций можно узнать на вики.

Также, у статус бара есть поля (Fields), хотя, по факту это переменные. Чуть ниже, я привел список полезных переменных:

  • HorizontalResolution — переменная типа int, хранит значение ширины экрана.
  • VerticalResolution — переменная типа int, хранит значение высоты экрана.
  • Centering — переменная типа bool, если указано true, то все элементы ставятся, относительно центра экрана.
  • CPlayer — переменная, хранящая информацию об игроке (самая полезная).
  • Alpha — переменная типа double, задает непрозрачность.
  • fullscreenOffsets — переменная типа bool, меняет систему координат, я объяснял fullscreenOffsets в уроке SBARINFO — меняем HUD, здесь он работает также.

Получить полный список переменных, можно на том же вики. Хорошо, возвращаемся к коду.

override void Init()
{
  super.Init();

super.Init() будет знаком тем, кто изучал Java. В нашем случае, данная комманда исполняет код функции Init, у родителя (BaseStatusBar). Код выше, можно представить таким образом.

override void Init()
{
  вызвать_функцию_у_родителя.Init();

В Java вместо родителей — суперклассы. А вместо функций — методы.

В виртуальных функциях, всегда вызывайте код родителя («super.название_виртуальной_функции«) в начале блока кода.

Последняя команда которою мы не разобрали это SetSize, синтаксис у неё следующий:

SetSize(насколько_поднять_оружие,ширина,высота);
  • насколько_поднять_оружие— здесь указывается количество пикселей, на которое поднимается оружие.
  • ширина — задается ширина статус бара в пикселях
  • высота— задается высота статус бара в пикселях

Теперь, осталось добавить наш статус бар, в MAPINFO. Пропишите там следующее:

GameInfo
{
  StatusBarClass = "myStatusBar"
}

Готово, вы создали ваш первый статус бар в ZScript.

Только этот статус бар пустой, не видно ни лица думера, ни счетчика патронов. Думаю, самое время это исправить.

Наполняем содержимым.

Сначала, скачайте картинку, с MEGA.

Та самая картинка

Теперь, давайте, перепишем стейт Draw:

override void Draw(int state, double TicFrac)
{
  Super.Draw (state, TicFrac);
  fullscreenOffsets = true;
  int health = CPlayer.mo.health;
  HUDFont bigFont = HUDFont.Create("BIGFONT");
  DrawString(bigFont, FormatNumber(health),(40,-20));
}

Разберем строки кода:

fullscreenOffsets = true;

Включаем fullscreenOffsets, подробнее о нем, в уроке SBARINFO — меняем HUD.

int health = CPlayer.mo.health;

Здесь, мы получаем количество хп, нашего игрока.

HUDFont bigFont = HUDFont.Create("BIGFONT");

В этой строке мы создали шрифт (HUDFont), данный шрифт будет использоваться в DrawString. Синтаксис у HUDFont следующий:

HUDFont.Create("название_шрифта", расстояние_между_буквами, monospaced, смещение_тени_x, смещение_тени_y);
  • название_шрифта — задает название шрифта (шрифты можно создавать в FONTDEFS)
  • расстояние_между_буквами — задает расстояние в пикселях.
  • monospaced — ?????
  • смещение_тени_x — задаем позицию тени, относительно текста, по оси x.
  • смещение_тени_y — задаем позицию тени, относительно текста, по оси y.

Кстати, HUDFont имеет всего-лишь 1 метод, и это Create. Ладно, перейдем дальше.

DrawString(bigFont, FormatNumber(health),(40,-20));

DrawString рисует на экране строку. Синтаксис у него следующий.

DrawString(шрифт, строка, позиция);
  • шрифт — объект типа HUDFont, задает шрифт.
  • строка — текстовая строка, например «Hello World», она как раз и появится на экране.
  • позиция — принимает 2 целочисленных значения, формата (значение1, значение2), и задает позицию строки на экране. Подобный формат (значение1, значение2), называется вектором.

Разумеется, это не полный синтаксис, а упрощенный. Подробная информация на вики.

В качестве строки, я использовал FormatNumber(health). Функция FormatNumber превращает число (int), в строку (string).

С кодом разобрались, время тестить.

Как вы видите, сбоку экрана появился показатель здоровья.

Теперь, давайте добавим показатель брони. Для этого, добавьте следующий код в стейт Draw.

int my_armor = GetArmorAmount();
DrawString(bigFont, FormatNumber(my_armor),(-40,-20));

Функция GetArmorAmount() возвращает количество брони. Именно это число (my_armor), мы рисуем на экране слева.

Теперь добавим картинку брони. Для этого, используем функцию DrawImage.

Добавьте следующий код, в блок Draw:

DrawImage("ARM1A0",(-60,-10));

Рисуем картинку.

Синтаксис у DrawImage, следующий.

DrawImage("название_картинки",(x,y));
  • название_картинки — название файла картинки без расширения
  • x, y — расположение картинки на экране.

В итоге, получилось следующее.

Добавляем MugShot (лицо думера).

В Draw, добавьте следующее:

DrawTexture(GetMugshot(5),(20,-20));

Разберем, данную строку:

  • GetMugshot(5) — возвращает текстуру лица думера.
  • DrawTexture — рисует эту текстуру на экране.

Синтаксис тут следующий:

DrawTexture(текстура,(x,y));
  • текстура — принимает объект типа TextureID, именно этот объект возвращает GetMugshot
  • x, y — координаты, на которых рисуется текстура.

Получилось, следующее.

Лицо думера можно увеличить, для этого замените DrawTexture, на следующее:

DrawTexture(GetMugshot(5),(20,-20), scale:(2,2));

Сразу бросается в глаза новый аргумент «scale:(2,2)«. В данном случае, мы обратились к нужному аргументу по имени, и присвоили ему значение. Подобный синтаксис будет знаком тем, кто работал с C#. Для понимания, разберем полный синтаксис (взят с вики)

DrawTexture(TextureID texture, Vector2 pos, int flags = 0, double Alpha = 1.0, Vector2 box = (-1, -1), Vector2 scale = (1, 1))

Вместо того что-бы заполнять все аргументы.

DrawTexture(GetMugshot(5),(20,-20), 0, 1.0, (-1, -1), (2, 2));

Мы, заполнили только то, что нужно, оставив остальным аргументам, значение по умолчанию.

DrawTexture(GetMugshot(5),(20,-20), scale:(2,2));

В итоге, MugShot увеличился вдвое.

Рисуем количество патронов (Ammo).

В Draw, напишите это:

Ammo ammo1, ammo2;
int ammo_count = 0;
[ammo1, ammo2, ammo_count] = GetCurrentAmmo();
DrawString(bigFont, FormatNumber(ammo_count),(-40,-50));

Что-бы понять этот код, вновь обратимся к вики, и посмотрим синтаксис GetCurrentAmmo.

Ammo, Ammo, int, int GetCurrentAmmo() const

Да, все верно, GetCurrentAmmo() возвращает 4 значения. А именно:

Ammo1, Ammo2, количество_патронов_1_типа, количество_патронов_2_типа.

Во всей этой солянке, нас интересует только количество_патронов_1_типа. Для его получения, я создал переменные:

Ammo ammo1, ammo2;
int ammo_count = 0;

ammo1, ammo2, я использовал как переменные затычки, просто что-бы были. И, в следующей строке:

[ammo1, ammo2, ammo_count] = GetCurrentAmmo();

Получил значение ammo_count. Кстати, когда используешь несколько переменных для присваивания, нужно использовать квадратные скобки [ ].

DrawString(bigFont, FormatNumber(ammo_count),(-40,-50));

Дальше, выводим значение на экран.

Объединяем строки (concatenate).

В блоке Draw, объединим количество патронов со строкой «Ammo: «.

DrawString(bigFont, "Ammo: "..FormatNumber(ammo_count),(-120,-50));

До этого, мы видели операторы (+, -, /, *) — все они работают с числами. Теперь, появился новый оператор (..) две точки, он, уже, работает со строками. Данный оператор, объединяет строки. В данном примере.

DrawString(bigFont, "Ammo: "..FormatNumber(ammo_count),(-120,-50));

Мы объединили строку «Ammo: « и строку FormatNumber(ammo_count), при помощи оператора «..«

Последнее что осталось, это добавить баннер «ZScript Status Bar«.

Добавляем финальный баннер.

Добавьте следующий код в блок Draw:

DrawImage("USELESS",(HorizontalResolution/2,50),Alpha:0.5);

Здесь, мы нарисовали баннер на в центре экрана, по оси x.

HorizontalResolution/2

И сместили его вниз на 50 пикселей (разрешение картинки 200×50). А также, сделали картинку полупрозрачной.

Alpha:0.5

В итоге получился вот такой статусбар.

Заключение.

Статья получилась огромной, и в ней я много чего, подробно объяснил, и честно говоря, я устал писать эту статью. Как заметил McMare, в статье есть неточности, но надеюсь, я смог сократить их до минимума. В любом случае, если есть вопросы, задавайте их в комментариях, или в группе ВК.

Итоговый результат.

Пожалуйста отключи блокировщик рекламы, или внеси сайт в белый список!

Please disable your adblocker or whitelist this site!