Як моделювати декілька "застосувань" (наприклад, зброя) для інвентарних ресурсів / предметів / предметів (наприклад, катана) у реляційній базі даних


10

Тому я працюю над розширенням використання елементів на www.ninjawars.net , і я не точно знаю, як їх гнучко представити у реляційній базі даних, яку ми використовуємо.

Я, можливо, гавкаю неправильне дерево, тому сміливо вносьте пропозиції в інших напрямках, але наразі я думаю, що кожен елемент повинен мати реляційні "теги".

Наприклад, Катана зараз рядок у базі даних "items". Щоб перетворити його на зброю та вміщувану річ, я думав, що у мене буде база даних «ознак» та таблиця елементів_трайтів, яка просто пов’язана між ними.

// Objects and their basic data

item_id | item
1 | Naginata


// Things that objects can do

trait_id | trait
1 | weapon
2 | holdable

// How those objects do those things, e.g. powerfully, weakly, while on fire

_item_id | _trait_id | item_trait_data
1 | 1 | damage: 5, damage_type: sharp, whatever, etc

Я не дуже впевнений, як моделювати додаткові дані, які приводяться (наприклад, збитки, які буде нанесений мечем, тип damage_ і т. Д.).

Я також не особливо радий, що весь предмет буде зберігатися в більш ніж одному місці, наприклад, щоб створити копію елемента з іншим іменем, як "короткий меч", мені доведеться копіювати з кілька таблиць для створення повторюваного елемента.

Чи є кращий спосіб викласти цей матеріал, який мені не вистачає?

Редагувати: Я просто зауважу, що у мене на сайті вже використовується база даних postgresql, тому я хочу використовувати її для зберігання даних.

Редагувати: я додав відповідь на реалізацію, яку зараз переглядаю.

Відповіді:


10

Я збираюся м'яко не погодитися з усіма і сказати, що реляційний підхід тут розумний. Тут цікаво те, що предмети можуть мати кілька ролей. Основне питання полягатиме в тому, що відображення між цим реляційним макетом і макетом ОО в коді не буде відчувати себе "природним", але я думаю, що на базі даних багато ролей можна виразити чисто (без дивних кодувань або надмірностей, просто приєднується) .

Перше, що слід вирішити - це кількість даних, що є специфічними для окремих елементів, і скільки їх поділяють усі елементи даного типу.

Ось що я можу зробити, якщо всі дані є специфічними для кожного пункту:

// ITEMS table: attributes common to all items
item_id | name        | owner         | location             | sprite_id | ...
1       | Light Saber | 14 (Tchalvek) | 381 (Tchalvek house) | 5663      | ...

// WEAPONS table: attributes for items that are weapons
item_id | damage | damage_type | durability | ...
1       | 5      | sharp       | 13         | ...

// LIGHTING table: attributes for items that serve as lights
item_id | radius   | brightness | duration | ...
1       | 3 meters | 50         | 8 hours  | ...

У цьому дизайні кожен елемент знаходиться в таблиці елементів, а також атрибути, які мають усі (або більшість) елементів. Кожна додаткова роль, яку може зіграти предмет, - це окрема таблиця.

Якщо ви хочете використовувати його як зброю, ви шукаєте це в таблиці Зброя. Якщо він є, то він може бути використаний як зброя. Якщо його там немає, то його не можна використовувати як зброю. Наявність запису говорить про те, чи це зброя. І якщо він там, всі його атрибути, специфічні для зброї, зберігаються там. Оскільки ці атрибути зберігаються безпосередньо замість кодованої форми, ви зможете виконувати запити / фільтри з ними. (Наприклад, на сторінці показників вашої гри ви можете зібрати гравців за типом пошкодження зброї, і ви зможете це зробити з деякими приєднаннями та типом ушкодження групи.)

Елемент може мати кілька ролей і існувати у більш ніж одній специфічній для ролей таблиці (у цьому прикладі як зброя, так і освітлення).

Якщо це просто булеве на зразок "це зберігається", я б помістив його в таблицю "Елементи". Можливо, варто кешувати "це зброя" і т.д. там, щоб вам не довелося виконувати пошук на таблицях Зброя та інших ролях. Однак це додає надмірності, тому вам слід бути обережними, щоб тримати його синхронізовано.

Рекомендація Арі щодо створення додаткової таблиці для кожного типу також може використовуватися при такому підході, якщо деякі дані не змінюватимуться для кожного елемента. Наприклад, якщо пошкодження зброї не змінюються в залежності від предмета, але ролі все ще різняться для кожного елемента, ви можете розподілити атрибути спільної зброї в таблицю:

// WEAPONS table: attributes for items that are weapons
item_id | durability | weapon_type
1       | 13         | light_saber

// WEAPONTYPES table: attributes for classes of weapons
weapon_type_id | damage | damage_type
light_saber    | 5      | energy

Інший підхід був би, якщо ролі, яку відіграють елементи, не відрізняються залежно від предмета, а лише за типом предмета. У такому випадку ви помістіть item_type у таблицю елементів і зможете зберігати властивості типу "зброя" та "чи зберігається вона" та "чи це світло" у таблиці ItemTypes. У цьому прикладі я також роблю назви елементів, які не змінюються для кожного елемента:

// ITEMS table: attributes per item
item_id | item_type    | owner         | location
1       | light_saber  | 14 (Tchalvek) | 381 (Tchalvek house)

// ITEMTYPES table: attributes shared by all items of a type
item_type   | name        | sprite_id | is_holdable | is_weapon | is_light
light_saber | Light Saber | 5663      | true        | true      | true

// WEAPONTYPES table: attributes for item types that are also weapons
item_type   | damage | damage_type
light_saber | 5      | energy

Цілком імовірно, що типи предметів і зброєтипи не змінюються під час гри, тому ви можете просто завантажити ці таблиці в пам'ять один раз і шукати ці атрибути в хеш-таблиці замість об'єднання з базою даних.


+1. Це єдина відповідь, яка використовує реляційну базу даних як реляційну базу даних. Будь-які, крім цієї чи іншої крайності пропозиції щодо Nevermind та просто використання БД як сховища даних, - жахливі плани.

+1 Але такі підходи виглядають настільки негнучкими. Прошу вибачення, якщо я звучу непослідовно, але я хочу мати можливість створити структури бази даних для елементів по суті один раз ... ... а потім кодувати більше функцій для елементів, робити більше вставок у базу даних для елементів, але не потрібно міняти база даних знову в майбутньому (звичайно, з рідкими винятками).
Kzqai

Якщо ви хочете, щоб реляційна база даних знала про ваші поля та оптимізувала їх представлення, вам потрібно їх десь додати. Це як статичні типи. Якщо ви хочете, щоб ваш компілятор знав про ваші поля та оптимізував їх представлення, вам потрібно оголосити типи. Альтернативою є динамічно типізований підхід, який використовується деякими базами даних документів, або певним чином кодування ваших даних у реляційну базу даних. База даних не буде знати, які поля є, або зможе оптимізувати їх зберігання, але ви отримаєте певну гнучкість.
amitp

4

На жаль, такі ситуації, коли реляційні бази даних (такі як SQL) не вживаються, а нереляційні бази даних (наприклад, MongoDB ) перевершуються. Зважаючи на це, неможливо моделювати дані у реляційній базі даних, і оскільки, схоже, ваша база коду вже покладається на SQL, ось модель, з якою я б пішов:

Замість створення елемента та використання таблиці створіть таблицю та "[item] _type" таблицю для кожного типу елементів.

Ось кілька прикладів:

weapon_type_id | weapon_type
1 | sharp

weapon_id | weapon| weapon_type_id | damage
1 | Short Sword | 1 | 5

potion_type_id | potion_type
1 | HP
2 | Mana

potion_id | potion| potion_type_id | modifier
1 | Healing Elixer | 1| 5
2 | Mana Drainer | 2| -5

Це рішення дає багато довгострокової гнучкості та масштабованості, воно зменшує (якщо не усуває) витрачений простір і є самодокументуванням (на відміну від "item_use_data"). Він передбачає трохи більше налаштування з кінця розробника, але, на мою думку, це найелегантніше рішення, доступне для ситуацій, коли для зберігання даних вам потрібно використовувати реляційну базу даних. Взагалі кажучи, нереляційні бази даних набагато кращі для розробки ігор, оскільки вони більш гнучкі в тому, як вони моделюють дані, в той час як більш ефективні, ніж бази даних на основі SQL - роблять їх вибір "виграшним".

Редагувати 1: Виправлено помилку з полем potion_type_id

Редагування 2: Додано більше деталей щодо нереляційних та реляційних баз даних, щоб забезпечити додаткову перспективу


Я дійсно не бачу великої гнучкості, що виходить із цієї системи. Наприклад, якби я хотів, щоб скляний флакон, який можна було б використовувати як зброю, і бути «гострим» ... ... я був би не щасливий. Плюс до цього, для кожного нового виду елемента я повинен був би створити структуру бази даних для нього. Навіть якби збиралися лише один або два типу.
Kzqai

Точки, які ви пропонуєте, є дуже вагомими та виділяють додаткові приклади того, чому реляційні бази даних не дуже підходять для цієї конкретної ситуації. Представлена ​​мною модель лише показує найкращий спосіб викладати дані таким чином, щоб зберігалася пам'ять і зберігалася функціональність SQL. Для зберігання предметів декількох типів найкращим способом представити ці дані в цій моделі буде створення таблиці зілля-зброї з відповідними властивостями зілля та зброї. Знову ж таки, це рішення НЕ ідеально, але це найелегантніше рішення при використанні реляційної бази даних.
Арі Патрік

@Ari: Не поважає неповаги, але його проблема полягає саме в тому, що реляційний підхід є більш справедливим, не меншим. Добре відомо, що бази даних NoSQL (або NoRel) чудово підходять для читання з великим обсягом, розподілених / високодоступних даних або погано визначених схем. Цей випадок - це просто класична асоціація "багато на багато", і такі, як Монго, роблять це не так добре, як RDBMS. Див stackoverflow.com/questions/3332093 / ...
alphadogg

@alphadogg Як це стосунки багатьох до багатьох? У базі даних зброя знає про типи зброї, але про типи зброї не потрібно знати про зброю, з якою вони пов'язані. Якщо з якоїсь причини ви хочете знати всю зброю, яка є певним типом зброї, ви просто напишіть запит, який повторюється над колекцією документів про зброю.
Арі Патрік

@Ari: У питанні ОП одна зброя може мати багато типів, а один тип може бути пов'язаний з багатьма зброями. Наприклад, вміститься зілля та меч. Меч є і рукояткою, і зброєю.
альфадог

3

Спочатку скиньте об'єктно-орієнтований підхід до успадкування та перейдіть із системою, що базується на компонентах .

Як тільки ви це зробите, макет SQL раптом стає набагато простішим. У вас є одна таблиця для кожного типу компонента зі спільним ідентифікаційним номером. Якщо ви хочете, щоб предмет №17, перегляньте "ідентифікатор елемента 17" у кожній таблиці. Будь-яка таблиця, що має ключ, додає її компонент.

Ваша таблиця предметів містить усі необхідні дані для предметів загалом (найменування, ціна продажу, вага, розмір, все інше, що ділиться між усіма предметами.) Ваша таблиця Зброя містить усі відповідні дані для зброї, ваша таблиця зілля містить усі відповідні дані що стосується зілля, ваша таблиця броні містить усі відповідні дані для броні. Хочете нагрудний знак, це запис у предметі та запис у броні. Хочете мечем, який ви можете випити, ви просто помістите запис у кожну таблицю, і бам, все закінчено.

Майте на увазі, що цей самий візерунок не є особливим для конкретних предметів - ви можете використовувати його для істот, зон, будь-чого іншого, що вам захочеться. Це напрочуд універсально.


2

Використання SQL було вашою серйозною помилкою. Він абсолютно НЕ підходить для зберігання статичних даних про дизайн гри.

Якщо ви не можете відійти від SQL, я б розглядав можливість зберігання елементів у серіалізованому від. Тобто

item_id (int) | item (BLOB)
1             | <binary data>

Звичайно, це некрасиво і викидає всі "смакоти" SQL у вікно, але чи справді вони вам потрібні ? Швидше за все, ви читаєте всі дані своїх предметів при запуску гри в будь-якому випадку, і ніколи SELECTіншим, окрім як item_id.


3
Оскільки (наскільки я розумію) гра є багатокористувацькою і підтримує PvP, існує ймовірність того, що більшість ігрових відомостей не зберігається на клієнті. Якщо це точно, то кожного разу, коли дані читатимуться з таблиці, її доведеться дезаріалізувати, перш ніж віддалено бути корисними. Як результат, цей формат робить запити елементів за їх властивостями ДУЖЕ дорогими; наприклад: якщо ви хочете отримати всі предмети, що належать до типу "зброя", вам доведеться отримати кожен елемент, дезаріалізувати кожен, а потім вручну відфільтрувати тип "зброя", на відміну від простого запиту .
Арі Патрік

1
З мого досвіду, всі предмети все одно зачитуються в кеш пам'яті на початку гри. Тож ви лише дезаріалізуєте їх один раз при запуску, а потім не використовуєте базу даних взагалі. Якщо в цьому випадку це не так, то я згоден, зберігання серіалізованих об’єктів - погана ідея.
забудьте

Хммм, єдине, що це залишає мені інтерфейс -потрібний- для редагування даних, що було б незручно.
Kzqai

2

Залежно від того, скільки ознак вам, швидше за все, знадобиться, ви можете використовувати просту бітову маску для об'єктів з різними бітами, що відповідають різним ознакам:

000001 = holdable
000010 = weapon
000100 = breakable
001000 = throwable
010000 = solids container
100000 = liquids container

Потім ви можете зробити прості бітові тести, щоб побачити, чи може об’єкт використовуватись для певної функції.

Тож "скляна пляшка" може мати значення 101111, тобто її можна зберігати, її можна використовувати як зброю, вона легко ламається, ви можете кинути її, і вона може містити рідини.

Будь-який редактор, який ви створюєте для елементів, може мати простий набір прапорів, щоб увімкнути / вимкнути риси об’єкта.


1
Мені подобається ідея, тому що вона дозволяє елементу мати власні риси, і я не знаю, що мені потрібно буде шукати в базі даних ознаки, але я не дуже впевнений, як зробити цю роботу у реляційному контексті. Я здогадуюсь, що кожен біт відображатиме інкрементальні ідентифікатори ознак у базі даних ...
Kzqai

Я думаю, що бітмаскінг корисніший для фіксованої кількості ознак, хоча?
Kzqai

Дуже так. Що станеться, якщо ви використали свій останній "шматочок"? Наскільки легко збільшити тип даних, щоб отримати біт і переробляти це через ваш додаток, або додати стовпець і зробити те ж саме? Також, чи налаштований ваш конкретний двигун бази даних для індексації для бітових операцій? Найкраще в реляційній базі даних залишатися атомною зі стовпцями даних. Не згуртовуйте кілька доменів в один стовпець. Ви не можете скористатися перевагами платформи.
альфадог

1
Якщо ви хочете таблицю пошуку в БД для ознак (напевно, гарна ідея, оскільки ви потім можете динамічно додавати їх до будь-якого редактора), тоді вона просто матиме щось подібне: id, бітмаска, опис. 1,1, "Холдинг"; 2,2, "Зброя"; 3,4, "Breakable" тощо. Що стосується фіксованого числа, так, це правда. Але якщо ви використовуєте 64-бітовий int, у вас має бути достатньо обсягу.
Muttley

1

У нашому проекті у нас є item_attributes для різних "додаткових даних", які може мати елемент. Тут викладається щось подібне:

item_id | attribute_id    | attribute_value | order 
1        25 (attackspeed)       50             3    

Тоді у нас є таблиця атрибутів, яка виглядає так:

id |   name       | description
1    attackspeed    AttackSpeed:

Арі Патрік має рацію, хоча, в кінцевому рахунку, реляційні тире не призначені для цього. Мінусом є те, що у нас є досить складні процедури для генерування нових елементів (які виконуються за допомогою зовнішнього інструменту - який я настійно рекомендую. Не намагайтеся вручну додавати їх, ви лише плутаєте себе)

Інший варіант, який ви використовуєте, використовується мова скриптів для створення шаблонів елементів, тоді ви можете легко розібрати їх і використовувати їх для створення нових елементів. Ви, звичайно, повинні зберігати дані про об'єкти в базі даних, але в цей момент вам не потрібно турбуватися про особливості створення нових елементів, ви можете просто скопіювати і старий файл сценарію, змінити певну інформацію, і ви добре йти.

Чесно кажучи, якби ми повторно робили, як створюємо нові статичні елементи, ми, ймовірно, пішли б на набагато простіший підхід, використовуючи шаблони елементів сценаріїв.


1

Це ваші типові стосунки «багато до багатьох», нічого надто езотеричного для будь-якої здатної реляційної бази даних. У вас є багато рис для будь-яких об'єктів, і будь-яка ознака може використовуватися одним або кількома різними типами об'єктів. Продемонструйте три відносини (таблиці), причому одне - відношення асоціації, і ви закінчили. Правильна індексація забезпечить вам швидке зчитування даних.

Шару ORM у вашому коді, і у вас повинно виникнути дуже мало проблем між БД і середнім програмним забезпеченням. Багато ORM також можуть автоматично генерувати класи, тому вони стають ще більш "невидимими".

Що стосується баз даних NoSQL, то немає жодної причини, коли ви не можете зробити це з ними. В даний час модно вболівати за цю технологію в торгових лахміттях і блогах, але вони пропонують цілий ряд своїх проблем: відносно незрілі платформи, чудові для простих нечасто змінених читань (як-от-багато-багато твітів у профілі), але поганий для складних динамічних зчитування чи оновлень, поганий підтримуючий інструментальний ланцюг, надлишкові дані та супутні проблеми з цілісністю тощо. Однак вони пропонують привабливість кращої масштабованості, оскільки вони у різній мірі уникають можливостей транзакцій та їх накладних витрат, а також простіші моделі розповсюдження / тиражування .

Звичайним хобгобліном реляційних баз даних проти баз даних NoSQL є продуктивність. Різні RDMBS мають різний ступінь накладних витрат, що робить їх менш кращими для масштабування до рівнів Facebook або Twitter. Однак, навряд чи ви зіткнулися б із цими проблемами. Навіть тоді прості серверні системи на основі SSD можуть зробити дебати про продуктивність марними.

Будемо зрозумілі: більшість баз даних NoSQL принципово розподілені хеш-таблицями, і обмежать вас так само, як і хеш-таблицю у вашому коді, тобто. не всі дані добре вписуються в цю модель. Реляційна модель набагато потужніша для моделювання відносин між даними. (Помилковим фактором є те, що більшість RDBMS є застарілими системами, які погано налаштовані на потреби популярних 0,0001% Інтернету, а саме Facebook та ін.)


Якщо ви збираєтеся посилатися на них колективно, навіть не турбуйтеся говорити про бази даних. NoSQL не є сутністю, про яку можна обговорити змістовно, це марна посилання на масу, яку використовує вентилятор SQL, щоб застосувати хибну логіку, що якщо в одній базі даних визначеної групи відсутні функції, то всі вони не мають цієї функції.
aaaaaaaaaaaa

NoSQL - це термін, який підштовхувачі NoSQL вирішили адаптувати. Я не знаю чому, це не дуже описово. Але це не якесь пейоративне. Попрацювавши з кількома, я думаю, що опис альфадоггів їх справедливий.

Можливо, мій коментар був трохи суворим, все ж я ненавиджу це ім’я та групування, неможливо провести кваліфіковану дискусію щодо тонких деталей різних систем баз даних, коли люди дотримуються цього спрощеного світогляду.
aaaaaaaaaaaa

@eBusiness: Я вважаю ваш коментар кумедним, враховуючи, що саме табір проти RDBMS, якщо такий є, той самопомазав свою групу технологій як "NoSQL". Справді, багато хто з них хотів би побачити це ім'я застарілим, заднім числом. Тим більше, що багато хто з них, нарешті, розшаровують SQL над своїми фізичними реалізаціями. Щодо розмови про них, я не сприйняв ваш коментар як суворий, або у мене товста шкіра, ваш вибір! :) Я маю на увазі, можна говорити про Tea Party, чому б не групу баз даних NoSQL?
альфадог

Це справедливо, я усвідомлюю, що відносини - це абсолютно багато до багатьох. Те, що перешкоджає мені створювати його у справді нормованому стилі, - це думка, що «речі», які становлять предмет, потім будуть розповсюджуватися як мінімум на дві таблиці. Мені не подобається думка про неможливість створення атомних вставок, я думаю.
Kzqai

0

Саме тут я вважаю, що більш сучасні АБО-картографи дійсно корисні, як-от Entity Framework 4 та його функція (CTP) -перше . Ви просто записуєте класи та їх зв’язки у звичайний код і навіть не потребуючи їх прикрашати чи вручну запускати будь-які інструменти, EF генерує резервну копію у форматі SQL для вас із необхідними таблицями посилань та всім ... це справді розкриває творчість imo ^^


Ну, я створю код і класи, і все, що в коді, лише спочатку ... ... просто треба перекласти це в існуючу структуру бази даних після.
Kzqai

0

Ось що я зараз розглядаю:

Оскільки кожна «ознака» по суті вимагає змін у коді, тож я вирішив просто зберегти риси (та будь-які потрібні їм дані за замовчуванням) у самому коді (принаймні, поки що).

Напр $traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));

Потім елементи отримують у базі даних поле "trait_data", яке використовуватиме json_encode()функцію зберігання у форматі JSON.

item_id | item_name | item_identity | traits
1 | Katana | katana | "{"holdable":1,"weapon":1,"sword":{"min_dam":1,"max_dam":1234,"0":{"damage_type":"fire","min_dam":5,"max_dam":50,"type":"thrown","bob":{},"ids":[1,2,3,4,5,6,7]}}}"

План полягає в тому, що елементи будуть успадковувати всі статистичні дані за замовчуванням від їхніх батьківських ознак, і вони матимуть лише надмірні риси, які визначають відмінності від значень, встановлених за замовчуванням.

Недоліком поля ознак є те, що, хоча я міг би вручну редагувати частину json ... ... це не буде дуже просто чи безпечно зробити це з даними, які є в базі даних.


1
Що відбувається, коли ви вводите нову ознаку? Як часто це могло статися? Якими будуть наслідки для подальшого навантаження на обслуговування вашого коду? На противагу цьому зберігати його як багато-багато-багато, за допомогою якого ваші об'єктні екземпляри динамічно накопичуються (за вибором ORM) для зберігання цих даних.
альфадог

Дякую, це хороший момент, додавання нових рис не буде тривіальним. Ви додали до мого аналізу параліч. : p
Kzqai

Вибачте, але це досить критично. Вам просто потрібно продумати це. Продемонструйте вправі, що вам доведеться робити, коли потрібно оновлювати деяку зброю, щоб додати нову ознаку, щоб покращення вашої гри могло працювати.
альфадог

-1

Це трохи хакі, і я не даю жодних гарантій, що це гарний дизайн БД; мої заняття з БД були певний час тому, і вони не приходять швидко на розум. Але якщо для елементів гарантується, що, скажімо, менше 10 елементів, ви можете надати кожному елементу поле атрибут1, поле атрибут2 і так далі до атрибута10. Це призведе до усунення вашої потреби в таблиці атрибутів "багато на багато" за рахунок обмеження кількості атрибутів.

Озираючись на це, я впевнений, що це жахливий дизайн, але це означає, що ти можеш працювати з зручною реляційною базою даних і не потрібно їхати на невідому територію. Ви самі вирішуєте, чи вартий компроміс.


Це жахливий дизайн, ніколи цього не робіть. Вам буде краще просто не використовувати базу даних.
ZorbaTHut

-1

Nevermind дав хорошу відповідь, але мені цікаво, чи вам навіть для цього потрібна база даних? Зберігання всіх даних у звичайному файлі та завантаження їх у програму при запуску здається найрозумнішим способом цього зробити.

Редагувати:
правильно, у середовищі, керованому запитом, вам краще зберігати дані в базі даних. Запишіть свої дані у файл та введіть фрагмент коду, щоб перетворити їх у базу даних типу запропонованого Nevermind.

Крім того, якщо кількість об'єктів не надто велика, декларування партії буквально у файлі коду може бути найшвидшим методом.


Помилка, у мене вже є база даних, яка використовується та інтегрована з рештою сайту.
Kzqai
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.