Pull to refresh

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

Reading time4 min
Views3.8K

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


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



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


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


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


Любой уровень в Source — набор сущностей.


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


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

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


Все сущности можно разделить вот так:



  • Точечные — это такие сущности, которые расположены в мире в определенной точке.
  • Брашевые — это такие сущности, которые состоят из примитивов.

ВАЖНО/Hammer: Не превращайте displacement в брашевые сущности, это приведет к ошибке компиляции уровня (vbsp)!


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


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


Например, сущность 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 файлов.
Это будет необходимо, так как частью позже я буду дополнять этот урок, добавляя нашей сущности вместо консольного сообщения аутпут.


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


Tags:
Hubs:
+4
Comments4

Articles

Change theme settings