Войти на сайт

Авторизация, ждите ...
×
  • Страница:
  • 1
  • 2

ТЕМА: ООП на ГеймМейкере

ООП на ГеймМейкере 5 года 8 мес. назад #93865

  • Xitilon
  • Xitilon аватар
  • Вне сайта
  • Познающий
  • Сообщений: 22
  • Спасибо получено: 32
EvilCat пишет:
Это, конечно, хорошо - говорить, что неправильный выбор типа - ошибка на уровне проектирования. Но почти никогда не бывает так, что ставишь точку в проектировании, а затем начинаешь разрабатывать собственно игру.
Ну, с самим фактом, что в одних языках нужно выбирать типы, а в других вообще не нужно, и это экономит время и силы, я не спорю - так и есть. Про итеративную разработку знаю, сам так и делаю.
EvilCat пишет:
Этот паттерн называется "God Object", то есть объект, который знает слишком многое, и это антипаттерн. Фактически это выбрасывание всего ООП в окно.
Не совсем. Я имею в виду всего лишь "God Enemy Object", а не что-то такое, что заведует сразу огромной частью игры, самой разной "компетенции" и назначения.

Кажется, то что я имел в виду, называют Декоратором.
EvilCat пишет:
Подобные вещи начинают требоваться, когда в одном месте нужен стреляющий летающий враг, в другом - хилящий летающий, а втретьем - хилящий стреляющий неподвижный. Ты как бы начинаешь думать, что враг должен уметь и стрелять, и хилить, и летать, просто некоторые враги решают этого не делать. Но решать это обычно следует композицией объектов, как, например, и делает Юнити, и даже в GM это можно сделать (собственно, в таком проекте мне и захотелось полифорфизма в GM).
Полиморфизм это всего лишь полиморфизм, он в ГМ реализуется с помощью event_inherited() в нужных местах (вызов унаследованного действия события того же типа и подтипа), а в данном случае мы вроде как про множественное наследование.

В GML можно выполнять событие любого объекта из любого другого объекта с помощью event_object_perform():
docs.yoyogames.com/source/dadiospice/002..._perform_object.html

И в этом случае не нужно будет наследовать вообще ни одного класса, кроме какого-нибудь условно выбранного самого главного. Просто выполнять события, хранящие поведения в ряде объектов, описанных как бы просто как хранители поведений. Типа:

if healing
event_perform_object(o_healing, ev_step, 0)

В GM8.1 вообще был execute_string(), это как eval() в JS, но в GM:Studio его уже нет, да и это более халтурный способ в любом случае.

Да, можно сказать что таким образом нарушается сокрытие реализации, но эту проблему надо решать строгими указаниями, что и куда подключать, и наличием документации по разработке проекта.
Последнее редактирование: 5 года 8 мес. назад от Xitilon.
Администратор запретил публиковать записи гостям.

ООП на ГеймМейкере 5 года 8 мес. назад #93866

  • Amphilohiy
  • Amphilohiy аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 547
  • Спасибо получено: 665
  • Программист Ruby2 место ГотвОраторУчительПобедитель Сбитой кодировки
Кажется, то что я имел в виду, называют Декоратором.
Скорее состояние (автомат). Так же рекомендуется ознакомится с стратегия. А декорация это обвертка функции. Похожим механизмом пользуются скриптеры мукера (тот же alias в руби), только из-за некоторых тонкостей не приходится делать отдельный класс для этого.
И в этом случае не нужно будет наследовать вообще ни одного класса, кроме какого-нибудь условно выбранного самого главного.
А есть ЯП с статическим типизированием, которому не надо наследоваться - go. Там и наследования как такового нет. Ты просто расписываешь интерфейс в качестве аргумента (или используешь заранее описанный интерфейс), и ошибка будет выдана только если объект не соответствует интерфейсу. Магия рефлексии.
Я верю, что иногда компьютер сбоит, и он выдает неожиданные результаты, но остальные 100% случаев это чья-то криворукость.
Последнее редактирование: 5 года 8 мес. назад от Amphilohiy.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Xitilon

ООП на ГеймМейкере 5 года 8 мес. назад #93868

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • 2 место 3 место ГотвУчитель
Полиморфизм это всего лишь полиморфизм, он в ГМ реализуется с помощью event_inherited() в нужных местах (вызов унаследованного действия события того же типа и подтипа), а в данном случае мы вроде как про множественное наследование.

В GML можно выполнять событие любого объекта из любого другого объекта с помощью event_object_perform():
docs.yoyogames.com/source/dadiospice/002..._perform_object.html

И в этом случае не нужно будет наследовать вообще ни одного класса, кроме какого-нибудь условно выбранного самого главного. Просто выполнять события, хранящие поведения в ряде объектов, описанных как бы просто как хранители поведений. Типа:

if healing
event_perform_object(o_healing, ev_step, 0)

Прошу прощения, я совсем не поняла, как это должно работать и какие проблемы решать.

Вызов одного из стандартных событий, которые есть у каждого объекта? Это полиморфизм, конечно, но не тот, который позволяет воспользоваться хорошими сторонами ООП.

Например, есть персонаж (объект), который может взять в руку оружие (объект). Он может взять любое оружие и нажать крючок, но каждое оружие делает своё: одно стреляет пулей, другое показывает флажок с надписью "Бах", третье лечит самого персонажа. Логично в таком деле сделать каждый тип оружия потомком класса "Оружие" (или реализатором интерфейса "Крючок") с методом "нажать", а оружие уж пусть само разбирается.

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

Договариваться "слушай, короче, нажатие крючка - это будет по событию ev_step)" - нецелевое применение системы. Представь ситуацию, что у нас есть несколько типов пистолетов, каждый из которых выпускает пулю, но также делает кое-что ещё - огнестрельный бахает, цифровой пишет в лог, шпионский выпускает также пулю в обратном направлении в не ожидающего врага... А один безумный пистолет вместо пули выстреливает другим пистолетом случайным пистолетом, который тут же тоже выстреливает. А ещё один срабатывает, только если с нажатием крючка крикнуть "Огонь!". Это целая куча поведений, часть из которых наследуется от предыдущего члена цепочки, часть - модифицирует сделанное предыдущим членом цепочки, а часть - добавляет что-то своё или даже переписывает произошедшее. Если бы мы договорились использовать строго одно событие, например, "пользовательское событие № 1" как вызов метода "нажать", нам бы быстро пришлось израсходовать весь запас пользовательских событий на наследование под-методов, которые мы бы просто применяли в нормальном ООП; или вписаться в копипасту.

Это не теоретические рассуждения - я писала экспериментальный прототип, когда столкнулась с этой проблемой и столкнулась с этими трудностями.
Администратор запретил публиковать записи гостям.

ООП на ГеймМейкере 5 года 8 мес. назад #93879

  • Xitilon
  • Xitilon аватар
  • Вне сайта
  • Познающий
  • Сообщений: 22
  • Спасибо получено: 32
Amphilohiy пишет:
Кажется, то что я имел в виду, называют Декоратором.
Скорее состояние (автомат). Так же рекомендуется ознакомится с стратегия. А декорация это обвертка функции. Похожим механизмом пользуются скриптеры мукера (тот же alias в руби), только из-за некоторых тонкостей не приходится делать отдельный класс для этого.

Не знаю, что есть alias, увы. А шаблон "Стратегия" тут не подходит, потому что количество классов, которые нужно описать, это комбинационный взрыв исходных компонентов. "Состояние" уже что-то более близкое, но для того чтобы выполнять по-компонентно все эти "частицы поведений", нужно иметь сразу много состояний, или постоянно их на месте переключать, что кажется мне странным. В общем, или я туплю, или это всё ещё другой паттерн, который решает что-то другое.

А про go - забавно, однако.
EvilCat пишет:
Вызов одного из стандартных событий, которые есть у каждого объекта? Это полиморфизм, конечно, но не тот, который позволяет воспользоваться хорошими сторонами ООП.
В данном случае прошу прощения уже я, но всё-таки - "вам ООП или игры прототипировать/делать"? Да, в Гамаке оно кривее чем хотелось бы, но это его слабое место, а сильное - в функциональной заточенности на 2Д-игры самых разных жанров. Для меня лично Гамак победил, потому что суммарная целесообразность использования этой хорошей IDE с этим... особенным языком - выше, чем у других испытанных мной средств для разработки игр - Scratch, Multimedia Fusion, Stencyl, Construct, тот самый Unity3D, и там ещё что-то, чего уже и не упомню. Графическое программирование из блоков пока что доказало свою несостоятельность в таком виде, в каком оно есть, а из попыток гибридных систем блоки+код Гамак опять же самая удачная. Хотя эти блоки я давно не использую, опять же.
(весь этот абзац о том, что я не думаю о степени ООПшности, и это нормально, пока я достигаю поставленной цели)
EvilCat пишет:
Например, есть персонаж (объект), который может взять в руку оружие (объект). Он может взять любое оружие и нажать крючок, но каждое оружие делает своё: одно стреляет пулей, другое показывает флажок с надписью "Бах", третье лечит самого персонажа. Логично в таком деле сделать каждый тип оружия потомком класса "Оружие" (или реализатором интерфейса "Крючок") с методом "нажать", а оружие уж пусть само разбирается.
Логично, согласен. Хотя оружие, которое лечит, немного смущает - эта штука, если и является оружием, то лечение должна производить, скажем, имплементацией интерфейса IHealingItem, или что-то в таком духе. Тот факт, что оружие само должно разбираться (как и все остальные объекты) - неоспорим, но я против него и не шёл в предложенном выше способе раскладывания поведений по объектам - предполагается, что все поведения известны ровно тем объектам, которым они конечном счёте нужны, причём часть этих поведений используется составными объектами, методом вызова событий из "менее составных".
EvilCat пишет:
Делать из оружия божественный класс (автомат, стратегию?) "если я пистолет, то... если я клоунский пистолет, то... если я шприц, то..." - адово, хотя, к примеру, в VBA (кстати, тоже статически типизированный язык с полиморфизмом, но без наследования) это иногда сделать проще и быстрее, чем что-либо ещё - для прототипа годится.
Итого, всё зависит от задачи. Если отличие каждого типа пистолета описывается парой строчек, а пистолетов десятки, то это проще потом поддерживать, чем если создать кучу наследников-пере-до-определителей.
EvilCat пишет:
Договариваться "слушай, короче, нажатие крючка - это будет по событию ev_step)" - нецелевое применение системы. Представь ситуацию, что у нас есть несколько типов пистолетов, каждый из которых выпускает пулю, но также делает кое-что ещё - огнестрельный бахает, цифровой пишет в лог, шпионский выпускает также пулю в обратном направлении в не ожидающего врага... А один безумный пистолет вместо пули выстреливает другим пистолетом случайным пистолетом, который тут же тоже выстреливает. А ещё один срабатывает, только если с нажатием крючка крикнуть "Огонь!". Это целая куча поведений, часть из которых наследуется от предыдущего члена цепочки, часть - модифицирует сделанное предыдущим членом цепочки, а часть - добавляет что-то своё или даже переписывает произошедшее. Если бы мы договорились использовать строго одно событие, например, "пользовательское событие № 1" как вызов метода "нажать", нам бы быстро пришлось израсходовать весь запас пользовательских событий на наследование под-методов, которые мы бы просто применяли в нормальном ООП; или вписаться в копипасту.
Мне уже нравится такая иерархия пистолетов! Ну хорошо, забыли то что выше, тогда вот что я предлагаю:

1) Описать иерархию классов:

Все поведения описаны только в User Event 0 каждого объекта.
НазваниеРодительСмысл
o_pistolНет"Абстрактный" (такого аттрибута в GML нет, это он по использованию по факту абстрактный). Он существует сугубо для того чтобы конструкция with o_pistol захватывала в контекст сразу всех его наследников.
o_pistol_firearmo_pistolОписано поведение выпускания пули.
o_pistol_boomo_pistol_firearmevent_inherited() запускает поведение по выпуску пули, а ещё делаем sound_play(...).
o_pistol_digitalo_pistolПишем в лог. Не понял, надо ли выпускать пулю - если да, то event_perform_object(o_pistol_firearm, ev_user, 0)
o_pistol_backfireo_pistol_firearmevent_inherited() запускает поведение по выпуску пули, и тут же добавляем второй выпуск, только назад.
o_pistol_pistolspawnero_pistolДелает p=instance_create(x, y, choose(список пистолетов либо вместо этого вызов скрипта выбора пистолетов)); with p event_user(0). Это создаёт пистолет и заставляет его выполнить его "главную функцию", которую мы своевольно приравняли к Пользовательскому Событию №0, ибо почему нет?

2) Выстреливание только если вместе с нажатием выкрикнуто "Огонь", реализовать через добавление в User Event 0 самого первого объекта o_pistol строку что-то вроде if works_with_shout && shout.

И кстати, я по ходу дольше расписывал, как бы я это сделал, чем я бы это по факту в ГМ сделал! А если прототип/игра работает, для чего же думать, "ровное" или "нормальное" ли там ООП?
EvilCat пишет:
Это не теоретические рассуждения - я писала экспериментальный прототип, когда столкнулась с этой проблемой и столкнулась с этими трудностями.
Радует, что говорю не с академико-теоретиками (как часто бывает). Не знаю как в VBA, а в GML всё-таки мне кажется не зазорным (не принадлежащим к классу плохих практик ООП) выполнять события других объектов-как-контейнеров-поведений, которые по факту во время выполнения программы никогда и не создаются, а служат чем-то вроде ячеек, куда ложат нужные поведения.
Последнее редактирование: 5 года 8 мес. назад от Xitilon.
Администратор запретил публиковать записи гостям.

ООП на ГеймМейкере 5 года 8 мес. назад #93880

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • 2 место 3 место ГотвУчитель
Не знаю как в VBA, а в GML всё-таки мне кажется не зазорным (не принадлежащим к классу плохих практик ООП) выполнять события других объектов-как-контейнеров-поведений, которые по факту во время выполнения программы никогда и не создаются, а служат чем-то вроде ячеек, куда ложат нужные поведения.

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

по сути мы обсуждаем решение проблемы, что в ГеймМейкере очень сложно сделать композицию (вложенность объектов) и невозможно приделать к объекту метод, вызываемый по имени (и, следовательно, нет нормальной работы с методами при наследовании). Даже в РПГ Мейкере это есть, слава Руби и Яваскрипту, хотя нет никакой визуализации этого. В той же Юнити мы бы просто не дошли бы до такого обсуждения, потому что в объект "игрок" будет вложен объект "оружие", а поведение "умеет обращаться с оружием" можно наложить на игрока, стрелковую башню, да хоть пулю у пистолета (как пули-мультяшки в "Кто подставил кролика Роджера" %)

Хуже всего, что необходимость этих подходов - не редкость в играх. В ролевых играх у персонажа есть экипировка и инвентарь, заклинания. В платформерах персонажи могут поднимать и применять предметы (Кирби, Йоши). В стратегиях у юнитов есть разные свойства, какие-то уникальные, какие-то встречающиеся у других войск. Есть игры с конструктором юнитов, космический кораблей или заклинаний. Есть игры со слотами (сокетами) у артефектов, куда можно вставить разные руны или драгоценные камни. Всё это легче всего сделать с привлечением ООП и композиции. И начинаются пляски с бубном...
Администратор запретил публиковать записи гостям.

ООП на ГеймМейкере 5 года 8 мес. назад #93912

  • Xitilon
  • Xitilon аватар
  • Вне сайта
  • Познающий
  • Сообщений: 22
  • Спасибо получено: 32
EvilCat пишет:
К сожалению, описанной здесь методики я тоже не поняла... Выполнить метод объекта, не создавая его? Почему бы просто не выполнить скрипт в таком случае?
Ну, так ведь более "по-ООПшному", нет? Кстати, разбирая исходники одного довольно известного Гейм Мейкериста, Даниэля Ремара, я увидел то, чего я увидеть никак не ожидал - все события у него переброшены в скрипты, то есть в событиях просто находится вызов скрипта, и в итоге вся игровая логика содержится в скриптах (а комбинации поведений - в их комбинациях). Вот и думай как хочешь после такого!

Про пляски с бубном конечно верно. Ну, я что мог, то пояснил. Сам знаю, что это всё странно смотрится и сложно представляется на практике. Но какая бы ни была цель, в Гамаке её скорее всего достичь можно, а недостатки языка перекрываются достоинствами самой - даже не среды разработки - системы сборки игры из ассетов и логики. Юнити действует похожим образом, и там это тоже хорошо. Но как-то он у меня не прижился, да и 3Д...
Администратор запретил публиковать записи гостям.

ООП на ГеймМейкере 5 года 8 мес. назад #93915

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • 2 место 3 место ГотвУчитель
Ну, так ведь более "по-ООПшному", нет?

Не получается, в духе "классы - избалованные неймспейсы", как говорят, когда класс используется просто как хранилище статических методов. (Что нормально в Руби, называется "модуль", но вынужденная мера в php, где механизм автозагрузки классов есть, а пространств имён - нет. Функций вроде только-только появился.)
Кстати, разбирая исходники одного довольно известного Гейм Мейкериста, Даниэля Ремара, я увидел то, чего я увидеть никак не ожидал - все события у него переброшены в скрипты, то есть в событиях просто находится вызов скрипта, и в итоге вся игровая логика содержится в скриптах (а комбинации поведений - в их комбинациях). Вот и думай как хочешь после такого!

И изданные игры на ГМ, код которых довелось посмотреть мне, тоже были сделаны так!

А Юнити сейчас и 2Д тоже.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Xitilon

ООП на ГеймМейкере 5 года 8 мес. назад #93917

  • Xitilon
  • Xitilon аватар
  • Вне сайта
  • Познающий
  • Сообщений: 22
  • Спасибо получено: 32
Избалованные неймспейсы, ха, я сразу понял о чём речь - именно так их использовали все (ну почти) мои одногруппники, когда "изучали" ООП. public static и пошло-поехало.

Даниэль Ремар тоже издавался, да. Короче, всё как-то не так, как надо.

Юнити я пробовал аккурат перед тем, как к его фреймворку добавили улучшенную поддержку 2Д из коробки. То есть, когда её добавили, я ещё не отошёл от прошлого опыта, и пробовать его повторно не хотелось, а потом как-то и вовсе забыл. Понятное дело, что это отмазка, куда ж без них! Что угодно, лишь бы игры не делать! Ну а так-то Гамака хватает с головой. Пока.
Последнее редактирование: 5 года 8 мес. назад от Xitilon.
Администратор запретил публиковать записи гостям.
  • Страница:
  • 1
  • 2
Время создания страницы: 0.228 секунд