Як я можу створити безліч різних типів атак, які можна комбінувати?


17

Я роблю 2-грі зверху вниз і хочу мати безліч різних типів атак. Я хотів би зробити атаки дуже гнучкими та поєднувати їх, як працює The Binding of Isaac. Ось список усіх колекціонування в грі . Щоб знайти хороший приклад, давайте подивимось на предмет Бендер з ложки .

Ложка Бендера дає Ісааку можливість стріляти в сльози.

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

Дає Ісааку потрійний удар

Яка хороша архітектура для такого дизайну? Ось таке грубе рішення:

if not spoon bender and not the inner eye then ...
if spoon bender and not the inner eye then ...
if not spoon bender and the inner eye then ...
if spoon bender and the inner eye then ...

Але це вийде з рук дуже швидко. Який кращий спосіб спроектувати таку систему?


Особисто я б просто зберігав усі обладнані елементи у списку і мав би їх реалізувати якийсь загальний інтерфейс, який бере об'єкт, який ви мутуєте, від об'єкта до об’єкта. "для кожного предмета змінювати планову атаку", щоб один предмет міг дублювати кількість снарядів, можна було додати його відтінок і змінити пошкодження (тому, якщо один предмет зробив червоні болти, а інший пожовтів, у вас буде помаранчева атака після обох змін атаки) . Ви також можете мати лише один загальний клас елементів, який має параметри, щоб вирішити, як він змінює заплановану атаку.
Бенджамін Дангер Джонсон

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

Відповіді:


16

Вам абсолютно не потрібно вручну комбінувати комбінації. Ви можете замість цього зосередитись на властивостях, які надає кожен елемент. Наприклад, позиція A встановлює Projectile=Fireball,Targetting=Homing. Пункт B безліч FireMode=ArcShot,Count=3. ArcShotЛогіка відповідає за відправку з Countчисла Projectileелементів в дузі.

Ці два елементи можна комбінувати з будь-якими іншими елементами, які вільно змінюють ці (або інші) властивості. Якщо ви додасте новий тип снаряда, він буде автоматично працювати з ним ArcShot, а якщо ви додасте новий режим стрільби, він автоматично працюватиме зі Fireballснарядами. Аналогічно, Targettingце властивість, яка встановлює контролер снарядів, FireModeстворюючи снаряди, тому їх можна легко і тривіально поєднувати в будь-якій комбінації, як це має сенс.

Ви також можете встановити залежності власності та інше. Наприклад, ArcShotвимагає, щоб у вас був постачальник Projectile(що може бути просто за замовчуванням). Ви можете встановити пріоритети так, що якщо у вас є два активні елементи, які надають Projectileкод, знаєте, який з них використовувати. Або ви можете надати користувальницький інтерфейс, щоб дозволити користувачеві вибирати тип снаряду, або просто вимагати від гравця розв’язати об'єкти з високим пріоритетом, які він не хоче, або використовувати останній предмет тощо. Ви також можете дозволити систему несумісностей , наприклад, такий, що два елементи, які обидва просто змінюють, Projectileне можуть бути забезпечені одночасно.

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


Маленька нітка, але, здається, у вас немає правильних властивостей для прикладів, які ви використовуєте. "Spoon Bender" додає самонаведення, а "Inner Eye" додає потрійний удар. Ні додати дугу, і те й інше - сльози. Якщо ви використовуєте довільні властивості у своєму прикладі для абстрагування дизайну, було б простіше прочитати, якби вони не були названі оманливими способами. Я вважаю за краще "Пункт А" та "Пункт В" перед цим.
Даніель Каплан

1
@tieTYT: Я узагальнив імена та розширив приклад, щоб включити режими націлювання на додаток до типів снарядів та режимів стрільби. Ніколи не грав у BoI більше декількох хвилин, тому я не маю назви таких інтерналізованих, як могли б інші. :)
Шон Міддлічч

Мені здається, що хитра частина - це визначення категорій властивостей. наприклад: Націлювання - це одне, FireMode - інше, кількість може бути 3-м ... а може і не. Можливо, 3 снаряди могли бути вогнем, а 5 - гранатами, хоча це одна зброя.
Даніель Каплан

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

Ось чудове відео про те, чому ви помиляєтесь щодо процедурного програмування youtu.be/QM1iUe6IofM , також керованим даними способом ви описуєте карти будь-яких блоків if / else в окремі набори даних, фактично нічого не роблячи, щоб зменшити кількість спеціальних сценаріїв, які вам потрібно програму, як вам доведеться їх усіх програмувати ...
RenaissanceProgrammer

8

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

Приклад грубого c ++:

class AttackBehaviour
{
    /* other code */
    virtual void Attack(double angle);
};

class TearAttack: public AttackBehaviour
{
    /* other code */
    void Attack(double angle);
};

class TripleAttack: public AttackBehaviour
{
    /* other code */
    AttackBehaviour* baseAttackBehaviour;
    void Attack(double angle);
};

void TripleAttack::Attack(angle)
{
    baseAttackBehaviour->Attack(angle-30);
    baseAttackBehaviour->Attack(angle);
    baseAttackBehaviour->Attack(angle+30);
}

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


Якщо я хочу змінити анімацію, чому це не для мене? Крім того, що робити, якщо я працюю більш функціональною мовою? Чи знаєте ви, що підходить для них модель дизайну?
Даніель Каплан

Його більш жорсткий, оскільки модифікатор завжди буде викликати Attackметод об'єкта, який він об'єднує. TripleAttackКлас не повинен знати про TearAttackкласі. Якби це було правдою, то це призвело б до стільки ж головних болів, як і else-ifблок. Це означає, що будь-яка анімація зі сльозами повинна знаходитися всередині TearAttackBehaviourоб'єкта. Цей об’єкт не знає (і не повинен) знати, що він прикрашений TripleAttackоб'єктом. Результат полягає в тому, що 3 анімації на сльозу проходять самостійно, оскільки є незалежними.
NauticalMile

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

Щодо реалізації цього на більш функціональній мові, я подумаю про це на деякий час і допомню свою відповідь, коли буду готовий.
NauticalMile

1

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

Мій висновок полягає в тому, що Ісаак та його сльози є двома сутностями в центрі масивної системи Компонент-Сутність . Суб'єкти мають деякі основні статистичні дані (movepeed, life, range, damage, sprite тощо), і кожен компонент принесе модифікатор stat та verb.

У коді Ісаак та його сльози мали б список, який містив би речі інтерфейсу. У Ісаака був би список речей, які підписалися на інтерфейс IsaacMutator, і його сльози слізли Mutator. IsaacMutator мав би функції змінити здоров'я Isaacs, швидкість, дальність, зовнішній вигляд та деякі спеціальні дієслова. TearMutator був би подібним. Один раз за цикл гри Ісаак проходитиме через усі IsaacMutators, які є, і всі живі сльози також. Для вашого прикладу англійською мовою було б так:

Isaac has IsaacMutators:
--spoonbender which gives no stat change and: Tears are made homing
--MeatEater which give +1 health, +1 damage and: nothing
--MagicMirror which gives no stat change and: Tears are made reflecting

Tears have tearMutators:
--(depends on MeatEater) +1 damage and: nothing
--(depends on MagicMirror) no stat change and: +1 vector towards isaac
--(depnds on spoonbender) no stat change and: +1 vector towards enemytype

і так далі. Оскільки типи є добавками, ви можете складати та додавати та вилучати до серця вміст.


-5

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

Ви також можете піти про це, створивши новий тип визначення, коли є два пункти, але це фактично додає згортки:

if spoon bender and the inner eye then new spoon bender inner eye

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