Как смоделировать несколько «использований» (например, оружия) для пригодного для использования инвентаря / объекта / предметов (например, катана) в реляционной базе данных

10

Поэтому я работаю над расширением использования элементов на www.ninjawars.net , и я не совсем уверен, как гибко представлять их в реляционной базе данных, которую мы используем.

Возможно, я лаю не на том дереве, поэтому не стесняйтесь делать предложения в других направлениях, но в настоящее время я думаю, что у каждого элемента должны быть реляционные «теги».

Например, Katana в настоящее время является строкой в ​​базе данных «items». Чтобы превратить его в оружие, и что-то вроде этого, я думал, что у меня будет база данных «черт» и таблица item_traits, которая будет просто связана между ними.

// 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

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

Я также не особенно рад, что весь предмет будет храниться в нескольких местах, например, чтобы создать копию предмета с другим именем, например, «коротким мечом», мне придется скопировать с несколько таблиц для создания дубликата.

Есть ли лучший способ выложить вещи, которые мне не хватает?

Изменить: я должен только отметить, что у меня уже есть база данных postgresql, используемая на сайте, поэтому я хочу использовать ее для хранения своих данных.

Изменить: я добавил ответ для реализации, которую я сейчас смотрю.

Kzqai
источник

Ответы:

10

Я собираюсь мягко не согласиться со всеми и сказать, что реляционный подход здесь разумен. Здесь интересно то, что предметы могут иметь несколько ролей. Основная проблема заключается в том, что отображение между этим реляционным макетом и компоновкой OO в коде не будет казаться «естественным», но я думаю, что на стороне базы данных несколько ролей можно выразить чисто (без странных кодировок или избыточности, просто объединяет) ,

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

Вот что я бы сделал, если бы все данные относились к конкретному элементу:

// 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 в таблицу Items и сможете хранить свойства, такие как «Является ли это оружием» и «Держится ли оно» и «Является ли это светом» в таблице 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

Вероятно, что типы предметов и типы оружия не меняются во время игры, так что вы можете просто загрузить эти таблицы в память и посмотреть эти атрибуты в хеш-таблице, а не в соединении с базой данных.

amitp
источник
+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 сохранялась. Чтобы хранить элементы разных типов, лучший способ представить эти данные в этой модели - создать таблицу potion_weapon с соответствующими свойствами зелья и оружия. Опять же, это решение НЕ идеально, но это наиболее элегантное решение при использовании реляционной базы данных.
Ари Патрик
@ Ари: Нет никакого неуважения, но его проблема именно в том, что реляционный подход является более обоснованным, а не меньшим. Хорошо известно, что базы данных NoSQL (или NoRel) отлично подходят для больших объемов чтения, распределенных / высокодоступных данных или плохо определенных схем. Этот случай - просто классическая ассоциация «многие ко многим», и такие базы данных, как Mongo, делают это не так, как RDBMS. См stackoverflow.com/questions/3332093/...
alphadogg
@alphadogg Как это отношения многих ко многим? В базе данных оружие знает о типах оружия, но типы оружия не должны знать об оружии, с которым они связаны. Если по какой-то причине вы хотите знать все виды оружия, относящиеся к конкретному типу оружия, вы просто пишете запрос, который перебирает коллекцию документов оружия.
Ари Патрик
@Ari: В вопросе ОП одно оружие может иметь много типов, а один тип может быть связан со многими видами оружия. Например, зелье и меч можно держать. Меч - это и оружие, и оружие.
alphadogg
3

Прежде всего, избавьтесь от подхода объектно-ориентированного наследования и перейдите к системе на основе компонентов .

Как только вы это сделаете, макет SQL внезапно станет намного проще. У вас есть одна таблица для каждого типа компонента с общим идентификационным номером. Если вам нужен элемент № 17, вы можете посмотреть «идентификатор элемента 17» в каждой таблице. Любая таблица, имеющая ключ, добавляет свой компонент.

Ваша таблица предметов содержит все необходимые данные для предметов в целом (имя, цена продажи, вес, размер, все остальное, что распределяется между всеми предметами.) Ваша таблица оружия содержит все соответствующие данные для оружия, ваша таблица зелий содержит все соответствующие данные для зелий ваша таблица брони содержит все необходимые данные для брони. Хотите нагрудник, это запись в Item и запись в Armor. Хочешь меч-шлем, который ты можешь пить, ты просто вставляешь запись в каждый стол, и бац, ты готов.

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

ZorbaTHut
источник
2

Использование SQL было вашей серьезной ошибкой. Это абсолютно НЕ подходит для хранения статических данных игрового дизайна.

Если вы не можете отойти от SQL, я бы посоветовал хранить элементы в сериализованном виде. Т.е.

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

Конечно, это уродливо и выбрасывает все «тонкости» SQL из окна, но они вам действительно нужны ? Скорее всего, вы все равно читаете все данные о ваших предметах в начале игры, и SELECTничем иным, кроме как item_id.

Ничего
источник
3
Поскольку (насколько я понимаю) игра многопользовательская и поддерживает PvP, существует вероятность того, что большая часть игровой информации не сохраняется на клиенте. Если это точно, то каждый раз, когда данные считываются из таблицы, их необходимо будет десериализовать, прежде чем они будут полезны удаленно. В результате этот формат делает запрос элементов по их свойствам ОЧЕНЬ дорогим; например: если вы хотите получить все элементы типа «оружие», вам придется извлечь каждый элемент, десериализовать каждый из них, а затем вручную выполнить поиск типа «оружие», в отличие от выполнения простого запроса. ,
Ари Патрик
1
По моему опыту, все элементы в любом случае считываются в кэш-память в начале игры. Таким образом, вы десериализуете их только один раз при запуске, а потом вообще не используете базу данных. Если это не так в этом случае, то я согласен, хранение сериализованных объектов - плохая идея.
Nevermind
Хм-м-м, единственное, что оставляет мне необходимый интерфейс для редактирования данных, что было бы неудобно.
Kzqai
2

В зависимости от того, сколько черт вам понадобится, вы можете использовать простую битовую маску для объектов с разными битами, соответствующими разным чертам:

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

Затем вы можете выполнить простые битовые тесты, чтобы увидеть, можно ли использовать объект для определенной функции.

Таким образом, «стеклянная бутылка» может иметь значение 101111, что означает, что она пригодна для хранения, может использоваться в качестве оружия, легко разбивается, вы можете бросить ее и содержать жидкости.

Любой редактор, который вы создаете для элементов, может затем иметь простой набор флажков для включения / отключения признаков на объекте.

Muttley
источник
1
Мне нравится идея, потому что он позволяет предмету сохранять свои собственные черты, и я не знаю, что мне нужно будет искать в базе данных черты, но я не совсем уверен, как заставить это работать в реляционном контексте. Я предполагаю, что каждый бит будет соответствовать инкрементным идентификаторам признаков в базе данных ...
Kzqai
Я думаю, что битовая маскировка более полезна для фиксированного количества черт, хотя?
Kzqai
Даже очень. Что произойдет, если вы использовали свой последний бит? Насколько просто увеличить тип данных, чтобы получить бит и просачивать его через слой приложения, или добавить столбец и сделать то же самое? Кроме того, хорошо ли настроен ваш движок базы данных для индексации битовых операций? Лучше всего в реляционной базе данных сохранять атомарность со столбцами данных. Не объединяйте несколько доменов в один столбец. Вы не можете воспользоваться преимуществами платформы.
alphadogg
1
Если вам нужна таблица поиска в БД для признаков (возможно, это хорошая идея, поскольку вы можете динамически добавлять их в любой редактор), тогда она просто будет иметь что-то вроде этого: id, bitmask, description. 1,1, "Holdable"; 2,2, "оружие"; 3,4, «Хрупкий» и т. Д. Что касается фиксированного числа, да, это правда. Но если вы используете 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 являются фундаментально распределенными хеш-таблицами и будут ограничивать вас так же, как и хеш-таблица в вашем коде, т.е. не все данные хорошо вписываются в эту модель. Реляционная модель гораздо более эффективна для моделирования отношений между данными. (Смешивающим фактором является то, что большинство СУБД являются устаревшими системами, которые плохо настроены на требования популярного 0,0001% Интернета, а именно Facebook и др.)

alphadogg
источник
Если вы собираетесь ссылаться на них коллективно, даже не думайте о базах данных. NoSQL - это не та сущность, которую можно обсуждать по смыслу, это бесполезная массовая ссылка, которую фанаты SQL используют для того, чтобы применить ошибочную логику, согласно которой если в одной базе данных этой определенной группы отсутствует особенность, то все они лишены этой возможности.
аааааааааааа
NoSQL - это термин, который пушеры NoSQL выбрали для адаптации. Я не знаю почему, это не очень наглядно. Но это не какой-то уничижительный. Работая с несколькими, я думаю, что описание alphadogg справедливо.
Возможно, мой комментарий был немного резким, но я ненавижу это имя и группировку, невозможно провести квалифицированную дискуссию о мельчайших деталях различных систем баз данных, когда люди придерживаются этого упрощенного взгляда на мир.
аааааааааааа
@eBusiness: я нахожу ваш комментарий забавным, учитывая, что именно лагерь против СУБД, если таковой имеется, самозазначил свою группу технологий как "NoSQL". По общему признанию, многие из них хотели бы видеть это имя устаревшим, задним числом. Тем более, что многие из них наконец-то наложили SQL на свои физические реализации. Что касается разговоров о них, я не воспринял ваш комментарий как грубый, или у меня толстая кожа, ваш выбор! :) Я имею в виду, можно говорить о чаепитии, почему не группа баз данных NoSQL?
alphadogg
Это справедливо, я признаю, что отношения очень много для многих. То, что мешает мне создать его в по-настоящему нормализованном стиле, - это мысль о том, что «материал», из которого состоит предмет, будет распределен по крайней мере по двум таблицам. Мне не нравится идея неспособности создавать атомарные вставки.
Kzqai
0

Здесь я считаю, что более современные средства сопоставления OR действительно полезны, например, Entity Framework 4 и его (CTP) функция «сначала код» . Вы просто пишете классы и их отношения в обычном коде, и даже без необходимости их украшать или запускать какие-либо инструменты вручную, EF сгенерирует резервное хранилище в формате SQL для вас, с необходимыми таблицами ссылок и всем ... это действительно развивает творческий потенциал. ^^

Оскар Дювеборн
источник
Хорошо, я создам код и классы и все это в коде только сначала ... ... просто должен перевести это в существующую структуру базы данных после.
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 вручную ... ... было бы не очень легко и безопасно сделать это с данными, которые находятся в базе данных.

Kzqai
источник
1
Что происходит, когда вы вводите новую черту? Как часто это могло случиться? Каковы будут последующие эффекты на вашу нагрузку обслуживания кода? Сравните это с хранением его как «многие-ко-многим», в результате чего экземпляры вашего объекта динамически создаются (через ваш выбор ORM) для хранения этих данных.
alphadogg
Спасибо, это хороший момент, добавление новых черт не будет тривиальным. Вы добавили в мой анализ паралич. : p
Kzqai
Извините, но это довольно критично. Вам просто нужно продумать это. Смоделируйте упражнение, помня о том, что вам нужно делать, когда нужно обновить какое-то оружие, чтобы добавить новую черту, чтобы ваше улучшение игры могло работать.
alphadogg
-1

Это немного глупо, и я не даю никаких гарантий, что это хороший дизайн БД; Мои классы БД были некоторое время назад, и они быстро не приходят на ум. Но если у предметов гарантированно будет, скажем, менее 10 предметов, вы можете присвоить каждому предмету поле attribute1, attribute2 и т. Д. Вплоть до attribute10. Это избавит вас от необходимости использовать таблицу атрибутов «многие ко многим» за счет ограничения количества атрибутов.

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

Грегори Эйвери-Вейр
источник
Это ужасный дизайн, никогда не делай этого. Тебе было бы лучше просто не использовать базу данных.
ZorbaTHut
-1

Nevermind дал хороший ответ, но мне интересно, вам вообще нужна база данных для этого? Хранение всех данных в виде простого файла и загрузка их в программу при запуске кажется самым разумным способом сделать это.

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

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

AAAAAAAAAAAA
источник
У меня уже есть база данных, которая используется и интегрирована с остальной частью сайта.
Kzqai