Обсуждение архитектуры игрового приложения - предложения и варианты

+91
в блоге IT Pony!
— Здравствуйте, это сайт по MyLittlePony?
— Какие паттерны мне использовать при разработке приложения?

Собственно, есть вполне конкретная задача, над которой я рву бороду уже неделю.

Дано: простая игра, назовём её «Пони и пчела». Двумерная аркада, вид сверху, стрелочное управление, прям в духе начала 90-х :-)
Пони от пчелы убегает в пределах экранных координат, пчела следует за ней неутомимо с равной скоростью, как T-800.
Догоняет — жалит в круп, после N покусаний, игра заканчивается.

Соль игры — в бонусах, которые выпадают на игровой экран, и поедая которые, пони получает различные преимущества.
Например, бонус «скорость» — собрав его, пони ускоряется и отрывается от пчелы.
Бонус «неуязвимость» — пони на время становится пофиг на пчелу, словно после применения IDDQD.
Бонус «невидимость» — пчела на время перестаёт «видеть» пони и начинает двигаться хаотично.
И т.д.

Описание проблемы и вопрос по архитектуре под катом

Вот так выглядит простейшее решение:



Главная проблема — что видов бонусов может быть много, десятки, сотни видов.
И по сути, каждый введенный новый бонус требует помимо собственного кода действия бонуса, еще вносить исправления в код либо пчелы, либо пони (например, для реализации неуязвимости, придется явно вводить это в код пони или пчелы, а роль класса бонуса сведется только к разовой процедуре «вкл»).
Таким образом, лобовое решение приводит к тому, что число связей между бонусами и пони/пчелой растет линейно вместе с ростом числа бонусов.
Это ни разу не круто.
К тому же, в идеале, разработкой бонусов могут заниматься вообще другие люди, которые не могут и не должны править основные классы пони и пчелы.

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



Плюс очевиден — для добавления бонуса, его нужно просто закодировать, и не лезть в код к пчеле и пони, к тому же, число связей снижено до минимума.
Минус — в один прекрасный момент, действие двух бонусов окажется конфликтным и конвеер команд разрушится.

Третий и наиболее годный (имхо) вариант — введение дополнительных объектов-менеджеров, которые управляют пони и пчелой, но каждый только в рамках своего типа действия. Один менеджер по движению, один по взаимодействию с пчелой, один для отрисовки и т.д. При этом, каждый бонус закрепляется за одним или нескольким менеджерами (например, бонус «Заморозка пчелы» обращается к менеджеру по движению (меняет скорость) и по отрисовке (добавляет лёд)) — и обращается не к пчеле или к пони, а к менеджерам, а те уже работают со своими объектами.



Плюсы тут тоже понятны — сотня связей разбивается на десять по десять, при введении нового бонуса пишем код для него и если нужно — только в тот менеджер, с которым бонус связан.
Минусы — общее усложнение архитектуры.

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

153 комментария

В программистскую ленту
NTFS
0
Мне кажется, проблема несколько раздута.

Лично моя практика показывает, что первый вариант (где все просто пишется в лоб прямо в коде реализации затрагиваемых вещей) идеален для малых-средних объемов (бонусов не более 10-15). Возможно какие-то наиболее распространенные эффекты (вроде увеличения скорости) можно частично привести ко второму варианту — в наружу вынести, например, базовую скорость движения, и каждый тик уточнять реальную скорость, которая так же торчит снаружи.

Если их количество грозит превысить второй десяток…
Окей, псевдокод на TSДекораторы как паттерн отпадают — грозит превратить все в трэш.

Третий вариант с менеджерами — по сути ты просто все говно перекладываешь из одного места в другое.

Если очень грубо, применяя ООП
class EffectableEntity {
    effects: Effects[]; // или Set<Effects> если они не могут дублироваться

    update() {
        this.effects.forEach(effect => effect.apply(this));
    }
}

interface Effect {
    apply(entity: EffectableEntity): void;
}


Эффекты написаны отдельными классами, они явно накладываются на сущность путем добавления в ее массив (или другую фигню подобного рода), а сущность каждый тик применяет эффект. Тонкости могут варьироваться по ситуации, но я бы сейчас это написал так.

Некоторая проблема будет с невидимостью и прочей штукой, когда реальную разницу от эффекта ловит другая сторона. В GBS у меня это решалось «тегами», которые учитывались (бы) ИИ (или тем, что он тыкает) второй стороны. Второй вариант — раздувать интерфейс эффекта (или базовый класс, чтобы не писать заглушки), добавляя больше рычагов под разные случаи (по-моему он тоже используется в GBS). Первый вариант звучит более мерзко, но по-моему он более правильный, если ту же самую невидимость учитывать в какой-то общей функции поиска.

Хрен знает, есть ли у такой структуры какой-то паттерн.
StaSyaN
+1
Изменён автором
Проблема не то, что раздута — просто условия задачи во многом заданы в теоретическом поле, и нужно сделать максимально развитую архитектуру, а не просто набросать и забыть.
Спасибо, вариант неплохой, обдумаю.
NTFS
0
Для комбинаций из двух и более бонусов, своеобразно сочетающихся между собой, можно создавать отдельные варианты, которые замещают исходные при обработке.

Это нормально и не костыль, в айзеке есть сочетания, которые, видимо, запрограммированы явно, иначе бы они просто не работали.
StaSyaN
0
Вообще не вкурил, слишком сложный программисткий замут. Потому влезу как дилетант:
Первое и самое основное: нужно ли, на самом деле, такое колличество бонусов?
Есть ли возможность присвоить персонажам приватные переменные и через них менять их состояния в зависимости от бонуса? (я так делал, когда пытался в бонусы)
KaskeT
0
нужно ли, на самом деле, такое колличество бонусов?

По условиям задачи, возможно вплоть до 100 типов бонусов совершенно разных действий.

Есть ли возможность присвоить персонажам приватные переменные и через них менять их состояния в зависимости от бонуса?

Так и делается в большинстве случаев, это у меня первый вариант.
Но это имеет определенные архитектурные проблемы, поиск путей преодоления которых — цель моего исследования.
NTFS
0
Изменён автором
Я не очень понимаю, какие команды и как могут конфликтовать.

Даже если они и будут так делать, что мешает добавить к каждой команде поле, в котором указано, за что она отвечает, и если в очереди две команды, которые имеют одинаковое такое поле, то работать будет только первая/последняя/сильнейшая из них, а остальные удаляются?
Niko_de_Andjelo
0
Изменён автором
Ну например, команда хаотичного движения пчелы и команда её полной остановки.
Результат зависит от порядка применения, хотя команда остановки должна иметь приоритет над командой хаотичного движения.
Ввести ранг команды можно, но тогда придется держать в голове связи каждой команды с каждой, сверяя при добавлении нового бонуса его ранг с остальными.
NTFS
0
Нет. Если у тебя в один проход применяются команды, а в другой — выполняется одно действие. Ну и ту же полную остановку можно сочетать с хаотичным движением, если считать эффективную скорость отдельно, и направление отдельно.
badunius
0
Можно и так, да.
NTFS
0
У тебя даже ускорение пчелы с полной остановкой не будет конфликтовать, если остановка будет скорость×0, а ускорение — скорость×2
badunius
0
Минус — в один прекрасный момент, действие двух бонусов окажется конфликтным и конвеер команд разрушится.

Любая программа должна быть детерминированной, ещё до своего написания.
Где-то должен существовать документ, в котором должны быть прописаны исходы всех конфликтов бонусов — один отменяет другой, или применяется какой-то третий бонус. Или ещё что.
Далее делаем Вариант 2, но вместо СделатьВсё() вызываем ПреобразоватьСписокАктивныхБонусовТакЧтобыНеБылоКонфликтующихИспользуяЗнанияИзДиздока().
А потом для этого красивого списка бонусов вызываем СделатьВсё().
GadS
0
*текст не вычитывался, и не будет :(
GadS
0
в котором должны быть прописаны исходы всех конфликтов бонусов

Звучит хорошо, но уже при 20 бонусах мы получаем огромный клубок зависимостей, и попытка добавить новый бонус приведет к необходимости пересмотреть всю архитектуру.
С одной стороны, это неплохо, с другой — разработчик бонусов будет страдать.
NTFS
0
Меня несколько смущает, что бонусы не продуманы заранее и не согласованы как с «командой», так и между собой.
KaskeT
0
«Таков путь»©
NTFS
0
разработчик бонусов будет страдать

Это работа геймдизайнера, ему за это платят. За что-то же им платят ведь.
GadS
+1
Любая программа должна быть детерминированной, ещё до своего написания.

Было бы, конечно, очень круто, но нет, это так работает только с очень простыми программками. Любое серьезное приложение будет постоянно модифицироваться, а супер-серьезное (вроде достаточно сложной игры) еще и практически невозможно прописать досконально, учтя все возможные варианты.
Ginger_Strings
0
В моем понимании бонус — это полноценный игровой объект, который имеет доступ к другим объектам, и который может модифицировать любые данные этого объекта. Такой подход может быть излишним, если бонус очень простой, например множитель урона, но если бонусы принципиально вносят что-то новое в геймплей — то кодинг «в лоб» может стать очень проблемным, будет куча ифов и это будет неизбежно приводить к багам при сочетании этих бонусов.
Sasha-Flyer
0
будет куча ифов

В том и проблема. В идеальной архитектуре, if должен быть только для ветвления «да/нет», остальное должно решаться полиморфизмом и виртуальными методами.
NTFS
0
Если еще конкретнее — то на каждое игровое событие должна быть возможность подписаться, чтобы изменить любые параметры этого события. И бонусы — это подписчики на эти события. По крайней мере так я делал свою карточную игру
Sasha-Flyer
0
Проблема перекрытия двух конфликтных бонусов от этого не исчезает, тем не менее.
GadS
0
Насчет конфликтных бонусов ничего не могу сказать, с таким не встречался, но я сталкивался с рекурсивными проблемами, когда бонус вызывал сам себя до бесконечности. Но это было в основном из-за моей неопытности, так как я первый раз делал «магические функции».
Что ты подразумеваешь под конфлитным бонусом? Типа, один увеличивает лечение цели на 100%, а второй запрещает цели лечиться вообще? Ну, тут логика очень простая — какой бонус последним применяет свой эффект, тот и победитель.
Sasha-Flyer
0
какой бонус последним применяет свой эффект, тот и победитель

В большинстве реализаций систем событий получаем гонку, то есть результат будет зависеть от чистого рандома. А такого не должно быть.
GadS
0
«гонка» может быть, если эти события обрабатываются в разных потоках, что недопустимо при вычислении строгой логики.
Sasha-Flyer
0
Ты можешь поручиться за порядок выполнения двух функций, повешенных на одно и то же событие?
Всегда ли этот порядок будет сохраняться?
Будет ли первой функция А или функция Б?
Не обязательно нужна многопоточность, чтобы сымитировать гонку.
GadS
0
А в чем эта проблема вообще состоит?
Ginger_Strings
0
Хороший пример:
Типа, один увеличивает лечение цели на 100%, а второй запрещает цели лечиться вообще?

Ещё пример: одновременное применение бонуса, ограничивающего движение определённой скоростью, и бонуса, увеличивающего скорость.
GadS
0
Лечение, кстати, вроде бы разруливается: если представить степень лечения в виде множителя (по умолчанию 1), то один бонус добавляет множитель 2, второй бонус добавляет множитель 0, в сумме умножении всех множителей получается вполне логичный ноль, как бонусы ни крути) Со скоростью аналогично

Наглядное представление множителей от Образовача
andreymal
0
Изменён автором
Как будет в этой системе выглядеть несуществующий гигантский галаго?
А гигантский несуществующий галаго?

И самое главное, этого ли хотел автор? Ведь звуковик мог, например, повесить на всех гигантских существ звук грохота при ходьбе. И потом игрок с удивлением обнаруживает подгрохочивающего к нему галаго, который ему даже до пупка не дотягивает.
GadS
+1
Грохот сделать пропорциональным множителю гигантскости, а про несуществование не понял
andreymal
0
Имеется ввиду настолько маленький, что даже невидимый.
Но наверно в обоих случаях будет просто очень маленьким.
GadS
0
Ну да, но это какой-то совсем странный бонус)
andreymal
0
Я сам себя перемудрил, да. :)
GadS
0
Если появится пара бонусов на уровне "+20 к скорости" и «скорость х1.5», то тут все равно придется создавать какие-нибудь внешние поля-рычаги (отдельно множители, отдельно просто добавления), чтобы гарантировать одинаковый порядок применения этих ребят независимо от того, как они лягут в очередь. Бонус «скорость не более 40» — это уже третий рычаг.
StaSyaN
0
Зачем такие сложности? Просто берешь формулу типа «min (X, [скорость] * Y + Й)» и при проходе списка собираешь модификаторы X, Y и Й.
Ginger_Strings
0
Изменён автором
Вот ты то же самое написал. В любом случае тебе нужно где-то протаскивать все модификаторы через всю очередь эффектов, и если таких характеристик больше одной…
StaSyaN
0
Нет. Ты проходишь очередь, отдельно суммируешь "+X", отдельно "×Y", а потом применяешь их за одну операцию.
badunius
0
Детали конкретной реализации. Можно эти отдельные суммирования на процесс подсчета хранить как поле объекта, у которого в конце считается финальная характеристика (что я и написал), либо вообще отдельно как-то таскать.
StaSyaN
0
Понял. Это разрешается таким образом:

У каждого объекта есть компонент наложенных эффектов, который представляет из себя, собственно, список наложенных эффектов. Когда игрок жрет бонус, в этот список добавляется новый эффект.

В самих эффектах логики нет. Там есть тип эффекта и его значение, например, «скорость +2» или «скорость = 0».

Логика же есть в менеджере эффектов. Который каждый такт проходит по списку эффектов, применяет их к объекту, выкидывает прошедшие и т.д. Мы просто берем и перед этим проходом добавляем еще один проход, который разбирает такие конфликты.

Как именно — это уже творческое решение. Например, при наличии эффекта, который приравнивает скорость к нулю, остальные эффекты на скорость не учитываются. Или же скорость сначала становится равна нулю, а затем к ней применяются остальные эффекты на скорость. И т.д.

Но суть в том, что решать эту проблему должны не сами эффекты, а их менеджер, который по своей сути видит их все и создан, чтобы ими управлять.
Ginger_Strings
0
Изменён автором
Логика же есть в менеджере эффектов. Который каждый такт проходит по списку эффектов, применяет их к объекту, выкидывает прошедшие и т.д. Мы просто берем и перед этим проходом добавляем еще один проход, который разбирает такие конфликты.

Да, примерно похожее я и предложил:
но вместо СделатьВсё() вызываем ПреобразоватьСписокАктивныхБонусовТакЧтобыНеБылоКонфликтующихИспользуяЗнанияИзДиздока().
А потом для этого красивого списка бонусов вызываем СделатьВсё().
GadS
0
Но суть в том, что решать эту проблему должны не сами эффекты, а их менеджер, который по своей сути видит их все и создан, чтобы ими управлять.

То есть, допиленный третий вариант из моего поста?
NTFS
0
Ну да. Я ж говорю, по сути ты изобрел компонентную архитектуру, то есть, базовый способ решения таких проблем. Если на С++ пишешь, кстати, могу посоветовать либу EntityX, работал с ней для реализации игрули в свое время.
Ginger_Strings
0
полноценный игровой объект, который имеет доступ к другим объектам, и который может модифицировать любые данные этого объекта.

NTFS, не делай так. Очень велик риск заблудиться в своем же коде через пару недель.
StaSyaN
0
не делай так

И не собирался. Если одному объекту нужен полный доступ к другому объекту (за исключением вопросов сериализации) — то пора пересматривать архитектуру.
NTFS
0
Кстати, а какой смысл программно разделять пчелу и поньку?
Они вроде, как я понял, отличаются только внешним видом и интеллектом.
Так может стоит их сделать одним и тем же видом объектов?
Тогда связей, как минимум будет меньше.
GadS
+1
Пони управляется игроком, пчела автоматическая. Это совсем разные объекты.
NTFS
0
Пони:
«Движимая сущность» < — «Программный интерфейс управления сущностью» < — «Считыватель ввода игрока»
Пчела:
«Движимая сущность» < — «Программный интерфейс управления сущностью» < — «Искусственный интеллект»

Такое не прокатит?
GadS
+1
Игры, в которых можно в любой момент перехватить управление над ботами: ну да, ну да, пошли мы нахер
andreymal
+2
Никакого. Никто так и не делает.

У них у обоих есть некий контроллер поведения. Разница исключительно в том, что у пчелки это ИИ (то есть, набор скриптов), а у поньки это клава. Все, что нужно, это дать им однотипный элемент с функцией «начать движение», но у пчелки сделать внутри него обращение к скриптам, а у поньки — опрос клавиатуры.
Ginger_Strings
+1
Никакого. Никто так и не делает.

Ну давай посмотрим, что общего у пони и пчелы.
1) Система координат на экране [X;Y] (в мире, если игра не одноэкранная).
2) Система вывода (и то, спрайт уходит в графическую библиотеку, непосредственно в логике игры прямого обращения к графике/рендеру нет).
Всё? Всё.
Даже скорости там по-разному обрабатываются — у одной задаются точным значением, у другой вычисляются по синусам/косинусам, дабы преследовать пони.
NTFS
0
Изменён автором
А какая разница, как именно обрабатывается скорость? Это все еще логика обработки скорости, то есть, один и тот же компонент с разной начинкой. Ты же не заводитшь по объекту на все возможные сочетания координат? Так и тут нет никакого толка заводить по объекту на разные методы расчета скорости.
Ginger_Strings
+1
Пиши унифицировано для обоих. Пересечений намного больше, чем расхождений.
StaSyaN
0
Не вижу смысла. Если объекты игровые одного ранга (например, умеют обновляться по времени и сериализовываться) — то как правило, делается общий потомок GameObject с виртуалками Update(dt) и Serialize, а конкретная логика поведения уже кодируется в потомках.
Иначе есть риск передать пчелу как пони в какой-то метод и долго страдать потом.
NTFS
0
Пчела и пони по твоему описанию из поста — объекты игровые одного ранга. Общий предок у них обязан быть.

Иначе есть риск передать пчелу как пони в какой-то метод и долго страдать потом.

Мне не приходит в голову ни один сценарий, где эти сущности могли бы являться аргументом, и чтобы при этом было критично различие в том, что одна из них управляется игроком.
StaSyaN
0
Да, ты прав, такое маловероятно. Но я стараюсь делать разные классы, если объекты делают разное.
NTFS
0
По-моему, их оправдано вести от одного общего корня. Не вижу никаких причин, чтобы на тех же пчел нельзя было накладывать эффекты.
StaSyaN
0
К — Композиция
badunius
0
Даже скорости там по-разному обрабатываются — у одной задаются точным значением, у другой вычисляются по синусам/косинусам, дабы преследовать пони.
Ну вообще-то это единственное различие: что скорость в каждый момент у пони задаётся пользователем, а у пчелы — в зависимости от направления вектора на пони.
makise_homura
0
Ух ты, чел! Да ты взял… и изобрел компонентную архитектуру, лол. Ну, почти. Причем мне память подсказывает, что я тебе вроде на одном из игроконкурсов даже о ней говорил. А ты спорил, что вроде че-то не то.

Если надо, могу описать, как внятно делается ее реализация.
Ginger_Strings
0
Изменён автором
Я знаю, что такое компонентная архитектура и вот как раз по поводу данного проекта над ней раздумываю.
Но спасибо, что напомнил.
NTFS
0
Я бы не раздумывал. Она ж как раз создана, чтобы решать подобные проблемы. Более-менее сложные игры не через компоненты уже никто и не пишет, насколько я знаю.
Ginger_Strings
0
Что-то у меня в голове всплыла одна модель… Быть может, бред седой кобылы. Есть там пачка сложностей, но может сработать.

А если… Сделать объекты пони и пчела, так сказать, мета-объектами. Суть: это контейнер + локальная система сообщений + менеджер бонусов. Контейнер хранит простые объекты-куски, отвечающие за одну простую задачу каждый (обработка кнопок, движение, таймеры, звук, отрисовка, ещё что-то), система сообщений связывает их (нажал кнопку — обработчик объекта «пони» сгенерировал сообщение — оно перехватилось заинтересованными объектами-кусками — есть реакция). При взятии бонуса дефолтный (или предыдущий) объект-кусок, на который влияет бонус, на лету подменяется способным реализовать этот бонус. Менеджер следит за временем действия бонуса, производит переключения в своём объекте и обрабатывает коллизии/конфликты бонусов. Влияние на другой объект (пони на пчелу и наоборот) можно провести через систему сообщений, дав ей возможность межобъектного общения.

Преимущество, как мне видится в разделении реализации. Отдельно — основной класс, отдельно — объекты-куски, дающие эффекты, отдельно — обработка взаимодействия бонусов. Минус — в многослойной структуре, которую ещё надо реализовать и отладить.
MexoOne
0
Да ничего многослойного там не будет. Откуда? Будет список объектов, у каждого из них — список компонентов. Для каждого типа компонентов свой менеджер, который работает именно с ними. Откуда сложности-то? Легко и просто все.
Ginger_Strings
0
Именно такого я пока не делал.
Просто видится взаимодействие таких мета-объектов на нескольких уровнях. Привычное с движком и через движок, внутреннее между компонентами, горизонтальное между системами сообщений разных объектов, горизонтальное между менеджерами бонусов и компонентов (возможно туннелем через систему сообщений, но всё же). Как бы не наткнуться в один прекрасный момент на загадку, какая из черепашек пиздит.
MexoOne
0
А в чем состоит загадка? Понимаешь, эта структура так по своей сути устроена, что отдельные системы друг с другом не взаимодействуют. Работают они только с данными массива объектов, причем, последовательно — сначала делает целиком свою задачу одна, потом другая. Конечно, там в некоторых случаях может понадобиться передача сообщений между системами, но ее тоже реализует отдельная система, которая все рассылает к началу следующего такта.

Иными словами, системы не могут работать одновременно. Они работают последовательно, и в этом, собственно, заключены простота и изящество конструкции. Логировать ее тоже очень легко, ты сможешь сразу понять, какая именно система выдает проблему и на основе каких данных.

В общем, там нужно некоторое время, чтобы впилить в концепт, но он реально значительно проще, чем типичное ООП.
Ginger_Strings
0
Введение дополнительных объектов-менеджеров, которые управляют пони и пчелой, но каждый только в рамках своего типа действия. Один менеджер по движению, один по взаимодействию с пчелой, один для отрисовки и т.д. При этом, каждый бонус закрепляется за одним или нескольким менеджерами (например, бонус «Заморозка пчелы» обращается к менеджеру по движению (меняет скорость) и по отрисовке (добавляет лёд)) — и обращается не к пчеле или к пони, а к менеджерам, а те уже работают со своими объектами.

В каком-нибудь Unity делалось бы именно так, только эти компоненты были бы не в менеджерах, а в самих управляемых объектах, и они обращались бы к общему состоянию игры, чтобы понять, какие бонусы сейчас действуют.
Минус — обращение к состоянию игры происходит каждый кадр (ну, если не мудрить с кешированием), плюсы — код тупой как валенок и его не очень много.
RikkyTikkyTavy
0
обращение к состоянию игры происходит каждый кадр

Оптимизация на данном этапе меня не беспокоит, при гигагерцовых процессорах и 4-8 Гб ОЗУ на типовом ПК можно делать как есть.
NTFS
0
Вородор так и не понял почему бы не сделать пони и пчелу подклассами одного бонусожрущего объекта?
Эх, не бывать мне программистом.
vorodor
+1
Изменён автором
Это не проблема, проблема в том, как понизить число связей в архитектуре, когда число типов бонусов перевалит за полсотни.
NTFS
0
Изменён автором
А куда ещё понижать если бонусы обращаются к общему родительскому классу? Меньше чем бонусов их всё равно не станет.
vorodor
0
Изменён автором
Связи «потомок-предок» — то ладно, задача в уменьшении горизонтальных связей. Если берем бонус бессмертия, то в лобовом решении для пони нужно добавить метод setImmortal(), а в бонусе — вызов метода pony.setImmortal() и опа — уже бонус и пони прибиты друг к другу гвоздями.
NTFS
0
А нафига тебе родительский класс? Прибей метод к нему, тогда он будет сразу применяться хоть к пони, хоть к пчеле, хоть к поньмедведосвину.
vorodor
0
Потому что действия бонусов для пчелы и пони могут отличаться. Или быть неприменимы.
NTFS
0
Тогда что тебя не устраивает? Если эффект разный, то его так и так придётся отдельно прописывать.
vorodor
0
Изменён автором
Отсутствие личной гостиницы в Сочи и дома в Туапсе общая кривизна лобового решения.
NTFS
0
Так а в чём кривизна? Чем больше бонусов, тем больше взаимодействий, тут ничего не попишешь.
vorodor
0
Да, но хотя бы сделать, чтобы пони не зависело разом от всех типов бонусов.
NTFS
0
Я не понял, ты хочешь что бы пони взаимодействовал с бонусами, но не взаимодействовал с ними? Что?
vorodor
0
Да, примерно так. Бонус в идеале не должен лезть напрямую к пони, он должен сказать в какой-то менеджер «давайте увеличим у пони булки в два раза», а уже менеджер должен решить, как эту задачу выполнить.
NTFS
0
Поздравляю, ты только что открыл функции, вот только они тоже будут обращаться к пони. Вообще, тебе внизу подробно расписали как структурируется работа скриптов и даже это настолько очевидно и элементарно, что даже весьма далёкий от программирования вородор считает это чем-то само собой разумеющимся. Алсо, если ты хочешь получить исчерпывающий ответ на свой вопрос, то и сам вопрос нужно формулировать настолько же подробно.
vorodor
0
Я ж не дурак, я понимаю, что никто мне UML-диаграмму не нарисует.
Просто собираю умные мысли в потоке.
NTFS
0
Изменён автором
как понизить число связей в архитектуре, когда число типов бонусов перевалит за полсотни.

Я бы на чем-то типа политик и их списков делал, которые в свою очередь в древовидные струкутры строятся.

для начала базовый список всех типов объектов что у тебя в игре могут быть — пони, пчелы, деревья, камни — все что хоть как-то может взаимодействовать. Потом такой же список для всех возможных их свойств (бонусы также свойства)- здоровье, мана, скорость, сила, вес, цвет… Потом начинаешь объединять в группы, какие тебе нужны, и эти группы привязывать к объектам. Скажем группа «базовая» имеет цвет и вес. От нее цепляем группу «пони» — добавляем здоровье, силу и скорость, еще выше группа «единороги» — добавляем ману. Потом если вдруг понадобитя уберкамень с маной — не вопрос, создаем группу «уберкамни», зацепленную от «базовая» + «мана»

Если любишь бдсм, можешь это прямо на дельфях/плюсах сделать… но православней сразу скриптвое что-то встроить…
KerHarrad
0
Что-то такое у меня в третьем варианте, да.

сразу скриптвое что-то встроить

Делаю UI на Qt+OpenGL, логику основных объектов на C++, а бонусы, конечно, будут жить в QtScript на JS-подобном языке. Это снимает часть проблем с расширением и поддержкой, но все равно вопрос, кто и что должен дергать, пока не решен.
NTFS
0
На этом этапе тебе нужно четко решить, сколько максимум бонусов ты можешь родить. Вот прямо сейчас начинай составлять список. Вообще все, что ты можешь придумать.

Потом гляди на этот список и думай, а вперлись ли тебе эти десятки человекочасов, когда ты будешь писать все вот эти обертки к расширениям, а потом еще раз писать и сами бонусы. Еще и на скриптовом языке (задай себе второй вопрос, будет ли их кто-то кроме тебя писать), который тоже должен что-то дергать.
StaSyaN
0
Изменён автором
К сожалению, решаю не я. Есть постановка задачи, по которой нужно исследование и практическая реализация.
В постановке — бонусов может быть over9000 и писать их может кто угодно.
В принципе, результат исследования в виде «это невозможно, пнх» — тоже принимается. Но хотелось бы решить задачу.
NTFS
0
Изменён автором
В принципе, результат исследования в виде «это невозможно, пнх» — тоже принимается.

Я обычно отвечаю «Это возможно, но вы не захотите столько ждать реализации».
Понятия не имею, как происходит внедрение скриптовых языков, и нужно ли как-то прописывать область видимости.
StaSyaN
0
, как происходит внедрение скриптовых языков
Если Lua — ваще легко и непринужденно, все просто как валенки… если Perl — придется пострадать, но терпимо. За Питон и Жабаскрипт не скажу, не юзал…
KerHarrad
0
За Питон и Жабаскрипт не скажу, не юзал…

В Qt уже встроен божественный механизм интеграции JavaScript, когда можно вызывать нативные методы как скриптовые.
NTFS
0
Потом начинаешь объединять в группы, какие тебе нужны, и эти группы привязывать к объектам.

Зачем эта камасутра? Проще тупо написать список комплектующих, а при старте программы считать из файла прототипы, где указано, какие из этих комплектующих и с какими параметрами будут у каждого типового объекта. Ну и потом при необходимости создать объект просто копировать прототип.

Вся вот эта группировка нужна при традиционном ООП. А с компонентами она тебе зачем?
Ginger_Strings
0
А, хотя одну причину для ее существования я нашел — когда надо всем объектам одного типа добавить новую характеристику, удобнее не в каждом менять вручную, а прототипировать от общего предка и добавлять только различия.
Ginger_Strings
0
Если тип у тебя будет определяться наличием компонента, то ты просто модифицируешь этот компонент. Точнее сказать трудно, потому что очень всё абстрактно.
badunius
0
А за пчелу играть можно будет?
Ertus
+2
Пока такой задачи не стоит.
NTFS
0
Но кстати, это тоже, по идее, было бы интересно.
(Вот и ещё один повод объединить классы пчелы и пони — а потом ещё так же можно будет туда ввести других акторов, там, не знаю, чейнджлингов, бризи, озабоченных баранов нет, лол, это из другой игры и т.п.)
makise_homura
0
Как это происходит в годоте:
1. Юниту инстанцируется узел с кодом эффекта
2. Всё

И да, юниты при этом имеют общую базу, которая слушает сигналы потомков, вроде «стой здесь»/«иди туда». Просто персонажу игрока выдаётся контроллер, который слушает пользовательский ввод, а ботам выдаются контроллеры, которые мониторят сцену.

В твоём случае бонусы похожи на арена-эффекты, но, поскольку действуют они избирательно, разумнее вешать инстанс бонуса на каждого юнита отдельно.
Реализовать их обработку можно, например, как цепочку мидлвар: каждая получает состояние и возвращает его в изменённом виде.
badunius
0
Как это происходит в годоте:
1. Юниту инстанцируется узел с кодом эффекта
2. Всё

А потом десяток кубических зебр насмерть вешают ваши древние ведроиды.
vorodor
+1
Изменён автором
А потом 2000 сферических пони с коллизиями и поиском пути в реальном времени ничего не вешают =/ Может, таки афедробрахия?
badunius
0
Изменён автором
Справедливости ради, вешают именно кубические зебры, а не их эффекты
StaSyaN
+1
Тут суть не в эффектах, а в гов.. особенностях движка.
vorodor
0
Ну да, ну да
badunius
0
Если кому-то интересно, вот видео, в котором клёво объяснена суть компонентной архитектуры, на который похож третий вариант архитектуры в посте. Первые минут семь видео можно пропустить, потому что там в основном информация, не относящаяся к делу.
gawk
0
Спасибо, изучу.
NTFS
0
Я, до сих пор живущий в веке С и указателей на функции, пожалуй, предложу сейчас крамольную, но на мой взгляд интересную мысль: сделать что-то, похожее на ZDoom-овский DECORATE.

То есть, как бы я делал: допустим, у пчелы есть методы Spawn (вызывается при создании пчелы), Pick (вызывается при встрече со съедобным бонусом), Chase (вызывается каждый тик и перемещает пчелу по конкретному алгоритму), Attack (вызывается при контакте хитбоксов пчелы и пони), Die (вызывается при уничтожении пчелы); у пони Spawn, Pick, Move (вызывается каждый тик и перемещает пони по командам с клавы), Die. С каждым стейтом связан упорядоченный список обработчиков (например, они отрисовывают их на экране, меняют их свойства — координаты, здоровье, статические характеристики — скорость там и т.п.). У каждого бонуса есть набор обработчиков на какие-либо из этих методов. Когда бонус берётся — обработчик метода Pick добавляет его в список (если, конечно, обработчик этого метода не отключен другим бонусом — например, бонус «IDCLIP» не позволяет подбирать другие бонусы — см. ниже) и удаляет конфликтующие с ним (у каждого бонуса должен быть список конфиликтующих). Причём обработчики бонусов могут влиять друг на друга в зависимости от порядка подбора.
Например, рассмотрим такие бонусы:
Bonus "Medicine"
{
    Pony::Move:
        health_next = nealth_now
    Bee::Chase:
        health_next = nealth_now
}
Bonus "Poison"
{
    Pony::Move:
        health_next = nealth_now - 1
    // just for fun: Bees are tolerant to this poison, so no Bee::Chase handler
}
Bonus "IDDQD"
{
    Conflicts = {"Poison"}
    *::Pick:
        if (pick_object == "Poison")
        {
            Fail_Pick(leave_on_ground = false)
        }
}
Bonus "IDCLIP"
{
    *::Pick:
        Fail_Pick(leave_on_ground = true)
}

Мы видим, что если сначала взять лекарство, а потом яд, то пони будет отравлен, т.к. обработчик из Poison будет вызываться после оного из Medicine; если же наоборот, то лекарство вылечит пони (или пчелу), поскольку обработчик из Medicine будет вызываться позже, и новое здоровье останется тем же, которым было. А вот взятие бонуса «IDDQD», во-первых, удалит весь яд из организма пони, т.к. он конфликтует с ним, а во-вторых, при попытке взять яд, будет обламывать это взятие (т.е. добавление обработчиков в списки, при этом всё равно убирая взятый яд с земли).
Можно добавить также к каждому бонусу параметр Duration, по истечении которого после Pick все его обработчики будут удаляться из списков.

P.S. возможно, это всё какой-то уже давно поименованный паттерн программирования, но я с ним встретился, когда писал моды для DOOM и не знаю его названия =)
makise_homura
0
^ выше в каментах еретики и джаббаскрипт ^
Инквизиция уже вызвана. Молитесь своим еретическим богам. Екстерминатус неизбежен.
Теперь о совершенно другом.
Есть КоньЛетающий (Конь) {
//Здесь поля коня, описывающие все его внутренние состояния, включая механизмы для
//фунциклирования бонусов. Например:
//Есть бонус «Суперскорость» — дающий РДэш-класс ускорение. Следовательно должно быть
//поле «множитель скорости», имеющее значение по умолчанию 1. Бонус влияющий на скорость
//увеличивает это поле при получении бонуса, и уменьшает на туже величину при снятии.

МножительСкорости: число;//Используется для расчета скорости движения персонажа
//TODO — перенести в Персонаж, чтоб Пчолы тоже могли использовать бонус СуперСкорость.

//Список действующих бонусов. Они добавляются в этот список при подборе и исключаются
//при снятии

СписокАктБонусов: СписокОбьектов;

}

Есть БонусСуперскорость(Бонус) {
Процедура АктивацияБонуса; МойХост.МножительСкорости++;
Процедура СнятиеБонуса; МойХост.МножительСкорости--;
}

p.s. Вопросы, ответы, жалобы и угрозы пишите на [email protected]
8a55
0
koniipcholy_DLC1.псевдокод
//И по сути, каждый введенный новый бонус требует помимо собственного кода действия бонуса, еще вносить исправления в код либо пчелы, либо пони (например, для

Есть Персонаж (Существо){...}
Есть Существо (ИгровойОбьект) {

Хиты: число;

Неуязвим: логическое;
Процедура ПолучитьУрон(аХитовСнято: число) {
Если Нет(Неуязвим) Тогда
Хиты:=Хиты-аХитовСнято; }

}

Есть БонусНеуязвимость(Бонус) {
Процедура АктивацияБонуса; { МойХост.Неуязвим:=Правда;}
Процедура Тик;{ МойХост.Неуязвим:=Правда; }
//исправление бага№2077100500 — при наложенном заклинании ПьянойБлакДжакВсеПох,
//снятие вызывает нарушение заклинания/бонуса
Процедура СнятиеБонуса; МойХост.Неуязвим:=Ложь;
}
8a55
0
ЯННП
badunius
0
Не очень удачный пример на тему — ООП рулит при пограммировании сложных систем.
8a55
0
Главное, правильно выбрать архитектуру — при неверном выборе, ООП превращает проект в страдания.
Примеры выше годные, но по сути, лобовое решение.
Потому что

МойХост.Неуязвим


требует наличия поля «Неузвим» в классе хоста. А его могло исходно и не быть — не предполагали, что объект может быть неуязвимым.
NTFS
0
Боюсь, ты хочешь невозможного. Бонусы могут как-то менять поведение объекта и некоторые его вещи, но определять что-то кардинально новое — те же самые конфликты возможного поведения ты в разумные сроки именно так, чтобы еще и бонусы отвечали только за себя, в принципе не сможешь решить.

МойХост.Неуязвим можно делать и не полем класса, но тебе нужно предусмотреть у самого Хоста, что возможно состояние параметров «защита» и еще чего-то там, чтобы урон не проходил совсем. То есть, чтобы бонус мог что-то сделать, тебе в любом случае нужно предусмотреть, чтобы Хост это в принципе мог осуществить. Грубо говоря, Хост все-таки должен уметь все. Единственное место, где решаются все конфликты, единственный клубок, лежащий в строго одном месте.

Это не значит, что бонусы беспомощны. Ты волен писать, например, произвольный ИИ в коде бонуса, тебе просто нужно предусмотреть у Хоста, чтобы «думать» за него мог кто-то другой.

Но если ты будешь разбрасывать реализации ровным слоем между Хостом и бонусами, готовься к тому, что разные сочетания этих бонусов (и разных Хостов, раз уж ты решил по-разному писать игрока и пчел) будут (обязательно будут, неважно, как ты все напишешь, мы так устроены) стрелять багами, и физический разброс кода между системами никак не упростит твою задачу.

Допустим, бонус, который дает неуязвимость и какой-нибудь бонус, который по какой-то причине может убить тебя вот прямо сейчас. Если все решается на уровне хоста, то мы элементарно либо не даем сдохнуть при неуязвимости, либо все-таки даем, либо еще что-то делаем. Если на уровне бонуса, то тебе нужно в каком-то из этих двух ребят добавлять проверку на его «противоположность». И это их только 2, а если 20? В каждый прописывать каждого? Чот такое. Антипаттерн прям.
StaSyaN
0
Изменён автором
Боюсь, ты хочешь невозможного.

— Я не могу в это поверить!
— Потому и не выходит у тебя.

Меня прельщает компонентная модель, которую рекламировал оратор выше, в принципе, она как раз это сможет решить. Если правильно применить, конечно.
NTFS
0
Ну компонентная часть (если я ее тоже правильно понял, это как менеджеры в твоем посте? Смотреть полуторачасовой видос честно впадлу) по сути просто разбивает клубок конфликтов (который ты в любом случае должен разбить хотя бы на функциональном уровне даже без менеджеров) на ускоспециализированные клубки поменьше. Тот момент, что факт наличия какой-то логики в бонусе все равно должен быть предусмотрен у того, с чем бонус будет тыкаться, не отменяет. Моя большая стена комментария все еще действует.
StaSyaN
0
Изменён автором
А вообще, не знаю, иди первого айзека декомпилируй (или TBoI WoTL), он на флэше работает, проблем не должно быть, даже скорее всего что-то читаемое получится (небось даже обратно соберется). Такая же история с овер900 бонусами неопределенного действия.
StaSyaN
0
Изменён автором
Оставить, нифига не даст, там AS2, ООП нет и в помине, бонусы просто по номерам и все захардкожены

Поле imba реально так и называлось
StaSyaN
0
//А его могло исходно и не быть — не предполагали, что объект может быть неуязвимым.
Компиляция завершиться с ошибкой.
8a55
0
Компиляции может и не быть — потому что бонусы могут добавлять не разработчики ядра, а левые люди, которым сказали «добавить бонус позеленения пони».
NTFS
0
требует наличия поля «Неузвим» в классе хоста. А его могло исходно и не быть — не предполагали, что объект может быть неуязвимым.

Технически нет, если делать подобные статусы добавляемым элементом.
vorodor
+1
Вот с этой штукой все знакомы?
8a55
0
//Процедура Тик;{ МойХост.Неуязвим:=Правда; }
// //исправление бага№2077100500 — при наложенном заклинании ПьянойБлакДжакВсеПох,
// //снятие вызывает нарушение заклинания/бонуса
— есличо это был контрпример, не делайте так никогда.
8a55
0
пчела следует за ней неутомимо с равной скоростью, как T-800
Это жжжжжжж MLP:CiM, только там наоборот.

Можно через командную консоль даже выползти за текстуры, наглядно видно будет, что радужная дрянь с целью затащить в своё карманное измерение только так и ползёт, и спасу от неё нет ;)
MiniRoboDancer
0
Ну а вообще, глядя на своё high-coupled-говноподелие с портянками условий — думаю, что кали распиливать его на плагины, то хуки совать! Как во вротпрессе. Да побольше. И конечно же, на каждый чих их не хватит и придётся совать новые хуки по слёзным просьбам плагиноделов, а как иначе :3

А жабистские извраты с ООП-паттернами здесь помогут примерно никак, если стоит цель запилить сколь-либо осмысленную базовую игру, а не очередной мегарасширяемый майнтест, где модами можно творить чо угодно, зато самой игры толком нет, бгг. Как единый™ обработчик команд поможет, какие команды он будет выполнять? Куда они должны вклиниваться, в каком порядке (это важно!)? Как разруливать конфликтные ситуации, когда один бонус должен изменять или корректировать поведение другого (самый цимес — не зная о его существовании!)? Да никак, хрен вам, а не красивая архитектура :P
MiniRoboDancer
0
Изменён автором
один бонус должен изменять или корректировать поведение другого (самый цимес — не зная о его существовании!)?
ваще изи.
badunius
0
Пример в студию :P
MiniRoboDancer
0
Добавить бонусам взаимодействие на уровне типов бонусов? Взаимодействие на основе самого факта наличия бонуса? Опосредованное взаимодействие на уровне изменения характеристик, затрагиваемых бонусом?
vorodor
0
Добавить бонусам взаимодействие на уровне типов бонусов?
Наследовать промежуточные классы бонусов? Допустим. Но как это поможет им взаимодействовать без доработок чужих бонусов?
Взаимодействие на основе самого факта наличия бонуса?
Не повлияет на чужой бонус.
Опосредованное взаимодействие на уровне изменения характеристик, затрагиваемых бонусом?
И как при этом не вляпаться в
АЪАЪАЪАЪАЪА
- Хорошо.  А  надоест ждать,  приходите ко  мне на  кухню!  -  ответила
Брюкман и  оставила Понеделькуса в  комнате.  Он  начал  разглядывать машину
желаний. Рычаг был поставлен на "ВКЛЮЧ.", и лампочка все время мигала.
     - Сдается  мне,  что  Пепперминт и  Субастик в  опасности.  Может,  они
угодили в  западню и  не могут теперь выбраться?  -  размышлял он.  -  А  не
пожелать ли,  чтобы они сейчас же вернулись домой? Да, конечно, пусть сейчас
же  вернутся!   Я  видел  третьего  дня,  как  работает  эта  машина.  Самое
благоразумное -  велеть ей  доставить их к  парадному.  А  не то хозяйка еще
что-нибудь заподозрит. Она ведь только что видела, что в комнате никого нет.
И  Понеделькус проговорил в  рупор:  -  Хочу,  чтобы  Пепперминт и  Субастик
очутились внизу у парадного входа!


     В  то же мгновение в хижине из пальмовых листьев на необитаемом острове
господин Пепперминт наклонился к Субастику и прошептал:
     - А теперь бежим отсюда,  пока нас не спасли и не доставили на корабль!
Хочу, чтобы мы с тобой очутились сейчас дома, в нашей комнате!..


     - Что  такое?   -  удивленно  воскликнул  Понеделькус,  когда  господин
Пепперминт и  Субастик появились вдруг  в  комнате.  -  Я  же  велел  машине
доставить вас к  парад...  -  слова застряли у  него в  горле.  Пепперминт и
Субастик снова исчезли.
     - Почему мы стоим у парадного входа?  Я же просил перенести нас прямо в
ком...  -  начал было Пепперминт, но не успел он договорить, как вдруг снова
очутился у себя в комнате.
     - ...ному  входу!  ...ному  входу!  -  в  полном замешательстве твердил
Понеделькус.  Но господин Пепперминт не успел ему ответить -  неведомая сила
тотчас же снова подхватила его и перенесла на улицу.
     - Что происходит? - крикнул он. - Почему мы опять...
     Но оба уже снова стояли в комнате рядом с Понеделькусом.
     - Синий  свет!  -  в  ужасе  вскрикнул  Субастик,  показывая на  машину
желаний. В лампочке слабо мерцал синий огонек. - Синий свет! Скорей хватайте
нас, а не то... Держите нас! Держите!..
     Но оба уже снова были внизу.
     - Что  все  это  значит?  -  испуганно  прокричал господин  Пепперминт,
Субастик не успел ответить, как они опять перенеслись в комнату.
     - Хватайте нас!  Держите!  - крикнул Субастик Понеделькусу. - Держите и
не отпускайте!
     Но  тот не  успел и  пальцем пошевельнуть,  как они снова перенеслись к
парадному.
     И  только  когда  их  швырнуло  в  комнату  снова,  Понеделькус схватил
Пепперминта за руку, а Субастик изо всех сил уцепился за Пепперминта.
     - Скорей выключайте машину! Скорей! Скорей! - вопил Субастик.
     Понеделькус протянул свободную руку к машине и выключил ее.  Субастик в
ужасе  уставился на  синий  огонек,  который замигал теперь  с  еще  большей
скоростью.
     - Слишком поздно! Синий свет! Синий свет! - в отчаянии кричал Субастик.
- Два желания борются теперь друг с  другом не на жизнь,  а  на смерть!  Кто
кого пересилит -  я машину или машина меня.  Кто проиграет,  пропал!  Крышка
тому!
     - Но почему крышка?  Что случилось?  - взволнованно спросил Пепперминт.
Ведь теперь,  когда Понеделькус выключил машину, они были в своей комнате, и
никто не должен был удерживать их за руки...
     - Два желания враз! - в изнеможении проговорил Субастик. - Два желания,
которые противоречат друг другу:  не можем же мы в одно и то же время быть и
в комнате, и у парадного. Кресло мне пододвиньте! Скорей! Мне совсем плохо!
     Пепперминт был вне себя от страха.
     - Какой ужас! - пролепетал он и, взяв Субастика на руки, бережно усадил
его в кресло. - Может, лекарство дать? Как тебе помочь?
     - Положи мне на лоб холодное полотенце!  - простонал Субастик. - Я весь
горю!
     - У него жар!  - воскликнул господин Пепперминт. - Он в самом деле весь
горит!
     - У  машины  тоже  жар!  -  закричал Понеделькус и  показал  на  машину
желаний.  Докрасна накалились в ней все провода. Одна за другой ослепительно
вспыхнули и перегорели все лампочки, и откуда-то изнутри повалил черный дым.
     - Да  брось ты  эту дурацкую машину!  -  крикнул Пепперминт.  О  ней он
теперь и думать не хотел.  - Скорей принеси мокрое полотенце! Не видишь, что
Субастику плохо?
     - Полотенце?  Сейчас! - задыхаясь от волнения, проговорил Понеделькус и
мигом вернулся назад с мокрым полотенцем в руках.
     Господин  Пепперминт сразу  же  положил  его  Субастику на  лоб,  и  от
полотенца пошел  пар.  И  тут  вдруг машина зашипела,  внутри у  нее  что-то
треснуло, синий огонек часто-часто замигал и погас.
     - Победа!  Машина  сгорела!  -  облегченно вздохнул Субастик и  лишился
чувств.
?

Не говоря уж о том, что влияние на поведение бонусов может выходить за рамки глобально видимых параметров.
MiniRoboDancer
0
Наследовать промежуточные классы бонусов? Допустим. Но как это поможет им взаимодействовать без доработок чужих бонусов?

Так пропиши их по умолчанию дурик.
Взаимодействие на основе самого факта наличия бонуса?
Не повлияет на чужой бонус.

На основе факта наличия того бонуса НА который влияют.
И как при этом не вляпаться в
АЪАЪАЪАЪАЪА

Ну, если руки из жопы растут, то тут уже никакая самая совершенная и продуманная архитектура не поможет.
vorodor
0
пропиши их
каво o_O
На основе факта наличия того бонуса НА который влияют.
Но вот как, собственно, влиять, не создавая связей между бонусами и не превращая архитектуру в лапшу?
MiniRoboDancer
0
каво o_O

Классы же, дурик!
Но вот как, собственно, влиять, не создавая связей между бонусами и не превращая архитектуру в лапшу?

Это специфический случай, который подходит для ситуаций когда принцип действия прочих бонусов не важен, например «сбросить все бонусы» или «увеличить/уменьшить время действия наложенных эффектов» в таком духе.
vorodor
0
Классы же, дурик!
Ну-ну. Есть классы BuffBonus, DebuffBonus и BellsNWhistlesBonus. От которого наследовать SwapBeeWithPonyBonus или MapRegenBonus?
когда принцип действия прочих бонусов не важен
Очень даже важен. Вот призывает один бонус пчёл-соратников, например, а другой бонус возьмёт да случайно их во враждебных превратит, ибо ассумит, что пчела одна и может быть только враждебной. Штоделотб?
MiniRoboDancer
0
Более того, если первый бонус в норме прихлопывает пчёл-соратников по таймауту, а другой бонус превратит их во враждебных, то после превращения во враждебных они больше сами не исчезнут (ибо первый бонус чистит пчёл с собою же выставленным кастомным флагом доброты, который испорчен вторым). И начнётся эпичный заруб.
MiniRoboDancer
+1
Изменён автором
И что самое печальное, такую фигню будет очень сложно поймать и отладить, ведь бонусов дохрена и выпадают они в произвольном порядке; надо, чтобы сойдеся обстоятельства: выпада первый и во время его действия выпада и применися второй.
MiniRoboDancer
+1
Ну, если руки из жопы растут, то тут уже никакая самая совершенная и продуманная архитектура не поможет.
vorodor
0
А как Мастера Гей-дева предлагают разрулить проблему в данном случае?
MiniRoboDancer
+1
Ты исходишь из того, что твоя интерпретация сочетаний бонусов единственная верная :^)

Спойлер
Вот призывает один бонус пчёл-соратников, например, а другой бонус возьмёт да случайно их во враждебных превратит, ибо ассумит, что пчела одна и может быть только враждебной.

Не вижу проблем.

Ибо первый бонус чистит пчёл с собою же выставленным кастомным флагом доброты, который испорчен вторым

Архитектура говнокод, первый бонус должен чистить конкретных пчел, ни на что не оглядываясь (кроме того случая, когда ему сказали «не чисти»).

Бонус1 даёт полную неуязвимость. Бонус2 снижает урон. Если активен Бонус1, то Бонус2 активировать бессмысленно, лучше приберечь. Как достичь этого, если Бонус1 и Бонус2 не знают друг о друге?

Не вижу ни одной причины, чтобы бонус 2 не работал во время бонуса 1, иначе балансно говно.


Уточняю: не стоит задача «сочетания бонусов должны вести себя строго конкретным образом». Стоит задача «сочетание бонусов должно вести себя одинаково независимо от того, в каком порядке они применены».
StaSyaN
+1
Изменён автором
Не вижу проблем
Ну да, внезапное повышение сложности, обычное дело :P А если это финальный уровень, до которого с трудом докарабкашася, то от таких глюков может и девайс в стенку полететь.
кроме того случая, когда ему сказали «не чисти»
Вот для этого, опять же, и надо взаимодействие между бонусами, и предусмотренные зацепки для изменения поведения извне. Более того, надо предусмотреть гибко, ибо может оказаться, например, что враждебных как раз оставлять надо — но сташа ими не все, а лишь те, что находятся в радиусе действия бонуса, например.
балансно говно
Почемяу? Если что тут и портит баланс, то сам факт наличия бонуса 1, но уж никак не возможность приберечь бонус 2.
«сочетание бонусов должно вести себя одинаково независимо от того, в каком порядке они применены»
А тут без взаимодействия возможна разве что возня с классами бонусов или приоритетами, на которую не все бонусы натянутся.
MiniRoboDancer
0
Ну да, внезапное повышение сложности, обычное дело :P А если это финальный уровень, до которого с трудом докарабкашася, то от таких глюков может и девайс в стенку полететь.

Это нормально для игр с сотнями бонусов. Разнообразное поведение с интересными ситуациями — то, что от них ожидается.
Если что тут и портит баланс, то сам факт наличия бонуса 1, но уж никак не возможность приберечь бонус 2.

Интерпретируй это, как удвоение длительности бонуса 2. Что рискует в некоторых условиях сделать его бесконечным. Но даже если и надо — ниже уже написали, как.
А тут без взаимодействия возможна разве что возня с классами бонусов или приоритетами, на которую не все бонусы натянутся.

А ты думал, что в сказку попал?) Нет, конкретное взаимодействие между конкретными бонусами должно быть только в рамках исключения, система должна быть создана так, чтобы суметь 99% разрулить сама. Иначе ты будешь все время прописывать тысячи сценариев.
StaSyaN
0
Изменён автором
интересными ситуациями
Угу, если ассумить, что все глюки интересные вне зависимости от их вредности.
удвоение длительности бонуса 2
Само оно так может не сработать, опять же.
чтобы суметь 99% разрулить сама
Угу, только если архитектура строгая и бонусы друг к другу лезть не могут, то 1% обломается. А если не строгая, то взаимодействием будут злоупотреблять. Какие уж тут сказки :/
MiniRoboDancer
0
Угу, если ассумить, что все глюки интересные вне зависимости от их вредности.

Глюки — отклонения от исходного плана.
Само оно так может не сработать, опять же.

Я тебе баланс поясняю, а не за логику.
Угу, только если архитектура строгая и бонусы друг к другу лезть не могут, то 1% обломается. А если не строгая, то взаимодействием будут злоупотреблять.

Ага, и в моем саааааааамом первом комментарии уже дана архитектура, которая позволит тебе чекать другие эффекты, но при этом не позволит манипулировать ими. Ваще хз, что вы тут обсуждаете.
StaSyaN
0
Изменён автором
Глюки — отклонения от исходного плана
Которые могут вообще сломать игру. Много радости?
в моем саааааааамом первом комментарии
в наружу вынести
Это если в стороннем бонусе вынесено. А если нет? Никто ж не заставляет. Запретить приватные данные? как? Ну и отталкиваться от них, опять же, костыльно и ненадёжно.
MiniRoboDancer
0
Которые могут вообще сломать игру. Много радости?

Спроси тех, кто в айзека играет.
Это если в стороннем бонусе вынесено. А если нет?

Если ты сам себе не хозяин, то я ничем не помогу.
StaSyaN
0
сам себе
Чавоу?

Вообще-то ещё в исходном условии задачи указано:
разработкой бонусов могут заниматься вообще другие люди
MiniRoboDancer
0
Ну тогда нужно пинать других людей.
StaSyaN
0
Угу, что нетривиально, если они вообще из других контор.
MiniRoboDancer
0
Ну это уже вопрос организации другого уровня
StaSyaN
0
Отнюдь, ибо проблемы, нерешаемые на уровнях выше технического, переходят на технический :P
MiniRoboDancer
0
Если ты не сможешь сформулировать решение проблемы, то как ты его вообще реализовывать собрался?
StaSyaN
0
Я говорю о нерешимости в рамках чёткой ограниченной архитектуры, о которой весь тред. Если можно манкипатчить чужие бонусы, то проблемы нет :P
MiniRoboDancer
0
Вородор не намерен лезть в ваши гейские дела, а по поводу бонусов я бы предложил не маяться хернёй, обсуждая влажные фантазии, а заняться уже внятной реализацией, благо насоветовали тут более чем достаточно.
vorodor
0
Давай конкретику.
badunius
0
Бонус1 даёт полную неуязвимость. Бонус2 снижает урон. Если активен Бонус1, то Бонус2 активировать бессмысленно, лучше приберечь. Как достичь этого, если Бонус1 и Бонус2 не знают друг о друге?
MiniRoboDancer
0
Плохой пример. Почему:
1. Это относится к активации бонуса, а не к его работе
2. Нет формализации того, как бонусы живут и истекают

Но допустим.
Рассмотрим кейс в рамках реализации через цепочку мидлвар.
Напомню, реализация заключается в том, что у каждого нита в игре есть состояние и список активных бонусов.
На каждом кадре логики (физики) берётся текущее состояние (которое включает направление движения, скорость и пр. параметры) и последовательно скармливается методу (например) `process` бонуса. Метод принимает состояние и возвращает его модифицированный вариант (в соответствии со своей логикой). После чего новое состояние скармливается следующему бонусу. Итоговое состояние применяется к юниту.
Истекание бонусов реализуем как их внутренннее состояние. То есть бонус знает. на сколько кадров его осталось и при каждой активации может уменьшить свой срок жизни или нет. Как удаляются истёкшие бонусы — не рассматриваем.

Итак, что мы имеем. Оба бонуса модифицируют damage resistance. Один присваивает ему значение 1, другой, какое-то значение меньше 1, но больше 0.
Значит, в логику таких бонусов следует добавить проверку: если DR, который он даёт, меньше того, которое уже есть, значит нужно вернуть не модифицированное состояние и не уменьшать свой срок жизни.
badunius
+1
Это относится к активации бонуса, а не к его работе
Будто активацию в архитектуре учитывать не надо.
если DR, который он даёт, меньше того, которое уже есть, значит нужно вернуть не модифицированное состояние и не уменьшать свой срок жизни
Ориентироваться на публичные параметры, затрагиваемые бонусом, вместо внутреннего состояния бонуса — опасный костыль.

Что, если, например, Бонус2 существует в двух видах: один даёт DR=0.7, другой DR=0.3? Объединить их имеет смысл, при этом если первый вид уже активен, то DR будет выше порога.

Также Бонус1 может иметь спадающий эффект, и в таком случае нельзя точно определить по DR, активен он или нет — начальное значение знает только Бонус1. Также одновременное применение такого спадающего бонуса может привести к вышеописанному АЪАЪАЪАЪА, если бонусы меняют DR абсолютным образом, и к зашкаливанию, если относительным.
MiniRoboDancer
0
Объединить их имеет смысл
Нет. Но если хочется, то да. Есть стакающиеся эффекты, есть не стакающиеся.
badunius
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.
Скрыто Показать