Всё ещё учусь работать в Game Maker (GMS 1.4.9), параллельно участвуя в конкурсе на GCup.
1)Немного неприятно, то что разрешение экрана и FPS нужно менять в каждом уровне отдельно,
по логике это должно выноситься в опции и быть общим для всех уровней.
2)Также немного прокачал рисование персонажа для кастомизации, хотя возможно это всё будет лагать при 60 FPS на слабом ПК, я не уверен:
- 18 июня 2020, 17:21
- 04
Столкнулся со странной проблемой в Game Maker. Хочу сделать проверку с каким объектом сейчас сталкиваемся для взаимодействия, например: NPC, ящик с сокровищем, дверь, вещь, интерактивный объект.
Чтобы при нажатии на кнопку "А" совершить действие с тем интерактивным объектом, который сейчас выделен.
У главного объекта при рисовании Draw Event сделал:
У интерактивного объекта сделал в Step Event:
И почему-то
if (global.Active != noone) {
не работает.self - это древнее зарезервированное слово для целей совместимости со старыми проектами, которое всегда возвращает -1. Вместо self можно попробовать id.
Спишу уточнить, что может ключевое слово и древнее, но про него не забыли. В справке актуальных версий указано, что self предпочтительно использовать вместо id для чтения идентификатора экземпляра, т.к. механизм который выдаст значение через self работает чуть быстрее оного на id. Если вывести на экран draw_text(x, y, self), появится именно id экземпляра (что-то вроде 100008). Но, как сегодня выяснилось, в обновлении Game Maker Studio 2.3 этот self теперь работает по другому. Ты все еще можешь обратиться к переменным экземпляра через self.переменная, но если отрисовать содержимое self, выводиться немного другая информация.
Я думал self это нормальный указатель, типа this или gameObject для Unity.
То есть лучше не использовать self?
Лучше не использовать. Просто следи в контексте какого объекта ты находишься.
Спасибо! Пойду избавляться от self)) жаль я уже много их везде в коде добавил)
Автозамена же есть =)
Я вообще не знаю для чего это было придумано. Судя по всему для тех языков, которые не умеют адекватно работать с контекстами. Тот же JS, например там есть this, который нужен потому что там нет нормального ООП и так ты типа разделаешь контекст объекта и общий контекст.
В ГМС1 автозамены по всему проекту нет, только по отдельным кускам. В 2 есть. Можно везде заменить self на пустую строку (ничего не вводить) - сработает.
Точно, нету.
Поиск только есть и он лучше, чем в GMS2, т.к. можно искат ьв отдельных частях проекта да ещё и не учитывать комментарии.
Незнаю, я проверил у меня работает. И кстати, зачем глобал если можно в героя посылать... Типо obj_hero.active
Не срабатывает, ты имеешь в виду. Ну да - у тебя тут Active, а Step Event'е - Action.
Ещё названия этих переменных ничего не говорят - хорошо было бы сделать, например, CurrentActiveInstance. Тогда ни с чем не перепутаешь. Мало ли, захочешь глобально какое-то другое действие хранить или настройку.
И Раззл прав - используй id, не надо self.
лол, да, простая невнимательность)

Спасибо! В целом я доделал активацию, и даже диалоги прилепил)
Я обычно баланс соблюдаю между понятностью и краткостью. Такие длинные названия для переменных я редко использую, ибо из-за этого строки кода увеличиваются и читаются сложнее для меня,
чем короче строка, тем легче сразу понять что тут написано
И даже сделал выбор Да/Нет в диалогах,но это максимум что могу сделать в короткие сроки,

да и непривычны в GMS многие вещи. Например я тут так и не нашёл нормального аналога
SendMessage
,а простейшая строка (на картинке ниже), вообще не работает, что немного странно,
а зачем тогда
event_user
, только чтобы изнутри программы использовать и всё?!Или я просто не так делаю.
Такие штуки делаются путём обращения через with, простым обращением, как к переменной, этого не добиться. Может в гмс 2.3 что-то поменялось, но пока так.
Спасибо большое!
А если я хочу не other, а скажем использовать другой идентификатор другого объекта тоже можно с with?
PS и я нашёл позже вот такой способ в гугле, но ещё не тестировал:
event_perform_object(object, ev_other, ev_user0);
Да, with работает как c id конкретного объекта, так и с other (который на самом деле тот же id), так и с названием ресурса объекта - тогда применяется ко всем экземплярам и экхземплярам потомков.
event_perform_object(object, ev_other, ev_user0) - выполняет событие другого объекта object в заданном контексте, .т.е выполнится код для того объекта из которого эту функцию вызываешь. Почти никогда не используется. По крайней мере я не испытывал такой потребности. Ну может раз на 30 проектов делал.
Меня, кстати в юнити эти штуки sendMessage вырубали постоянно. Как-то с with удобнее работать. Причём функция капец какая универсальная. Поставил id объекта - проваливаешься в его контекст и выполняешь код для него. Поставил id ресурса - то же самое только для всех экземпляров и экземпляров потомков этого объекта.
А эти SendMessage насколько я помню - полная жопа, потому что этот месседж нужно ещё принять и обработать. А это значит - писать код на двух сторонах вместо одной. Нам в гамак такого дерьма не надо.
А мне всё равно придётся на двух сторонах вместо одной работать:
1) в первой стороне нужно отправить функцию активации, например я отправляю сигнал "Talk" другому персонажу, чтобы с ним поговорить;
2) а у второй стороны в функции "Talk" может быть совершенно разные диалоги и обрабатывать совершенно разные переменные, например:
*Дворецкий - откроет дверь и скажет что-то типа: "Прошу, проходите!"
*а Парикмахер поприветствует и предложит меню Парикмахерской
я и не уверен как сделать из одной стороны такой скрипт, когда не знаю что будет в функции второй стороны заранее
Вместо того чтобы менеджить диалог на двух сторонах проще сделать o_dialogue_manager, который и будет брать на себя все функции отыгрыша диалога. Тогда диалоговая система строится на паре списков и нескольких обработчиках событий. Я бы именно так делал. У меня был бы скрипт типа
create_dialogue куда бы я скармливал айдишники всех объектов-участников и список с репликами. У меня бы создался этот самый o_dialogue_manager, который бы залочил движение если нужно, управлял бы камерой и отрисовкой текстов. А при конце диалога бы самоуничтожался и возвращал управление и камеру. Так же можно сделать изи функцию скипанья.
Если там будет прям диалоговая СИСТЕМА с выбором или множественным выбором - тоже не проблема. Всё менеджишь в объекте o_dialogue_manager
Есть разные ветви? Вместо списка делаешь дерево и граф. Делов то =)
А ещё туда можно зашить какие-то названия скриптов, которые будут дёргаться и как-то вилять на игровой мир меняя значения глобальных переменных.
Я пока не совсем понял как работать с массивами, может позже если разберусь то попробую
читать из текстового файла и заносить в массивы инфу и оттуда управлять событиями и диалогами.
Но помимо диалога бывают и другие общие механизмы, например использование "Use",
где мне нужно второй стороне отправить событие Use:
у Яблока своя функция Use, которая отличается от яда, или от фотоаппарата, но у всех есть как бы общая функция Use, но внутри разная.
Это то как принято делать в Юнити, но нет никакого общего геймдевелоперского принципа по которому нужно строить всё именно так. В ГМе действительно нет сообщений между объектами, но организовать требуемую систему можно как минимум тремя разными способами (кроме деградантского - не использовать ООП вообще, и всю игру описывать в одном объекте):
1) В том объекте, который нужно применить, сделать User Event с номером, можно искусственно договориться что в проекте ВСЕ User Event'ы под номером 0 это реакции на "Use", ВСЕ User Event'ы под номером 1 это реакции на "Talk", и тогда тебе придётся следовать такому договору, но ты легко сможешь сориентироваться где реакция на что. Примерно это ты сейчас и делаешь - вызывающий объект должен делать with other event_user(номер);
2) Не считать что существует какое-либо передаваемое между объектами сообщение, сделать скрипт, назовём его например talk_system, и он будет принимать на вход в качестве аргумента тот инстанс, с которым происходит разговор. Вызывать ты его будешь так: talk_system(other.id), а делать он будет следующее:
Это просто другой порядок (иерархия) описания действий. Соответственно для другого действия надо создать другой скрипт, типа "Use", и там будет уже другое. Это хорошо тем что ты сразу видишь возможные результаты действий по разным объектам, открываешь один скрипт - и скроллишь все результаты одного действия. Плохо это тем что иногда тебе может понадобиться видеть не "вширь", а "вглубь" - описывать результаты действий исходя из типов объектов в первую очередь, а из выбранного действия во вторую.
Тогда нужно опять же делать скрипт butler_action(действие) т. е. вызов будет butler_action("Talk"), в сприпте писать:
3) И ещё один принципиально другой способ - подобно тому как Хейзер говорил про менеджер диалогов, создавать объекты-менеджеры любых действий. o_controller_talk, o_controller_use, и так далее. Как это будет работать?
В этих контроллерах нужно писать функционал, реализующий то что на них возложено. В целом, это больше похоже на контроллеры интерфейса, чем на скрипты-переключатели, и да, тут снова придётся делать User Event'ы.
В общем, мне вариант (2) кажется наиболее оптимальным (оба из его вариантов иерархии). И, как видишь, все перечисленные варианты сенд_месседж не делают, а значит он не необходим.
А можно использовать макросы или enum и не ебаться с запоминанием. А просто писать
Можно в объект-прототип заложить весь функционал, сделать отдельные скрипты на use и talk, которые по object_index будут делать те или иные вещи. Но там проще просто
Можно ещё кучей способов построить удобную систему. Это, конечно, напоминает костыли. Но костыли эти будут во много раз лучше чем строить диалоги на sendmessage.
Про макросы и enum ты прав (и enum будет очевидней), а о прототипе нифига не понял тебя - ты имеешь в виду что это будет парент? Ну тогда будет куча пустых объектов-наследников и много писанины в скрипте. ХЗ будет ли удобно, но тут все способы имеют свои как плюсы, так и минусы.
Общее правило - надо смотреть какая часть проекта "широкая", а какая "узкая", чтобы кодить приходилось только "узкую", а "широкая" решалась автоматикой. Нельзя автоматизировать всё, но можно выбрать более выгодный порядок абстрагирования.
Больше объектов? Надо начинать с действий.
Больше действий? Надо начинать с объектов.
Ну а если некуда девать время, то можно и без ООП писать. А если совсем супер-некуда, то и на более сложных движках.
Мне вот просто интересно. Ты так делаешь по какой причине?
Сам придумал? Перетянул опыт с другого движка? Кто-то научил в универе?
Сам придумал, но я не оптимально обычно программирую, но в целом думал о такой логике:
если есть человек, то можно ему подать общие сигналы: Talk
если есть разные вещи в инвентаре, то можно им дать общий сигнал: Use
если есть дверь, то ей тоже можно отправить сигнал Use
PS а у каждого объекта свои функции со своими обработками, хоть называются одинаково,
более лучшего вариант не придумал
Если тебе нужно передать "сигнал" заведи для него переменную в объекте, а потм можно и: other.signal="Talk" типо того.
Ему нужно не только передать тип действия, но и фактически его выполнить в этом же кадре. Иначе может быть задержка и рассинхронизация, типа на стороне игрока он уже уйдёт в следующую комнату, а сюжетный предмет ему выдасть не успеет перед порталом. (конечно кто сказал что так вообще надо делать, но чем раньше в архитектуре проекта стыковать всё плотно, тем меньше будет мест для ошибок и затягиваний проекта потом)
Эти проблемы людей с кучей комнат... ХД
В исходнике 9 комнат, это мало? Если поставить ещё 100, проблем тоже не будет - это проблемы людей которые где-то забыли поставить галочку, а где-то забыли снять.
Вопрос - а нельзя ли просто сделать ивент коллизии с другой стороны, то есть коллизия o_BoxCore с o_HeroGirl сразу выполняет код, даже без юзер-ивента 0?
Коллизию я сделал для теста, на самом деле мне для других целей, для того чтобы использовать
какие-то объекты по нажатию кнопки А или поговорить с ними.
В конечном итоге я пока без интернета сидел вчера, таким способом сделал (через переменные):
1) первая сторона отправляет переменную isUse
2) а вторая сторона принимает сигнал:
теперь конечно я постараюсь без переменных сделать, а с with и user event
Это вполне рабочий метод, но может приводить к несостыковкам по времени, как я уже писал - мгновенные телепорты в другую комнату, например, при этом будут запрещены, или придётся писать специальную очередь действий которые надо подождать перед переходами. В общем, лучше скриптами это делать, ИМХО.
Добавляю интерфейс, выглядит конечно топорно, но ок)

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