Всем привет.
Регулярно начинаю писать систему предметов для синглплеерной игры с сохранениями и всегда получается херня.
Предметы стакаются и имеют список примитивных свойств (string, int)
Есть несколько контекстов:
- база данных ( обычно ScriptableObject, данные о предметах, их названия, иконки, id, фабрика для создания дефолтного предмета )
- сохранения ( предмет включает id и список примитивных свойств, опционально guid и позицию на сцене)
- сцена ( непосредственно пикапы предметов, контейнеры с предметами на сцене, инвентари, магазины)
- ui ( инвентарь, перекладывание в контейнер, покупка в магазине, экипировка предметов в руки персонажей)
Может у кого-то есть под рукой хороший пример реализации такой системы из выпущенной игры?
туторы или готовые фреймворки - не предлагайте. Они многие аспекты или отбрасывают или перегружают.
Есть 3 ключевые проблемы:
1.когда реализую добавление предметов в контейнер, учитывая существующие стаки, мне кажется, что я что-то делаю не так. Эта функция возвращает остаток предметов, и если он 0, то исходный объект предмета ( пикап или объект в другом контейнере) уничтожится, если больше 0, то поменяется количество. А в самой функции он должен попытаться, предварительно добавить в каждый существующий стак такого предмета, и вычетать из исходного количества. Какие еще варианты реализаций вы знаете?
2. Как совместить данные о стаке и свойства предмета? Получается что у каждого предмета в стаке могут быть те же свойства. Единственное что я придумал более менее адекватное - массив свойств для каждого предмета в стаке, но только если свойства не default
3. Как связать контексты, не создав спагетти?
mitay-walle
> Какие еще варианты реализаций вы знаете?
Minecraft
Инвентарь https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bu… nventory.java
ItemStack https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bu… temStack.java
Проще говоря, есть Item, все предметы наследуются от него. Блоки, оружие, любой предмет который вообще может существовать.
Когда ты добавляешь в инвентарь он перебирает каждый слот и смотрит есть ли вообще такой ItemStack с предметом. ищет ближайшего. Нашел - отдал. Ты его заполняешь, твой stack как то изменяется или полностью исчезает. Если осталось мы ищем дальше стак с предметом, не нашли, ищем полностью свободную ячейку (cell, это не stack). Свободные ячейки или их порядковый номер можно хранить отдельной коллекции.
В отличии от Item, именно ItemStack хранит информацию о разрушении предмета (или его использовании. Фонарик с аккумулятором например тратиться только когда включен).
Был у нас например аккумуляторов пачка (стак), пустые. Мы заряжаем какой то из пачки и он становится отдельным ItemStack, при чём таким, что его уже нельзя сложить в пачку. За это отвечает флаг isStackable в стаке.
Item - это структура
ItemStack это структура (то есть он не изменен по определению и чтобы внести изменения надо создать другой ItemStack). Но можно сделать и в виде Class, главное чтобы за событиями об его изменении кто то следил.
Inventory - класс
Можно сделать вариант с ECS, тут всё зависит от опыта и свободного времени. Он будет ещё гибче, но можно будет заблудиться если прийти на проект без TDD.
Salamandr
> Minecraft
> Инвентарь https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bu…
> nventory.java
> ItemStack https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bu…
> temStack.java
Спасибо, буду смотреть
А что такое стаки у тебя?
В моем понимании это укладывание однотипных предметов в один слот инвентаря. Например деньги, камни. А предметы с разными свойствами никак не могут быть в одном слоте. Только если слот сам содержит не предмет, а массив в котором лежат уже конкретные предметы
DemiosFantasimo
> А что такое стаки у тебя?
ну как раз об этой проблеме и речь. Как сделать это адекватно. В стаке могут быть предметы, которые имеют собственные свойства. Например еда, которую можно есть кусками. Или батарейки частично севшие
Ну типо такого. Но это не рабочий скрипт откудато, а на скоряк набросаный для примера архитектуры примитивного инвентаря
class Item { public enum Propertie{damage, resist, value}//Свойства предметов public enum TypeItem{sword, armor, food}//Типы предметов public enum TypeStack{noStack, Stack}//Тип определяющий стакается предмет или не стакается public TypeItem type; public TypeStack typeStack; public IDictionary<Propertie,int> properties; public string image; } class Inventory { List<Item> Inventory; public AddItem(Item item) { //Вилка по типу стакания switch(item.typeStack) { //Если предмет не стакается то просто добавляется в инвентарь case TypeStack.noStack: inventory.Add(item); break; //если предмет стакается case TypeStack.Stack: //Ищем тип предмета к которуму пристакаем новое int index=-1; for(int i=0; i<=inventory.Count-1; i++) { if(inventory[i].type==item.type) { index=i; break; } } //Если не был найден однотипный предмет к которому можно пристакать то просто текущий добавляем в инвентарь if(index<0) { inventory.Add(item); } else //Если найден однотипный предмет к которому можно пристакать, то просто прибавляем значение к свойству value { inventory[index].properties[Propertie.value]+=item.properties[Propertie.value]; } break; } } } Inventory inventory=new Inventory(); Item item; //Создаем меч item=new Item(); item.type=TypeItem.sword; item.typeStack=noStack; item.properties=new Dictionary() { {Propertie.damage,10}, }; item.image="bubs.png" inventory.AddItem(item); //Создаем еду item=new Item(); item.type=TypeItem.food; item.typeStack=Stack; item.properties=new Dictionary() { {Propertie.value,3}, }; item.image="tort.png" inventory.AddItem(item);
DemiosFantasimo
> Ну типо такого
Спасибо, что уделили время, но я просил именно варианты из релизнувшихся игр. Таких примеров много в интернете, и они не учитывают все упомянутые контексты
mitay-walle
> но я просил именно варианты из релизнувшихся игр. Таких примеров много в
> интернете, и они не учитывают все упомянутые контексты
Ну удачи в поиске)))
DemiosFantasimo
> Ну удачи в поиске
Спасибо, Salamandr уже скинул из Minecraft'а
Я делал инвентарь через серилизованные структуры. C# жи, ведь.
Инвертор получается очень лёгким и максимально гибким. В одной структуре можно описать тысячу полей свойств, можно вложить структуру структуру сделать в них списки и так далее. В целом это почти как конструктор но только структуру можно сохранить без танцев и бубнов.
То есть архитектура заключает в себе только одну структуру со свойствами, которая является ячейкой инвентаря и списком этих структур которые и будет являться инвентарем.
А далее просто собираем ui, по нажатию кнопки и всё. Если надо внести изменения в уже собранный ui то просто пересобираем его.
sledo
Он с программированием (судя по проблемам описаным в nil- посте) видимо не очень, по этому и ищет готовый вариант
sledo
> Я делал инвентарь через серилизованные структуры
Я тоже пробовал этот вариант, но я не понимаю - какой в этом смысл, по следующим причинам
- Проблема 16 байт
https://stackoverflow.com/questions/1082311/why-should-a-net-stru… than-16-bytes
https://mdfarragher.medium.com/whats-faster-in-c-a-struct-or-a-cl… -99e4761a7b76
- массив - ссылочный тип, так что при дублировании структуры с массивом вы либо таскаете один и тот же массив по ссылке, либо создаете мусор
DemiosFantasimo
> ищет готовый вариант
Я ищу real-world examples, а не очередной тутор ради просмотров или попытку навариться через Asset Store. Ваш код вообще не соответствует тому, что я описывал в посте и не решает проблему взаимодействия контекстов (save, ui, database, scene)
Вы не желаете мне помочь и скатываетесь в оскорбления - давайте закончим на этом.
Искренне успехов вам в ваших проектах (: очень вас прошу, не пишите
Спасибо еще раз Salamandr за корректный ответ
mitay-walle
> Вы не желаете мне помочь и скатываетесь в оскорбления
Где я оскорблял?
Если ты сам на что-то оскорбился, то это проблема твоего слабого характера, а я тебя не оскорблял.Адиос!
mitay-walle
> - Проблема 16 байт
В первый раз вижу в этом проблему. Хотя конечно если создавать как в ссылке миллион экземпляров, то наверное да, тут любое решение будет проблемой. Даже бд.
mitay-walle
> - массив - ссылочный тип
Это что ещё за чушь?
И?!! Да, в шарпе массивы это списки и что? Вы теперь их вообще использовать не будете? А что тогда? Создадите стопитьсот переменных имитируя структуру массива? Причём без строк, так как это тоже ссылки.
Классы, делегаты, интерфейсы - все в топку?
Можете тогда закрыть движок, так как все объекты на сцене, это тоже ссылки.
Более того, это ещё и массив ссылок.
DemiosFantasimo
> Он с программированием (судя по проблемам описаным в nil- посте) видимо не
> очень, по этому и ищет готовый вариант
Судя по всему, он о программировании только наслышан.
Тема закрыта.