Предисловие
У вас может возникнуть вопрос, зачем мне 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, в статье есть неточности, но надеюсь, я смог сократить их до минимума. В любом случае, если есть вопросы, задавайте их в комментариях, или в группе ВК.