Source Modding — Часть 2 — Всё есть сущность

от автора

Source Engine никогда не постареет. Во всяком случае умирать он еще не собирается.

В предыдущей части урока мы научились базовой работе с VPC и печати сообщений в консоль разработчика.
В этой части урока мы разберем систему энтити, научимся создавать классы и создадим нашу первую логическую сущность.

Опять термины

Сущность или Энтити(entity) — основной объект на уровне.
Система I/O — система, предназначенная для "общения" сущностей друг с другом.
Инпут(input) — команда, меняющая поведение сущности. Бывает вызвана игроком через консоль или аутпутом.
Аутпут(output) — событие, вызываемое при изменении состояния сущности.

А вообще, как это работает?

Любой уровень в Source — набор сущностей.
На каждом уровне присутствует как минимум одна сущность, называемая worldspawn. Эта сущность по факту — весь "твердый" мир, а именно:

  • Примитивы a.k.a. браши — кубы, шары, арки, etc.
  • Так называемые displacements (диспы, диспенсеры) — примитивы на стероидах, позволяющие создавать более детализированные ландшафты или (Новые карты в CS:GO используют это!) здания. Не поддаются оптимизации, а поэтому желательно прикрывать их "снизу" примитивами.
  • Статичные модели (prop_static). В отличие от тех же prop_dynamic, сущностями как таковыми не являются.

Остальные же сущности (включая игроков!) имеют свои собственные свойства и поведение.
Все сущности можно разделить вот так:

  • Точечные — это такие сущности, которые расположены в мире в определенной точке.
  • Брашевые — это такие сущности, которые состоят из примитивов.
    ВАЖНО/Hammer: Не превращайте диспы в брашевые сущности!!!

Логическая энтити

Логические сущности — самые простые из всех, так как не имеют визуального компонента и коллизии. Самые простые и самые полезные!
Например, сущность logic_auto через несколько аутпутов обрабатывает события запуска карты в различных режимах.

logic_5 — код

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

  1. В проекте серверной части игры создайте файл с названием logic_5.cpp.
    То есть, файл будет лежать по пути: src/game/server/logic_5.cpp.

  2. Включите необходимые заголовки в наш файл:

    // cbase.h - прекомпилированный заголовок. // Он включает в себя всё необходимое для базового программирования под сурс. #include "cbase.h" // memdbgon.h - заголовок с переопределениями операторов new и delete // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h"

  3. Создайте класс CLogicFive наследующий CLogicEntity:

    class CLogicFive : public CLogicEntity {     DECLARE_CLASS( CLogicFive, CLogicEntity ); public:     // ... private:     // ... };

    Погодите-ка! Что за DECLARE_CLASS()? Это специальный макрос, предназначенный для облегчения работы при множественном наследовании. Он определяет два типа — ThisClass для текущего класса и BaseClass для базового класса (этот тип мы кстати видели в предыдущей части урока…).

  4. Добавьте определения под public:

    public:     DECLARE_DATADESC();     void Input_Tick( inputdata_t &id );

    Ну и что есть что?

    • DECLARE_DATADESC() это макрос, объявляющий таблицу метаданных класса для движка.
    • void Input_Tick( inputdata_t &id ) это функция, которая будет являть собой инпут tick.

  5. Добавьте счетчик тиков под private:

    private:     int m_iTicks = 0;

  6. Теперь нам необходимо как бы "прибить гвоздями" наш класс к имени сущности logic_5.
    И в этом нам поможет (еще один!) макрос — LINK_ENTITY_TO_CLASS()!
    Добавляем его сразу после объявления класса:

        // ...     int m_iTicks = 0; }; LINK_ENTITY_TO_CLASS( logic_5, CLogicFive );

  7. Теперь нам необходимо определить таблицу метаданных, объявленную в шаге 4:

    BEGIN_DATADESC( CLogicFive )     DEFINE_FIELD( m_iTicks, FIELD_INTEGER ), // наш счетчик тиков     DEFINE_INPUTFUNC( FIELD_VOID, "tick", Input_Tick ), // наш инпут END_DATADESC();

    • BEGIN_DATADESC() — макрос, раскрывающийся в начало таблицы метаданных для класса.
    • DEFINE_FIELD() — макрос, создающий в таблице поле. Любое поле, добавленное этим макросом в таблицу будет иметь своё место в файлах сохранения!
    • DEFINE_INPUTFUNC() — макрос, определяющий инпут. В нашем случае он ничего не возвращает (FIELD_VOID), называется tick и ссылается на метод Input_Tick.
    • END_DATADESC() — макрос, раскрывающийся в конец таблицы метаданных.

  8. Из необходимого остается только реализовать метод Input_Tick:

    // обработчик инпута tick() void CLogicFive::Input_Tick( inputdata_t &id ) {     m_iTicks++;     if( m_iTicks % 5 == 0 )         // если новое количество тиков делится на пять, напечатать цветное сообщение!         ConColorMsg( Color( 255, 255, 0 ), "logic_five: Another fifth tick!\n" ); }

  9. [НЕОБЯЗАТЕЛЬНО] Добавьте наш файл в VPC скрипт:

    // src/game/server/server_episodic.vpc // где-то внутри $Folder "Source Files" $File "$SRCDIR/game/server/logic_5.cpp

    Затем, разумеется, перегенерируйте решение.

logic_5 — в игре

  1. Запустите игру и любую карту (sdk_vehicles или dm_lockdown, например)
  2. Создайте нашу сущность консольной командой:
    ent_create logic_5
  3. Пять раз вызовите инпут tick консольной командой:
    ent_fire logic_5 tick
  4. После пятого вызова в консоли должно появиться желтое (или любого другого цвета если вы его поменяли) сообщение!

Заключение

Чему мы научились?

[Я надеюсь, что] из этой части урока вы узнали:

  • Что такое сущности и каких видов они бывают
  • Что такое система ввода-вывода у сущностей
  • Как создавать свои логические сущности

Что будет дальше?

В третьей части я расскажу о основах редактора Valve Hammer Editor и структуре FGD файлов.
Это будет необходимо, так как частью позже я буду дополнять этот урок, добавляя нашей сущности вместо консольного сообщения аутпут.

Полезные ссылки

ссылка на оригинал статьи https://habr.com/ru/post/511032/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *