Тест метроидвании Quest of Dungeloria
Платформер-метроидвания в открытом мире, с элементами адвенчур.
Тестирование
Привет, Гаминчане. За два с половиной года, практически в соло, можно сделать видеоигру. Сегодня принес вам на тестирование уже отполированную и протестированную версию. Вообще, надо сказать, игра тестировалась по этапам. Был тест ранней стадии, после которой мне пришлось переработать практически весь дизайн уровней и добавить множество элементов, которых до этого не предполагалось. Во второй раз тестировал уже переделанную игру, а точнее первую ее половину. Эта демка была практически готовой частью игры, но все еще сырой. Каждый такой тест сильно улучшал опыт игрока и развинчивал мои заблуждения. Когда вся игра целиком была готова, я начал предфинальное тестирование. Этот тест прошел совсем недавно и по итогу, игра дошла до отполированного, почти релизного состояния. И вот теперь, в таком виде, я уже могу показать ее широкой публике,
Задача
На самом деле, я прошу вашей помощи, а не просто попробовать игру. Вот список вещей, которые мне нужны от гамин-тестировщика:
- Записать видеопрохождение через OBS или любую другую программу захвата экрана.
- Комментировать в микрофон свои ощущения и мысли, возникающие в процессе игры.
- Пройти игру целиком, т.к. поздние этапы тестировались меньше ранних.
- Проходить можно частями. Полное прохождение занимает 3−4 часа.
- Видео можно присылать частями, мне в личку.
- Критически ошибки и вылеты, лучше присылать сразу скриншотом.
- Играть можно как с геймпада, так и с клавиатуры. Как в окне так и на полный экран.
- Проходить на русском языке, т.к. английский перевод пока не завершен.
- Отдельная просьба к стримерам, пока не обозревать игру публично, т.к. если гаминчанец видит игровой процесс на стриме, то самому играть уже не так интересно, а тест будет не ликвидным. Мне важнее всего поведение именно девственного игрока, который сталкивается с игрой впервые. Так я смогу увидеть свои недочеты и недоработки.
Ссылка
Ссылку на игру я дам любому желающему, готовому записать свое прохождения. Просто чиркните мне в личку и сразу получите ссылку на игру.
Страница в Steam: https://store.steampowered.com/app/2623580/Quest_of_Dungeloria/
- 22 ноября 2024, 13:55
- 018
Ссылку убрал. Игра уходит на исправление ошибок на пару дней.
прогресс просрется?
Да. Если далеко зашел, багов не было, играй так.
20 минут пока, буду ждать новую версию. Но вообще странно что сохранения похерятся
У объектов в сцене есть свои айдишники. Если убрать объект или добавить новый, файл сохранения даст ошибку попав на несуществующий айдишник.
ох уж этот гейммейкер. Стандартную систему сохранения используешь?
А после релиза как ты собрал патчи выпускать? Каждый раз вайпая прогресс игроков? Хочешь тонну отрицательных отзывов?
Система сохранений своя. А причем тут гейм мейкер? На юнити инстансы не имеют уникального идентификатора?
Ну с ним много проблем без своих либ.
Имеют, суть не в этом же. А в том, что когда разрабатываешь архитектуру системы сохранений она должна поддерживать модифицируемость игры. Я не знаю в чем именно баг, но знаю, что если в твою игру купит много людей, они 100% наткнутся на баги, которые ты не нашел даже с плейтестами. И если ты очередным патчем, который эти баги будет чинить, убьешь им сохранения это будет очень плохо воспринято. Будь игра в раннем доступе, да еще и с плашкой мол "изменения убьют ваши сейвы" то ок, но если такое после релиза, то это мрак.
Я понял твою мысль. На гейм мейкер ты зря нагнал. Ошибка в моей архитектуре. (которой нет)
В такие игры подходит событийно-ориентированная система сохранений. Выписываешь список всех событий, которые могут происходить в игре. А объектам вручную прописываешь поведение, в зависимости от наступления или не наступления события. Можно хоть в creation_code. Но лучше всего в variables сделать type и по нему в room start проверять наступило ли событие.
Заводишь массив или хэш-карту, в которую добавляешь наступвишие события по мере их наступления.
Тогда тебе в сейве кроме статов нужно хранить только список событий. И никакой привязки к айдишникам.
Мне это очень нравится. Спасибо! В следующей игре буду опираться на это.
Хотя поспешил. Вдумался и стало не понятно. Как локальные события прописывать? Вот, скажем, в игре десять рычагов, тысяча монеток, пятьдесят яблок, двадцать нпс, шесть замков и т.п.
Что значит локальные? Если ты хочешь хранить прогресс глобально - они глобальные.
Если объектов не сильно много то нумеруешь их. Например:
lever_1
lever_2
lever_3
И т.д.
У объекта level (рычаг) добавляешь variable с типом строка и названием, например event. В редакторе комнаты перезаписываешь этот variable кастомным. Например, lever_3.
В коде рычага:
1) При его активации проверяешь если значение этого event не пустая строка - закидываешь его в список событий.
2) В room start проверяешь есть ли этот event списке событий. И если есть - проводишь активацию рычага, типа он уже включен.
В случае с сотнями и тысяч коллектиблов это может быть геморно. Я бы сделал автоматизацию.
Во-первых, сделал бы отдельные списки событий, например apples_events, coin_events и т.д.
А у самого объекта вместо variable генерировал бы название event автоматически. В том же room_start, например.
В качестве идентификатора использовал бы название комнаты и координаты:
То есть если у нас уже в списке есть такое событие - значит мы собрали яблоко/монетку/итд.
Ну и при сборе добавлять в список событий собранный коллектибл. Поясню ещё, что roomName - это текущее название комнаты. Лучше, чтобы оно не было связано с названием ресурса, а задавалось вручную в виде строки. Ещё eventExists - функция проверки события, её нужно будет писать вручную и это зависит от реализации списка событий: массив, хэш-карта, очередь и т.д.
Да, если ты монетки подвинешь - они будут считаться уже как новые, не собранные. Тем не менее, сейв не поломается. Чтобы это починить можно совместить оба метода. Нумеровать монетки внутри комнаты, раздавая им номера вручную, но генерировать событие с префиксом комнаты.
Я туповат немного и до конца все ровно не понимаю, когда читаю вот так из статьи новый для себя метод. Если каждому инстансу прописывать номер вручную это будет люто. Представляешь сколько раз я вношу изменения в дизайн уровней. Там все время что-то добавляется или удаляется. Даже если нумеровать их только в рамках своей комнаты, что уже подразумевает, что комната должна быть небольшой, и в этом можно не запутаться, хотя человеческие ошибки все ровно будут, суть в том, что это просто убивает весь процесс левелдизайна напрочь. Поставил облачко, залез в пресет, прописал переменную, закрыл пресет, удалил облачко, поставил солнышко, залез в пресет, прописал переменную, закрыл пресет, понял, что облачно было лучше, удалил солнышко.
Я понял главное, что надо как-то уходить от привязки к айдишникам и что проверка на эвент из самого экземпляра хорошая архитектура, но пока не понятно, как это реализовывать, чтобы было удобно.
Кажется, Хейзер имеет в виду, что поскольку большая часть игры в метроидвании тупо перезагружается при повторном посещении комнаты, то её и не надо запоминать. А запоминать только прогресс по сюжету и прокачку.
У тебя вроде бы небольшие комнаты, не?
Если комнаты большие или ты хочешь больше автоматизации и удобства - нужно свой редактор уровней пилить под свою игру.
Так это и есть суть левелдизайна XD
Ручная расстановка и ручная настройка объектов.
Т.к. мало кто хочет такой геморрой, то коллектиблы в играх делают в очень ограниченном количестве. А игровой мир практически не меняется.
Не совсем понимаю зачем нумерация для простых объектов типа монет? Честно, не делал ещё такого, но вариант такой:
В файле храниться данные о комнате и списки объектов по типам. Какие подводные камни я не учитываю?
Кстати, да, есть вариант забить на структуру игры, которая оригинальная, и просто загружать все объекты, как они указаны в сейве. Если нужно поправить баг, то тогда по условию версии игры у сейва что-то ещё поменять после загрузки.
Нумерация нужна потому, что каждая монета уникальна. Если у тебя в сохранении только один эвент, что монета подобрана, то все десять монет должны будут удалиться из сцены, а если в файле сохранения прописано, что осталось 5 монет из 10, то тогда вообще не понятно, какие конкретно монеты удалять, а какие оставлять. Речь изначально шла о том, чтобы отказаться от ссылки на уникальный идентификатор экземпляра объекта, чтобы с новым патчем сохраненный прогресс не ломался.
Пока что, думаю, что можно записывать в файл сохранения родные айдишники, но при загрузке не упоминать их из вне, а использовать через конструкцию with.
И при этом еще перейти от записи параметров, к записи эвентов. Уж конкретный экземпляр знает сколько у него эвентов может произойти. Обычно один, иногда два-три. Эвент уже будет "вручную" назван. Мне не нужно будет настраивать по уникальному эвенту для каждой монетки, а достаточно будет сделать общий эвент для всего класса.
Сохранять не только параметры/event, но и координаты монеты (я это написал). С помощью координат ты оставшиеся 5 монет устанавливаешь на их места. Если каждая монета уникальна, то уникальность ведь в её параметрах (ценность, спрайт и т.п.). При загрузке из сохранения ты просто будто динамически их создаёшь, а не просто "ищешь" в комнате.
Если монеты не могут менять положения, не двигаются, то координаты являются уникальным значением для каждой монеты, ведь не будет две монеты друг на друге.
Так в том и прикол, что монеты могут менять положение. Ты можешь её подвинуть, например при исправлении бага. Или изменить размеры комнаты и сдвинуть все объекты, что тоде приведёт к изменению координат. Поэтому идентификация по координатам так себе идея. Лучше прописывать уникальный номер в пределах сцены. Расставлять монеты при инициализации комнаты - тоже так себе идея. Проще удалить, те, которые уже были собраны.
Если ты меняешь всю комнату, то при наличии данных в сохранении можно при проверке версии игры их игнорировать. Идентификатор надёжный способ, но работы больше в начале. Насколько это оправдано зависит от игры.
Если ты хранишь только удалённые, а не оставшиеся. С какой стороны посмотреть.
Чем больше то? Идентификаторы расставить? Больше, чем скрипт расстановки всех объектов писать? С поддержкой костылей при появлении новых объектов и изменениях?
Нечего тут смотреть. Удалять проще, т.к. они на сцене изначально уже есть. Иначе придётся их сперва удалить, а потом создавать.
Как я понимаю, то речь про левелдизайн такой, что всю комнату разнести и переделать. У меня тогда вопрос зачем (или в чём претензия к) рабочие сохранения в этом случае? Хорошо если игра не вылетит и не сломается, а если комната переделана (можно хранить версию комнаты прямо в сейве?) игнорировать вообще все эти данные и говорить ей, что они устарели.
Тут я тебя немного не понимаю. Задача: оставить рабочие сохранения при изменении комнаты. Я согласен, что при глобальном изменении, старые данные можно игнорировать, я об этом писал. Но если уровень изменился немного...
Значит мы рассматриваем только изменения уровня подвинуть монетки и стало их 5 а не 6, например?
Тут речь идёт да, о не больших изменениях, но которые могут повлечь за собой изменение количества объектов или их положения. В общем, универсальный способ - это использование идентификаторов на объектах. Как и писал Herzenrg.
Ага, с такой задачей ничего лучше идентификаторов прописывать руками наверно и не будет. Так сказать, однозначно объявлять соответствие.
А если это не монетка, а яблоко, у которого надо сохранять координату, степень гниения и существует/не существует? То есть, ты все таки предлагаешь, как Скорчед написал, управлять спавном в ручном режиме. Мне вообще-то нравится. Действительно мы от айдишников уходим полностью. Осталось только вот что не понятно. У тебя в редакторе уровня эти яблоки расставлены изначально, ты же их не из кода берешь. Получается, при загрузке комнаты, все яблоки удаляются, а потом спавнятся из файла. Откуда берется сам файл? Получается, что в первый раз, когда мы попадаем в эту комнату, яблоки должны записать свое начальное положение в сейв. Это окей, это можно сделать. Вопрос. Если я редактирую уровень. Например добавил еще два яблока. У игрока уже есть сейв-файл, где прописано, что яблок десять, пять из которых сгнили. Как мы будем спавнить в нужных местах два новых яблока?
По факту, да. Scorched кратко описал решение.
Яблоко, монеты разница. Если яблоки есть на карте в редакторе, то у тебя есть список того, что должно быть и текущего состояния комнату (что осталось только и их состояние, например). По факту, мы делает "снимок/состояние" комнаты и восстанавливаем его.
По поводу файла: при попадании в комнату, если данные о ней в файле нет, то ничего не удалять и не загружать. Всё остаётся как ты сделал в редакторе. По поводу добавления и изменения положения яблок, про это и Herzenrg вот описал. Тут, конечно, хорошо иметь идентификатор, так ты имеешь полный контроль. Единственное остаётся это проверка версии игры и от неё что-то учитывать, а что-то нет. Можно просто игнорировать сохранение и считать, что игрок первый раз попал в комнату (тут, конечно, можно индивидуально смотреть на объекты). С идентификаторами, конечно, надёжнее, но зависит от игры и это лишняя трудоемкость.
Это бред, не вникай даже. Храни только то, что собрано и удаляй при инициализации комнаты.
Если бы у тебя был кастомный редактор уровней или процедурная генерация, тогда можно над этой идеей подумать.
У меня так и работает! Аахах
у тебя разве монетки в самом мире лежат? я пока только в сундуках видел. А если только в сундуках, то зачем каждую монетку индивидуально помнить? Типа не собрал часть монеток, запоминай сколько "осталось" в сундуке, а не какая конкретно монетка подобрана, а какая нет
из сообщений выше
Чёт не понятно. Что значит "при загрузке проходит по 5 монетам" и "при загрузке сохранения находить по координатам"? Обычно есть один файл сохранения, в котором хранятся какие-то данные в виде текста, JSON или как то ещё. При загрузке сейва и старте игры - эти данные считываются в какие-то глобальные переменные. Когда стартует сцена, объекты при инициализации должны посмотреть в эти глобальные переменные и если есть события к ним относящися - что-то сделать. Модифицировать сцены до их инициализации звучит вообще не очень с точки зрения менеджмента. Это значит, что часть данных о сцене хранится не в самой сцене, а в каком-то непонятно левом месте.
То есть рычаги и кнопки внутри сцены уже настроены. При инициализации важно знать был ли он нажат или нет. Если у него всего два состояния нажатости, достаточно понять есть ли его идентификатор в списке событий. Если есть - он должен "проиграть" свою функцию, т.е. открыть дверь, активировать платформы и т.д. Это тоже своего рода "гемор", и совершенно отдельная задача.
Монетки в сцене уже расставлены, у них есть какие-то координаты, и, возможно, параметры. При инициализации опять же достаточно понять есть ли идентификатор монетки в списке событий или нет. Если есть - просто удалить монетку на старте сцены. Можно этот идентификатор составлять из названия комнаты и координат, как я уже писал выше. Но лучше, чтобы он был уникальный и не менялся при редактировании комнаты. А это в любом случае прописывать вручную.
Если отказаться от встроенного редактора сцен в GameMaker, и запилить свой - можно сделать некоторую автоматизацию, типа автонумерации монеток.
Можно, конечно, вместо событий хранить контент комнаты - список всех объектов и их состояние. Но это прям скажем не всегда нужно, только если мир разрушаемый, например. Иначе это огромный размер сейва и куча геморроя при загрузке. Но вообще обычно всегда хранят diff-ы.
Никто не пишет, что комната модифицируется до инициализации. Ты сохранил данные из файла в глобальные переменные, в которые ты смотришь при инициализации комнаты. Т.е. зашёл игрок в комнату1, при создании смотрим в глобальные переменные, получаем данные о этой комнате из переменной. Получается у тебя некая "база данных" о состоянии игры, откуда ты берёшь данные (сейф в памяти). Ты можешь при инициализации этой комнаты данные из файла для неё считать, тут как тебе удобно хранить и пользоваться. При загрузки комнаты все "проходы" и происходят. Ты немного смешал место и способ хранения с применением этих данных на комнату.
Хранить состояния и "проигрывать" функции объектов, ты сам указал, что это "гемор". Смотря как подходить. Для меня проще запомнить: рычаг - нажали, дверь - открыта, кнопку - не нажали. Сохранив состояние комнаты. И при загрузке просто, назначив состояния. Размер хранимых данных можно уменьшить учитывая состояние по умолчанию. Рычаг, по умолчанию, всегда выключен. Если включим, храним состояние, а если нет игнорируем. Так храним только изменение.
По поводу изменения положения монет и т.п., то я согласен, что уникальный идентификатор очень надёжное решение. Всё зависит от игры.
Я то же самое и предложил, только с простой реализацией в GameMaker,
Проверить в событии room_start событие конкретного объекта - это по сути и есть "пройти по 5 монетам". Только это такой децентрализованный и более универсальный проход. Состояние по-умолчанию - это отсутствие события в списке событий.
Разница только в том, как детектить объекты - по уникальному вручную прописанному идентификатору или автоматически по координатам. У каждого способа есть свою плюсы и минусы.
Думаю на этом сойтись, я не распылятся на две ветки и писать об одном и тоже. Всё зависит от конкретной игры и механик.
В целом да. Но...
Ты зачем-то предлагаешь при загрузке сцены создавать то, что уже есть на сцене. Я этого искренне понимаю =)
Может быть ты с гамаком не работал никогда, ХЗ.
Нет же. Я могу ошибаться, формулировка была не подходящая, но про создание во время инициализации не писал. Я указал на модификацию имеющихся экземпляров. Вот моя цитата:
Я писал это. Что мы используем имеющиеся на уровне объекты, которые создаются с комнатой (т.е. меняем их координаты и параметры, не важно где они стояли в прежнем месте, т.к. это по факту пулл объектов).
Работал, на джем делал игру. И от движка это не зависит, везде можно удалять, менять, добавлять... ¯\_(ツ)_/¯
Значит мало работал с движками. В каком-нибудь Flixel или Phaser ты уровень в коде создаёшь. Когда так - там можно сразу создать только те объекты, которые не собраны.
Что значит в коде уровень создаёшь? В редакторе Tiled же!
Ещё можно в CSV или PNG-картинке, но тогда на каждый слой отдельный файл.
Вообще это сторонняя примочка.
Всё равно сцена создаётся кодом, к которому разработчик как бы имеет доступ и может модифицировать, управлять созданием объектов. В редакторе сцен гамака или юнити объекты наваливаются те, которые в этой сцене были и проверки нужно проводить после инциаилизации сцены.
Даже если объекты насильно наваливаются, удали их и создай другие, которые в сейве (при условии, если нужна именно такая модель работы с сейвами)
Ты о чём-то уже своём. В движках, в которых есть свой редактор сцен/комнат, конечно, всё что в ней загружается сразу. Если такого нет и используются другие редакторы (например, Tiled или свой), то карта генерируется кодом, и можно при том учитывать всё. Это тут при чём? ¯\_(ツ)_/¯
Ты пишешь, что я говорю "Всё удали и создай заново", но я это не писал... Я это пояснил - "Модифицируй и удаляй остальное".
Ну вот видишь. Наконец-то дошли, что от движка тоже зависит как ты будешь пайплайн сохранения/загрузки строить, а говоришь, что от движка ничего не зависит.
Ещё и от того как звёзды на небе сошлись и от давления у разработчика во время работы. Такое себе на придумает... Шутка, конечно.
Тогда становится ещё более непонятно. А как ты поймёшь где какие монеты? Я так понял, ты хочешь их по координатам идентифицировать. Допустим, одна монетка передвинулась при багфиксинге, что тогда?
Если игрок до фикса её не собрал, значит она есть в файле со старыми координатами. При загрузке мы её не находим в списке оставшихся... значит удаляём? То есть передвинутая монетка просто пропадёт из игры во время сейва. И такой баг будет ой как непросто найти.
Да, такая проблема есть и я это признал. Указав, что можно при загрузке сохранения данных это учитывать, если проверять версию игры до сохранения и после. В зависимости от игры можно и удалить или вообще "перезапустить" комнату (удалив сохранения именно её, если она изменилась сильно). С идентификаторами надёжнее, при загрузке уровня кодом - это вообще не проблема.
Ты уже идёшь по второму кругу.
Я иду по второму кругу, потому что Мурка был введён в заблуждение идеей всё удалить и создать по новой - именно так он интерпретировал твои слова =)
Там тоже есть свои нюансы, но это проще.
Ты пишешь мне, потому что третий человек меня, якобы, не правильно понял... Ну... Забавно...
Строго говоря, я вчера переспросил правильно ли я тебя понял. И хотя, понял я тебя не правильно, ты мою ошибочную логику одобрил и даже продолжил объяснять ее в последующем сообщении. Через день только Herzenrg выяснилось, что ты совсем другое имел ввиду. И хоть это и не фатально, звучишь ты так, что хочется докапаться!
На твоё:
Я ответил:
Также я писал про состояние, его сохранения и восстановления. Scorched тоже ничего не писал про удаление, а про то, что в сейфе храниться по факту состояние всего текущего уровня. Т.е. если данные в файле есть, мв игнорируем оригинальный уровень. Ничего про удаление и пересоздание он не писал.
Как ты будешь воссоздавать состояние уровня это уже технический вопрос и зависит от твоего подхода: не жалко тебе ресурсы удаляй и заново создавай, жалко модифицируй из "пула". Я писал в самом первом сообщении своё предложение, спросил про подводные камни... Обсуждали это с Herzenrg. И я честно не думал, что обсуждаем такую мелочь как работу с пулом объектов и когда надо из создавать, а когда нет. Но вопрос возник остро и важно.
Да я верю, что ты мне зла не желаешь. Но ты даже сейчас не понял, что именно ты не понял, когда мои ошибочные слова подтверждал. Может быть ты просто не целиком сообщение прочитываешь, а только первое предложение? Тогда действительно твоя логика верна.
Укажи, пожалуйста, на предложение, которое говорит об "удалении всего"?
Так речь не об удалении всего. Точнее, об удалении всего тоже речь идет, но принципиальная позиция в другом. Ты написал сначала все нормально. Потом тебе Скорчет ответил, что это классная идея, чтобы отказаться от стандартного способа загрузки объектов и грузить вручную. То есть не с уровня, а с файла. Я так и подумал сначала, что ты не это имеешь ввиду. Потом подумал подумал. Понял, что твою концепцию до конца не понимаю и переспросил. И далее веду мысль: "если Скорчед тебя правильно понял... тра-та-та... (а если Скорчед тебя понял правильно, значит ты топишь за то, чтобы грузить не уровень который в IDE создан, а грузить объекты как-то кодом - это и есть принципиальная позиция)... тогда следует, что надо мол удалить сначала то, что в левел эдиторе уже было, и только потом грузить". То есть про удаление, это уже следствие принципиальной твоей позиции. И ты эту позицию не стал отрицать почему-то. Следующим ответом ты написал, что: "да-да, все верно, Скорчет правильно разъяснил" и продолжил развивать идею системы сохранения не оспорив и следствие об предудалении инстансов из комнаты при загрузке оной! То есть ТЫ.
УБЕДИЛ... МЕНЯ... В ТОМ... ЧТО ПРЕДЛАГАЕШЬ МНЕ ОПРЕДЕЛЕННУЮ ЛОГИКУ. Мне щас таблетки уже не помогут из-за тебя. Я тебе написал два тезиса. Первый тезис твоя принципиальная позиция. Второй тезис следствие этой позиции. Ты ни первое ни второе не отрицал. Если бы ты хотя бы одно из этих двух обозначил как неверное, тебя бы ВСЕ переспросили. Но ты ТОПИЛ за то, что хочешь уничтожить левел эдитор гейм мейкера и заменить его ручным лодером.
Scorched написал здесь:
Я с этим убеждение согласен: если сохранения ЕСТЬ, то загружать данные оттуда (данные о состоянии объектов и его позиции). Если их НЕТ, то оставить оригинальный уровень.
Я НЕ ПОНИМАЮ, где тут увидели слова об уничтожить левел эдитор гейм мейкера и заменить его ручным лодером? Где?
Далее были вопросы про откуда взяться сейву, что я тебе предыдущем сообщение ещё раз повторил. Я действительно не понимаю, где я или Scorched говорили о другом способен хранения уровня или его загрузке.
Пиши адрес свой, я подъеду.
Серьёзно? Пусть другие читающие и Herzenrg, с которым обсуждали удаление всего и вся, скажут, что я, действительно, топил за отказ от редактора уровня из GameMaker, чтобы грузить не уровень который в IDE создан, а грузить объекты как-то кодом - это и есть принципиальная позиция.
Я так и не понял, почему нельзя загрузить заново уровень Гамаком, удалить в нём все объекты, пересоздать эти же объекты, но уже из сейва? Просто потому, что это неоптимальный код?
Не совсем. Если комната изменилась, например, в ней были добавлены ещё монеты. И ты хочешь, чтобы игрок их увидел даже при наличии сохранения на эту комнату, то удалять их не надо. Если ты удалишь всё и создашь только из сохранения, то новые там не появятся. Либо тебе в функции загрузки данных нужны прописывать, что вот появились новые монеты и их тоже надо создать. В таком случай, это не удобно выходит и надо внимательно следить за функцией и не пропустить в ней ничего. В остальных случаях, важной причиной является оптимизация.
Я считаю, что если игрок начал старую версию игры, то у него и должна продолжаться именно старая версия. За исключением всяких багов и балансировки, которые бы хотелось применить и к предыдущим сейвам игроков и которые просто надо аккуратно учесть вручную через условия if.
А такие мелочи удобнее проигноривать, а не учитывать.
Под мобилку что ли разрабатываешь?
Оптимизация не всегда сложный процесс. Так что перфекционист не против, даже если не под смартфон.
Надеюсь без обид? Возникло недопонимание: ты ждал от меня одного, я ждал от тебя другого.
Какие обиды. Щас мы с пацанами бумер заберем из сервиса и к тебе. Обсудим всё лично.
Ладно, я понял. Эвентами это верный путь. А что если я буду названия эвента делать так: save_event_name = string(id)?
Ничего хорошего не будет.
id назначается объекту при создании и значение выбирается по порядку. То есть при изменении комнаты у новых объектов будут новые id. А динамически создаваемые объекты вообще всего разные имеют.
id нужен только для доступа к полям конкретного объекта и в конструкции with. Считай, что это адрес ячейки памяти, в которой объект хранится. И адрес этот может меняться при перезапусках игры.
Если я верно тебя понял, то это интересная идея хранения айдишников для сохранений! Т.е. как я понял мы храним не имя lever_1, а например lever_red_door в то время, когда lever_1 имеет в себе ссылку на эту самую red_door ?
Т.е. храним не имя_объекта, а по сути тип+ссылка_на_объект_именем ?
Я опишу, что я имел ввиду: я говорил о связи двух объектов на уровне. Есть рычаг, который открывает дверь. У рычага есть параметр - идентификатор двери (как хранится эта ссылка другой вопрос). При активации рычага, мы смотрим на идентификатор и активируем дверь. Если дверь находится в другой комнате, то можем указать второй параметр у рычага: идентификатор комнаты. При активации мы знаем, что такая-то дверь с такой-то комнаты открыта.
А можно сделать по другому. Например, мы хотим, чтобы рычаг активировал несколько вещей. Тогда проще хранить информацию о состоянии рычага. И при инициализации комнаты нужные двери/предметы смотрят на значение этого рычага в данных. Предположу, что ты это и описал. Так получается более гибко. И идентификатор рычага должен иметь либо уникальное имя, либо составное уникальное имя (идентификатор комнаты, где он находится, и его идентификатор в рамках комнаты).
По описанию, я верно понял то, что ты в первом абзаце описал.
Не в тему сейвов, но во взертосе (да сколько можно на него ссылаться!) я делал чисто логические игровые объекты, которые назвал сплиттер и комбайнер. Собственно сплиттер это как "двер" — может быть активирован рычагом, а потом этот сигнал активации посылает другим объектам, хоть 3 дверям.
Вот не согласен, лучше по возможности не хранить указатели/ссылки на одни игровые объекты в других игровых объектах, сэкономишь много нервов при отладке странных редких крашей, когда какой-то объект уничтожен, а ссылку отменить забыли.
Могу предложить такой вариант - рычаг имеет тип "quest_1_lever" (через enum или строку). Дверь имеет тип ключа/рычага "quest_1_lever". В пределах одного патча игры этот тип "quest_1_lever" будет иметь всегда одинаковое значение, в отчилие от глобальных ID объектов. И даже если уничтожить дверь или рычаг, то после этого не надо будет обновлять никакие ссылки и связи, потому что они никогда нигде и не хранились (при нажатии на рычаг дверь с таким типом ищется линейным перебором).
Поясню, идентификатор - это просто уникальное значение, чтобы определить объект. Ты предлагаешь использовать последовательность или строку для определения, я же ни о каком конкретном решении что это будут за данные не писал. Я указал абстрактный "идентификатор".
Вот тут не понял. Если рычаг и дверь связанные за счёт типа, то удалив рычаг, ты дверь не откроешь. Тебе надо обновлять в нём связь: привязывать другой рычаг.
Имею в виду, что внутри игры монстр стукнул по рычагу, и он развалился (хотя более иллюстративно, если стукнул по двери, чтобы мысленно прочувствовать краш при нажатии потом на рычаг :D). Да, дверь нельзя будет открыть, так и задумано. Это не про дизайнерскую работу.
Вообще, можно посмотреть наверно как в каких-нибудь РПГ сделаны сохранения. Явно кто-то всё это разбирал, если есть программы для их изменений.
Вот я и писал как-то, что это не очень хорошая штука для сейвов. А со структурами ещё больший мрак. У меня оригинальные сейвы у Взертоса были тоже на айдишниках и отвечаю RedOni как я выкручивался в патчах:
У ГМ есть айдишники, но, если их прохавать, то замечаешь очевидную зависимость айдишника, у объекта, который ты руками поставил в редакторе и того, который ты создал через код. В итоге, у тебя айдишники на карте "статичные" и всегда одни, до тех пор, пока ты уровень\карту\руму не переделаешь удаляя-создавая всякое. Это я пишу о ГМС2, сейчас уже ГМ2024 и там поди это изменилось.
Сохранял созданные во время игры объекты сохранением имени класса, позиции и прочей инфы в список. Т.е. я буквально при загрузке удалял их все и создавал снова. Актуально это было, кажется, только для предметов, которые подбирает игрок.
А вот это очень прикольная система! Что-то вроде статов у меня в играх хранятся тоже — типа флаги, которые читаешь и потом реагируешь.
А вот в бумажном сохранения совсем уходят от айдишников в привычном смысле: объекты хранятся по ключам (хэш таблица?), где ключ это позиция в клетках. Подземелья, враги и прочее генерируется же. Поэтому у меня есть однозначно стартовое положение объектов, даже если они сдвинулись во время игры\сейва (враги убежали, например)
А у тебя бумажное же процедурно генерируется?
При процедурной генерации ты в любом случае в сейве весь мир хранишь.
Я так в своём Опознанном Нелетающем Субъекте делал.
Процедурно, но есть места с левелдизайном. Ох, уже не помню точно, но кажется, что я храню только сид + изменения. Т.е. если я открыл сундук, то я записываю, что сундук я этот открыл. Мир же я по сиду восстанавливаю.
Вообще нужно данные хранить в JSON. Он в гамаке отлично упаковывается в строку и обратно. Есть пара нюансов с кавычками и null/undefined, но это всё просто костыляется.
Ну вот, кстати, если сделать что-то вроде ECS со своими собственными ID, то её будет легко сохранять-загружать.
Когда я в 202X+ обновлял Взертоса, то изменил систему сохранений. Как раз чтобы избавиться от ГМ id т.к. ещё и как раз побились эти самые id.
Кажется, я где-то подсмотрел эту систему, но суть такая: есть список объектов для сохранения, у них переменная-ID. И вот у большого кол-ва объектов я просто в Create событии делаю что-то вроде saveID = global.counter; global.counter++;
Технически, я там могу указать имена переменных, которые сохранять, либо сразу указываю функцию сохранения и записи (как вести себя с файлом). И всё это напоминает json структуру.
Сейчас попытался посмотреть код в игре. Лучше бы не видел. Опять такое накрутил!
Как и с любым другим инструментом, если делать что-то серьёзное.
А так с ним проблем нет.
есть разница между "мы вам все дали для разработки", но часть из этого лучше не юзать и нужно их заменять сторонними решениями, и "вот вам ряд базовых инструментов, а дальше делайте сами", и ты юзаешь всю базу, и доп. либы для другого функционала
Так и что?
В GameMaker есть всё, что нужно для разработки. Что там лучше не юзать и заменять - я ХЗ. Вроде работает нормально. Иногда новый функционал сыроват, но его обычно допиливают.
А штатная система сохранения в GameMaker работает нормально для определённого вида проектов. Сам иногда пользуюсь.
Ну я давно там не сижу уже, и, например, аудиодвижок был одной из причин бросить гамак
Без понятия, что тебе там не понравилось. Звук даже с позиционированием есть.
Лол, а в юнити прям есть система сохранения из коробки, которая запоминает полное состояние сцены?
её нет, и нужно писать самому)
Как примерно в любом игровом движке =)
ну я и говорю про гейммейкер где она есть и может чудить
Чудить может только разработчик. Не зависимо от движка. Обычно, по неопытности, многие разработчики списывают ошибки своего кода на ошибки движка. Если разработчику хватает терпения разобраться в проблеме, у него в голове откладывается, что движок работал как часы, а ошибка была человеческая. Когда данная ситуация происходит несколько раз, разработчик уже полностью убеждается в том, что движок не может сбоить и всегда уверенно находит проблему в своем запутанном коде. Такой разработчик имеет обширное представление о механике работы своего инструмента, всегда относится с уважением к другим технологиям, понимая, что любая неудача происходит лишь из-за отсутствия знаний и старается не формировать оценочное мнение тех областях, где у него этих знаний нет.
А еще чудить могут разработчики движков
Надеюсь, это пройдет у тебя.
Так в гамаке тоже такой функции нету, её нужно самому писать. В гамаке встроенные сейвы - это дамп данных со своими ограничениями и подводными камнями. Но если понимать как он работает - вполне применимо.
Upd
Так даже эту функцию уже из движка убрали. Т.е. теперь систему сохранения самому писать в любом случае.
а, вот как. Ну раньше когда я юзал его там одной кнопкой это добавлялось, ща хз