Всё ещё учусь работать в Game Maker (GMS 1.4.9), параллельно участвуя в конкурсе на GCup.
1)Немного неприятно, то что разрешение экрана и FPS нужно менять в каждом уровне отдельно,
по логике это должно выноситься в опции и быть общим для всех уровней.
2)Также немного прокачал рисование персонажа для кастомизации, хотя возможно это всё будет лагать при 60 FPS на слабом ПК, я не уверен:
- 18 июня 2020, 17:21
- 04
Сейчас гуглю, как работать с коллизией в GMS, place_meeting вроде ок, только надо понять как заставить фильтровать во что врезаться, а что игнорировать) Надеюсь эта функция проверки коллизии очень быстрая, ибо в коде её надо вызывать 2 раза для проверки X и отдельно Y скорости, прям как в моих самописных проверках коллизии для Tic-80 и BlitzMax, где проверка по разным осям идёт отдельно, но если что-то не предусмотреть то можно застрять в стене, например если стена будет двигаться на персонажа)
да, норм по скорости... у тебя какой гмс?
GMS 1.4.9.
У меня почему-то в редакторе подтормаживает при запуске игры, если включен хром, может быть это из-за моего слабого ноута, но это пустое приложение без ничего, кроме перса, а иногда работает очень плавно и хорошо, хз с чем связано.
Надо потом потестить в билденной версии вне редактора, и параллельно хром включить.
Всем известно, что Хром прожорливая скотина способная даже при пустом окне съесть всю твою память и даже не подавиться...
смотри диспетчер... а так фиг знает...
так же чтобы определять с кем столкновение, можно использовать "кнопочную" коллизию с идентификаторами self.inst (obj name something other), other.
Спасибо, попробую разобраться с этой функцией! Я тут как раз думаю над тем, как сделать так, чтобы часть объектов игнорировалось при коллизии:
Например я нашёл функцию, которая сталкивает только с объектами "solid"
и есть такой способ, чтобы проверять более сложную коллизию, но не уверен быстро ли он работает
и нормально ли то что я вначале place_meeting проверил, а потом сделал вторую проверку (для оптимизации),
или может это не важно и можно сразу делать проверку на instance_place:
В данном случае первая проверка не нужна. Если говорить про оптимизацию то это просто лишнее условие. Скорее всего обе функции отрабатывают по скорости примерно одинаково.
Насчёт solid-объектов, как мне кажется этот функционал лучше не использовать. Лучше сделать через наследование объектов. place_meeting отлично работает, я сам пользуюсь периодически, хотя чаще всего collision_ функциями.
Не выдумывай, тебе достаточно collision_rectangle(x,y,x+hspeed,y+vspeed, obj_точтонужно, 1, 1)
Зачем collision_rectangle-то?
ну, он "легче"
Точная инфа? Я думал, что наоборот, особенно если для маски объекта не используется точная проверка столкновений, повторяющая его форму. Так-то при столкновении всё равно движок, по идее, обрабатывает bbox_* переменные, фактически - прямоугольные границы (если не была изменена форма коллизии), что равнозначно collision_rectangle. Я бы даже сказал наоборот - place_meeting возвращает true/false, а от collision_rectangle ещё и айдишник объекта, с которым сталкивается, то есть это place_meeting + instance_place.
Когда place_meeting возвращает true, движок внутри себя уже знает id этого объекта, т.к. проверяет каждый конкретный объект по списку id. По скорости нет разницы что он возвратит - id или true. По крайней мере, я не вижу иной оптимальной схемы работы движка.
Задержка может быть только в том, сколько экземпляров он проверил перед тем, как нашел нужный.
Вот кстати о множественной одновременной коллизии - если писать кодом, то нужно будет получать СПИСОК объектов с которыми столкнулись и с каждым из них выполнять код. Если делать через событие коллизии, то оно автоматически выполнится для всех объектов с которыми столкнулись, не надо будет писать цикл по ним. Так что пока что вообще не обосновано написание отдельного кастомного кода для того чтобы реагировать на коллизии.
Обычно это делают против нежелательного туннельного эффекта, когда слишком быстро летящая пуля со слишком маленькой маской пролетает через слишком тонкие стены. В проекте Алекса такого, насколько я видел, нет, а может и не предвидится. Зачем же мудрить?
Второе "обычно это делают" - это когда нужно детектить пол в платформерах и отдельно левую, правую и верхнюю грань коллизии. У Алекса не платформер, там перемещения на манер битэмапа/RPG по примеру Beyond Oasis (только вид таки сбоку).
А еще можно использовать стандартный механизм коллизий для получения этого СПИСКА в одной строчкой кода, чтобы потом использовать его в самописных коллизиях. Но я честно хз, что происходит первее ev_collision или ev_step (normal)
Для самописных коллизий пока что не видно причин использования в проекте Алекса. А так да, можно всё писать вручную, а с ГМС2.3 при желании вообще можно выкинуть всё встроенное и писать почти как на голом C++ уже обвязанном библиотеками вывода графики, звука и всего остального. Причём кроссплатформенно ключая игровые консоли.
https://docs2.yoyogames.com/source/_build/2_interface/1_editors/events/index.html
См. Event Order - про коллизии на данный момент ничего официально не пишут. Полагаться ни на какой порядок нельзя, но с другой стороны это всегда можно отладить для конкретной версии ГМа на которой идёт работа.
https://kolenka.net/posts/tochnaya-proverka-korotkikh-intervalov-vremeni-v-game-maker - но в ГМ8.1 они абсолютно точно работают после Step и до Draw.
А зачем place_meeting?
А можно из той коллизии получать свойства объекта при столкновении?
Мне просто нужно знать с кем столкнулся и провести некоторые проверки или переслать сигналы.
И можно ли вместо "obj_точтонужно" поставить, например типа свойства "solid" или по другим свойствам?
А то мне не нравится когда спрашивают с каким объектом сталкиваться, ибо я не знаю что это будет за объект, но я знаю какие нужны будут свойства этого объекта, чтоб столкнуться.
1) Например я хочу чтобы моя пуля сталкивался с 4-мя типами объектов: Стены, Другие Пули, Враги, Герой.
Должен ли я писать 4 функции для этого или хватит просто им проставить ну например свойство solid?!
2) Ещё я не уверен, можно ли как-то послать сигнал объекту, когда столкнулся с ним, потому-что кажется что внутри объектов в GMS нет классических функций, как в юнити происходит методом
SendMessage
илиGetComponent<Class>.MyFunction();
,мне нужно послать сигнал урона, к объекту с которым столкнулась пуля, типа
MinusHP(5);
Пока я сделал через переменные, но выглядит немного как костыль и не уверен нужно ли стенам тоже добавлять эти переменные типа HurtHP,HurtSX,HurtSY, чтоб не выскочила ошибка, хотя стены не уничтожаются и им это не нужно, разве что можно ещё одну сделать переменную типа Type, которая будет говорить,
что это тип стена Type = "Wall" и чтобы не было проверки на HurtHP:
Флаг solid вообще не для того - всё что ты обозначаешь как solid, будет выталкивать из себя все столкнувшиеся с ним объекты автоматически, причём это выталкивание может работать не так как тебе нужно, и настроить его нельзя. По большому счёту это вообще лишний функционал, тянущийся из старых Гамаков 15-летней давности чтобы не нарушать обратную совместимость.
По поводу твоего случая - ну, если бы я рассуждал таким образом, то я бы писал 1000 разных веток столкновений для 25 видов оружия и 40 видов врагов в моём проекте Void Source, но всё делается намного проще.
Если твои пули не летят с гигантской скоростью, то коллизии с ними (и чем угодно другим) легко можно поручить событию Collision (как ни странно), поэтому код проверки коллизии писать вообще не понадобится, сразу будешь писать действия которые при этом надо совершить.
Далее - тебе нужна абстракция, то есть некий объект, "то с чем может столкнуться пуля", а чтоб это было валидным названием объекта, назовём его o_shootable, дословно "то, во что можно стрелять". Теперь мы берём эти твои 4 типа объектов: "Стены, Другие Пули, Враги, Герой", и каждому из них назначаем объектом-родителем этот самый o_shootable, вот здесь:
Теперь идём в объект-пулю, создаём там событие Collision с o_shootable и пишем туда код, ну скажем:
Теперь, с кем бы ни столкнулась пуля, она уничтожится сама, а потом уничтожит тот объект с которым столкнулась. Естественно, можно выполнять любой код как с пулей, так и с другим объектом, типа - отнять щит вместо хитпоинтов, вообще говоря проверить меньше ли хитпоинты нуля, прежде чем уничтожать объект, снизить скорость пули вместо уничтожения и летать дальше, и так далее.
Тут может стать вопрос - что если видов стен и врагов много, всем наследовать o_shootable? Нет! Тебе нужен объект абстрактного врага, объект абстрактной стены, и так далее. Они наследуют o_shootable, а каждый конкретный враг наследует o_enemy, тогда как каждая конкретная стена - o_wall. Нафига, спросишь ты. Для того чтобы вместо 40x25 коллизий у тебя только в абстрактном выстреле была только одна коллизия с абстрактным врагом, и все конкретные выстрелы, враги, стены, и так далее - автоматически наследовали это поведение.
У объектов в GMS есть пользовательские события, делаешь так:
Потом в событии коллизии пули с o_shootable вместо того что я выше написал, делаешь так:
И у объекта происходит обработка повреждений - такая, какую ты ему прописал в событие User Event 0, например:
Учитывая наследование, которое я описал выше, писать это нужно в абстрактные объекты, а не в конкретные, чтоб не копипастить всё по 200600 раз.
Альтернативный вариант:
И создаёшь скрипт receive_damage() в котором пишешь то же самое, что и в предпоследнем куске кода - отнять хитпоинт, если закончились - исчезнуть. Ведь скрипты в ГМе - это и есть функции (примечание - только до версий раньше ГМС2.3 - начиная с 2.3 в одном скрипте может быть даже несколько функций).
А это, кстати, для чего тебе передавать нужно? Или точнее - что это, скорость урона в координатах...???
У меня Пули и Враги имеют уже родителя (parent), то есть например:
Laser имеет родителя ShotCore (как бы подвид выстрела, а в ShotCore основной скрипт), EnemySpaceBunny имеет родителя EnemyCore (как бы подвид врага и главный AI всех врагов), стены пока не имеют скриптов, но видимо придётся добавлять.
Если это и есть "родитель всех категорий врагов", куда вынесена самая общую для всех врагов логика, то используй его в схеме описанной выше. Если это только некая категория врагов, но не все конкретные враги это наследуют, то нужно создать более абстрактного врага и унаследовать все категории врагов от него. То есть будет:
EnemySpaceBunny -> EnemyCore -> EnemyVoobshe
Laser -> ShotCore -> ShotVoobshe
Важный момент: объекты надо называть с префиксами типа o_ потому что спрайт Laser и объект Laser будут конфликтовать, и ты в коде не поймёшь на что ссылаешься. Либо спрайты называй на s_, но обычно объектов в проекте меньше чем спрайтов, так что и лишних нажатий при наименовании меньше будет.
Вопрос про HurtSX актуален - что это такое и как используется в твоей архитектуре геймплея?
видимо пока не поздно надо всё переименовать с "s_" "o_", чтобы не запутаться потом)
у меня пуля не сразу уничтожается, вначале прокручивается таймер и анимация эффекта уничтожения

Такое поведение удобно делать с отдельным объектом: пуля ведь не может продолжать сталкиваться с чем-то ещё, если она уже столкнулась? Значит пора бы её действительно уничтожить, а в событии Destroy этой пули сделать instance_create(x,y,o_shot_effect) который уже просто будет проигрывать анимацию, ни с чем не пытаясь сталкиваться и не разбираясь во флагах надо ему сталкиваться или нет. Просто другой объект-пустышка с событием Animation End в котором instance_destroy(). Не забыть в Create поставить image_speed=0.25 или сколько нужно.
Если объектам делать сложную parent структуру, то может и правда стоит делать новые объекты для уничтожения пули, чтобы это всё не занимало ресурсов и не было проверок на коллизию.
Я тут для иллюстрации набросал схему. Сложно читается, но думаю суть ясна. Не важно сколько уровней наследования ты используешь, на каждом из них ты экономишь силы при расширении функционала конкретных объектов, за счёт изменения абстрактных объектов.
Меняешь o_damageable - влияешь на всё "живое".
Меняешь o_wall - влияешь на все стены.
Меняешь o_enemy - на всех врагов.
o_enemy_swimming - только на плавающих.
Ну а меняешь конкретные объекты влияешь точечно, именно на них.
В этом вообще смысл наследования в ООП.
Мне только немного странно с GMS 1.4.9, что если например есть переменная HP в o_damageable,
и я её указал при старте
HP = 10;
но почему-то, если я не укажу туже самую строку при старте o_enemy (хотя в родителе уже инициирован HP)
HP = 10;
то почему-то без этой строки в наследнике компилятор выдаёт ошибку, неужели у одного родителя не достаточно этой строки при Start Event?
Create Event ты имел в виду наверное.
Да, тут есть один минус - для того чтобы наследники выполняли код родителя, нужно писать команду event_inherited(), но с другой стороны - зачем тебе враг без хитпоинтов, тебе их всё равно придётся прописать в каждого врага - лично его живучесть, как и все другие статы.
В событиях, которых у наследника нет, код родителя выполняется автоматически (вот на этом и экономится та тысяча комбинаций коллизий), в остальных - event_inherited() пиши где нужно. Можно до своего кода, можно после, можно даже несколько раз в разных местах.
точно, я забыл про эту штуку) хотя в примере было про это, когда учился inheritance на GMS
наглядно) сохраню
Вот это офигенно), но жаль что этих функций ограниченно.
С 2006 года делаю игры на ГМе и ни разу не задействовал даже половину из них - нет таких архитектур и геймдизайнов в которых нужно столько всего. С другой стороны циферные обозначения это вообще неудобно - для этого есть скрипты, и вот их уже действительно можно создать неограниченное количество.
Для того чтобы оттолкнуть врага или героя в зависимости от скорости и силы пули или урона в противоположную сторону.
Ого, это крутая механика, продуманно.
Не обязательно что-то там передавать, если можно просто от имени пули:
Как вариант, hspeed/2 и vspeed/2.
точно, хотел погуглить как hspeed и vspeed ставить на паузу при открытии меню
Никак. Придётся использовать Unreal Engine 5. :yak:
instance_deactivate_all(true)
и
instance_activate_all()
То же самое с _object вместо _all, если в проекте несколько уровней вложенности паузы, или сложная система игровых состояний.
А, ну ты щас спросишь - а как чтоб объекты отображались на фоне паузы. Тогда:
https://docs.yoyogames.com/source/dadiospice/002_reference/windows%20and%20views/screen_save.html
И отрисовывай скриншот.
Если же ты хочешь чтоб они ещё и анимировались, ну это тебе придётся весь код писать так, чтоб объекты можно было произвольно "запаузить" - бэкапить их таймеры чтоб они не срабатывали во время паузы, но при этом не трогать image_speed, при паузе делать global.paused=!global.paused и прописывать в Step каждого объекта if global.paused exit. Это всё реализуемо, главное сразу говори в точности что тебе нужно.
>>instance_activate_all()
Я немного не понял, а в кнопках в меню тоже остановится скрипт с этой функцией? Потому-что мне нужно
не только поставить игру на паузу, но и при этом лазить в меню и продолжать работать со скриптами,
но только теми, что относятся к меню.
Например для ручной скорости у меня есть решение, вот код из управления героем:
if (global.Menu != noone) exit; //Pause Correction
то есть дальше этой строки не читаем и персонаж не двигается и не анимируется вручную,
но пуля у меня на автоматической скорости c hspeed/vspeed, и я думаю возможно придётся её тоже делать вручную...
Я предполагал что меню у тебя одним объектом вместе с кнопками. В таком случае:
Чтобы не писать все кнопки вручную (меню ведь может меняться на протяжении разработки игры), опять-таки, создай абстрактную кнопку и унаследуй все кнопки меню от неё. Вне зависимости от того, какие из них ты будешь использовать в игре, активироваться будут они автоматически и в любых комбинациях.
Проще при паузе делать:
А при снятии паузы обратно:
Наверное тогда проще использовать вместо hspeed/vspeed, свои переменные SpeedX/SpeedY,
видимо точно придётся избавить от всех переменных автоскорости в проекте
хотя в таком случае, надо подумать как изменить эти строки кода с автоматической скоростью, на ручную при создании выстрела
Ну вот видишь. Не проще.
Ты много хочешь изначально, я не представляю как это всё кодилось бы в Юнитях-Годотах, несмотря на то что всегда кто-то выкрикивает из толпы "да это же элементарно!". Ну и как это сделать?
И всё же, у тебя достойная цель, поэтому я тебе написал как это делается, в десяток строчек и один дополнительный объект.
лол, у меня есть костыль, но выглядит тупо) забрать данные у hspeed и отключить)
Этот код вызывает пару вопросов...
Почему мы стреляем от имени прицела, а не от имени игрока?
Почему -180 отнимается в image_angle, а не в _ang?
Но вообще, это должно работать. Только я напомню, ты решаешь сейчас задачу, которая тебе накидывает на горб обязанность во ВСЁМ проекте, ВСЕГДА для всех останавливаемых на паузу объектов прописывать свою "неавто"-скорость. Подумой.
Если ничего не надумоешь (а зря), то вот штатное решение, которое на 1% облегчит использование твоего дорогого и трудоёмкого костыля:
наверное, если сделать так, то не надо будет и -180 делать, это просто мой косяк, ибо это стреляет враг по герою, а не герой по врагу
Мне кажется это решение немного странным. Я наверное просто избавлюсь от всех hspeed/vspeed, если только потом не окажется что частицам (если частицы вообще работают на моей бесплатной версии GMS 1.4.9 :P ) для паузы тоже нужен такой способ, какой ты предложил. Я хотел просто отключить логику объектов, но продолжать рисовать спрайты.
Чего кажется странным, рабочее решение, просто не анимированное.
Есть ещё вариант для анимированного: забываешь про глобальный флаг паузы и exit, действительно всё кроме меню и кнопок деактивируешь, но ровно перед этим делаешь вот что:
o_decoration это объект, в ивенте Draw которого прописано:
Он будет просто "торговать лицом", используя код объекта поставленного на паузу, пока пауза не вернёт к жизни тот объект который он отрисовывает. При снятии с паузы нужно сделать
with o_decoration instance_destroy()
соответственно.что-то жесть какая-то) возможно решение со скриншотом легче или хз
Не знаю, вот тебе три разных решения, выбирай на вкус.
А я думаю что у меня объекты иногда странно отталкиваются, не так как я прописал) надо убрать эти галочки
Чтобы скорость в GMS начала проседать нужно быть очень плохим программистом.
Если ты понимаешь что делаешь, то смело используй практически любые функции столкновений. Пока у тебя в комнате счёт на объекты не перевалит за тысячу-другую вообще не переживай.
Ну и проверки столкновений в циклах используй аккуратно (вот там много итераций цикла тебе fps не простит)