Робити бонуси в компонентній системі


29

Я тільки починаю по-справжньому обертатися навколо компонентної конструкції. Я не знаю, який "правильний" спосіб це зробити.

Ось сценарій. Гравець може обладнати щит. Щит намальований міхуром навколо гравця, він має окрему форму зіткнення і зменшує збиток, який гравець отримує від ефектів на область.

Як побудований такий щит у грі на основі компонентів?

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

  • Зменшення / фільтрування пошкоджень
  • Спрайт
  • Колайдер.

Що може погіршити, різні зміни щита можуть мати ще більше поведінки, і всі вони можуть бути компонентами:

  • підвищити максимальне здоров'я гравця
  • регенерування здоров'я
  • прогин снаряда
  • тощо

  1. Чи я це передумую? Чи повинен щит бути просто складовою?
    Я справді думаю, що це неправильна відповідь. Тож якщо ви думаєте, що це шлях, будь ласка, поясніть.

  2. Чи повинен щит бути власною сутністю, яка відслідковує місцезнаходження гравця?
    Це може ускладнити здійснення фільтрації пошкоджень. Це також неначе розмиває лінії між приєднаними компонентами та об'єктами.

  3. Чи повинен щит бути компонентом, в якому розміщені інші компоненти?
    Я ніколи не бачив і не чув про щось подібне, але, можливо, це звичайне явище, і я просто ще недостатньо глибокий.

  4. Чи повинен щит бути лише набором компонентів, які додаються до програвача?
    Можливо, з додатковим компонентом для управління іншими, наприклад, щоб вони могли бути видалені як група. (випадково залиште після себе компонент зменшення шкоди, тепер це було б весело).

  5. Щось інше, що очевидно для тих, хто має більш складний досвід?


Я взяв на себе сміливість зробити ваш титул більш конкретним.
Тетрад

Відповіді:


11

Чи повинен щит бути власною сутністю, яка відслідковує місцезнаходження гравця? Це може ускладнити здійснення фільтрації пошкоджень. Це також неначе розмиває лінії між приєднаними компонентами та об'єктами.

Редагувати: Я думаю, що для окремої сутності недостатньо «автономної поведінки». У цьому конкретному випадку щит слід за ціллю, працює для цілі і не переживає ціль. Хоча я схильний погоджуватися, що в понятті "щитовий об'єкт" немає нічого поганого, в цьому випадку ми маємо справу з поведінкою, яка добре вписується в компонент. Але я також прихильник суто логічних сутностей (на відміну від повномасштабних систем сутностей, в яких можна знайти компоненти Transform і Rendering).

Чи повинен щит бути компонентом, в якому розміщені інші компоненти? Я ніколи не бачив і не чув про щось подібне, але, можливо, це звичайне явище, і я просто ще недостатньо глибокий.

Бачити це в іншій перспективі; додаючи компонент, додаються й інші компоненти, а після видалення додаткові компоненти також втрачаються.

Чи повинен щит бути лише набором компонентів, які додаються до програвача? Можливо, з додатковим компонентом для управління іншими, наприклад, щоб вони могли бути видалені як група. (випадково залиште після себе компонент зменшення шкоди, тепер це було б весело).

Це може бути рішенням, воно сприятиме повторному використанню, однак воно також є більш схильним до помилок (наприклад, про проблему, про яку ви згадали). Це не обов'язково погано. Ви можете дізнатись нові комбінації заклинань із спробою та помилками :)

Щось інше, що очевидно для тих, хто має більш складний досвід?

Я збираюся трохи допрацювати.

Я вважаю, ви помітили, як деякі компоненти повинні мати пріоритет, незалежно від того, коли вони були додані до об'єкта (це також відповість на ваше інше питання).

Я також припускаю, що ми використовуємо комунікацію на основі повідомлень (заради обговорення, це лише абстракція щодо виклику методу на даний момент).

Щоразу, коли компонент щита "встановлений", обробники повідомлень компонентів щита приковані до конкретного (більш високого) порядку.

Handler Stage    Handler Level     Handler Priority
In               Pre               System High
Out              Invariant         High
                 Post              AboveNormal
                                   Normal
                                   BelowNormal
                                   Low
                                   System Low

In - incoming messages
Out - outgoing messages
Index = ((int)Level | (int)Priority)

Компонент "stats" встановлює обробник повідомлень "пошкодження" в індексі In / Invariant / Normal. Щоразу, коли надходить повідомлення про "пошкодження", зменшуйте HP на його "значення".

Досить стандартна поведінка (будь-яка природна стійкість до пошкоджень та / або расові риси, як би там не було).

Компонент щита встановлює обробник повідомлень "пошкодження" за індексом In / Pre / High.

Every time a "damage" message is received, deplete the shield energy and substract
the shield energy from the damage value, so that the damage down the message
handler pipeline is reduced.

damage -> stats
    stats
        stats.hp -= damage.value

damage -> shield -> stats
    shield
        if(shield.energy) {
            remove_me();
            return;
        }
        damage.value -= shield.energyquantum
        shield.energy -= shield.energyquantum;

     stats
        stats.hp -= damage.value

Ви можете бачити, що це досить гнучко, хоча це потребує ретельного планування при розробці взаємодії компонентів, оскільки вам доведеться визначити, в якій частині оброблювачів подій компонентів протоколу обробки повідомлень встановлені.

Має сенс? Повідомте мене, чи можу я додати більше деталей.

Редагувати: стосовно кількох компонентів (два компоненти броні). Ви можете або відстежувати загальний кількість екземплярів лише в одному екземплярі сутності (однак це вбиває стан компонента), і просто продовжувати додавати обробники подій повідомлень, або переконайтеся, що контейнери компонентів дозволяють заздалегідь дублювати типи компонентів.


Ви відповіли "ні" на перше запитання, не вказавши жодної причини. Навчити інших - це допомогти їм зрозуміти міркування, що стоять за будь-яким рішенням. ІМО, достатньо того факту, що в RL силове поле буде окремим "фізичним утворенням" від вашого власного тіла, щоб воно було окремим кодом у коді. Чи можете ви запропонувати вагомі причини, щоб підказати, чому їхати цим маршрутом погано?
Інженер

@Nick, я ні в якому разі не намагаюся нікому чогось навчити, а поділяюся тим, що я знаю з цього приводу. Однак я збираюся додати обґрунтування того "ні", яке, сподіваємось, усуне цей неприємний результат :(
Raine

Ваш пункт автономії має добрий сенс. Але ви зауважуєте: "у цьому випадку ми маємо справу з поведінкою". Правда - поведінка за участю абсолютно окремого фізичного об'єкта (форма зіткнення щита). Для мене одна сутність пов'язана з одним фізичним тілом (або складеним набором тіл, сполучених, наприклад, суглобами). Як ви це погоджуєте? Зі свого боку, мені буде незручно додавати "фіктивну" фіксацію, яка активується лише в тому випадку, якщо гравцеві трапляється використовувати щит. ІМО негнучкі, важко підтримуються в усіх структурах. Розглянемо також гру, де щитові пояси тримають щити навіть після смерті (Дюна).
Інженер

@ Nick, більшість сутнісних систем мають однакові як логічні, так і графічні компоненти, тому в цьому випадку мати сутність для щита абсолютно розумно. У чисто логічних системах сутності "автономія" є продуктом того, наскільки складним є об'єкт, його залежність і термін експлуатації. Зрештою, вимога є королем - і враховуючи, що немає реального консенсусу щодо того, що таке система сутності, є багато місця для розроблених проектів рішень :)
Raine

@deft_code, будь ласка, повідомте мене, чи можу я покращити свою відповідь.
Дощ

4

1) Чи я це передумую? Чи повинен щит бути просто складовою?

Можливо, залежить від того, наскільки багаторазовим ви хочете бути вашим кодом, і якщо це має сенс.

2) Чи повинен щит бути власною сутністю, яка відстежує місцезнаходження гравця?

Хіба що цей щит не є якоюсь істотою, яка може ходити незалежно на якомусь етапі.

3) Чи повинен щит бути компонентом, в якому розміщені інші компоненти?

Це дуже схоже на сутність, тому відповідь - ні.

4) Чи повинен щит бути лише набором компонентів, які додаються до програвача?

Це ймовірно.

"Зменшення шкоди / фільтрація"

  • функціональність основного щита

"Спрайт"

  • Чи є причина, що ви не можете додати ще один елемент SpriteComponent до своєї символьної сутності (іншими словами, більше ніж один компонент певного типу на кожну сутність)?

"Колайдер"

  • ти впевнений, що тобі потрібен ще один? Це залежить від вашого двигуна фізики. Чи можете ви надіслати повідомлення ColliderComponent суб'єкта символу і попросити його змінити форму?

"Максимальне здоров'я гравця, режим здоров'я, відхилення снаряда тощо"

  • інші артефакти можуть зробити це (мечі, чоботи, каблучки, заклинання / зілля / відвідування святинь тощо), тому вони повинні бути компонентами.

3

Щит, як фізична сутність, нічим не відрізняється від будь-якого іншого фізичного утворення, наприклад, безпілотник, який кружляє навколо вас (і який насправді міг би бути самим типом щита!). Тому зробіть щит окремим логічним утворенням (таким чином дозволяючи йому вміщувати власні компоненти).

Надайте своєму екрану пару компонентів: фізичний / просторовий компонент, який представляє його форму зіткнення, і компонент DamageAffector, який містить посилання на якусь сутність, до якої вона буде наносити збільшений або зменшений збиток (наприклад, персонаж вашого гравця) кожного разу, коли сутність утримання DamageAffector спричинює шкоду. Таким чином, ваш плеєр завдає шкоди "через проксі".

Установіть позицію об'єкта щита на позицію гравця кожного галочки. (Напишіть клас компонентів для багаторазового використання, який робить це: пишіть один раз, використовуйте багато разів.)

Вам потрібно буде створити сутність щита, наприклад. на збір живлення. Я використовую загальну концепцію під назвою Емітер, яка є типом компонентної сутності, що породжує нові сутності (як правило, за допомогою EntityFactory, на який вона посилається). Там, де ви вирішите знайти емітер, залежить від вас, наприклад. поставте його на джерело живлення і активуйте його, коли накопичувач збирається.


Чи повинен щит бути власною сутністю, яка відслідковує місцезнаходження гравця? Це може ускладнити здійснення фільтрації пошкоджень. Це також неначе розмиває лінії між приєднаними компонентами та об'єктами.

Існує тонка грань між логічними підкомпонентами (просторовими, AI, слотами для зброї, обробкою введеннями тощо) та фізичними підкомпонентами. Вам потрібно визначитися, на якій стороні ви стоїте, оскільки це чітко визначає, яка система сутності у вас є. Для мене підкомпонент "Фізика" моєї сутності використовує фізико-ієрархічні відносини (наприклад, кінцівки в тілі - вузли сценарних сценаріїв), тоді як логічні контролери, зазначені вище, як правило, представлені компонентами вашої сутності, а не вони представляють індивідуальні фізичні «світильники».


3

Чи повинен щит бути компонентом, в якому розміщені інші компоненти?

Можливо, не розміщено інших компонентів, але контролює термін експлуатації підкомпонентів. Тож у деяких грубих псевдокодах ваш клієнтський код додав би цей компонент "щит".

class Shield : Component
{
    void Start() // happens when the component is added
    {
        sprite = entity.add_component<Sprite>( "shield" );
        collider = entity.add_component<Collider>( whatever );
        //etc
    }

    void OnDestroy() // when the component is removed
    {
        entity.remove_component( sprite );
        entity.remove_component( collider );
    }

    void Update() // every tick
    {
        if( ShouldRemoveSelf() ) // probably time based or something
            entity.remove_component( this );
    }
}

Не ясно, що thisозначає у вашій відповіді. Є thisпосиланням на компонент щита або ви маєте в виду особа , яка використовує щит, його батьків? Плутанина може бути моєю виною. "Компонент на основі" начебто невиразний. У моїй версії об'єктів на основі компонентів сутність є просто контейнером компонентів з деякою власною мінімальною функціональністю (назва об'єкта, теги, повідомлення та ін.).
deft_code

Було б менш заплутано, якби я gameObjectщось використовував . Це посилання на поточний об'єкт гри / сутність / що належить компонентам.
Тетрад

0

Якщо ваша компонентна система дозволяє створювати сценарії, компонент екрана може бути майже суперкомпонентом, який просто викликає сценарій для його параметра "ефект". Таким чином ви підтримуєте простоту одного компонента для екранів і вивантажуєте всю логіку того, що він насправді робить, для власних файлів сценаріїв, які подаються до екранів за визначеннями вашої особи.

Я роблю щось подібне для свого Moveable компонента, він містить поле, яке є сценарієм keyreaction (підклас скрипту в моєму двигуні). Цей скрипт визначає метод, що відповідає моєму вхідному повідомленню. як такий, я можу просто зробити щось подібне у моєму файлі визначення tempalte

camera template
    moveable
    {
        keyreaction = "scriptforcameramoves"

    }  

player template
    moveable
    {
        keyreaction = "scriptfroplayerkeypress"

    }  

то в моєму рухомому компоненті під час реєстрації повідомлень я реєструю сценарій методу Do (код у C #)

Owner.RegisterHandler<InputStateInformation>(MessageType.InputUpdate, kScript.Do);

звичайно, це спирається на мій метод Do, керуючись зразком функцій, які приймає мій RegisterHandler. У цьому випадку його (відправник IComponent, аргумент типу ref)

тому мій "скрипт" (у моєму випадку також C # щойно зібраний цикл) визначає

 public class CameraMoveScript : KeyReactionScript
{
 public override void Do(IComponent pSender, ref InputStateInformation inputState)
 {
    //code here
 }
}

і мій базовий клас KeyReactionScript

public class KeyReactionScript : Script
{
      public virtual void Do(IComponent pSender, ref InputStateInformation inputState);
}

потім пізніше, коли компонент введення надсилає повідомлення типу MessageTypes.InputUpdate з типом як таким

 InputStateInformation myInputState = InputSystem.GetInputState();
 SendMessage<InputStateInformation>(MessageTypes.InputUpdate, ref myInputState);

Метод у скрипті, який був пов'язаний з цим повідомленням та типом даних, буде запущений та керуватиме усією логікою.

Код досить специфічний для мого двигуна, але логіка повинна бути функціональною у будь-якому випадку. Я роблю це для багатьох типів, щоб зберегти структуру компонентів простою та гнучкою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.