リレーショナルデータベース内の使用可能な在庫/オブジェクト/アイテム(カタナなど)の複数の「用途」(たとえば武器)をモデル化する方法


10

したがって、私はwww.ninjawars.netでアイテムの使用を拡大することに取り組んでおり、私たちが使用しているリレーショナルデータベースでそれらを柔軟に表現する方法が正確にわかりません。

私は間違ったツリーを吠えているかもしれないので、他の方向に遠慮なく提案してください。しかし、現在、各アイテムには関係する「タグ」が必要だと思っています。

たとえば、現在、カタナは「アイテム」データベースの行です。それを武器にして保持可能なものにするために、私は「特性」のデータベースと、それらの間を単純にリンクする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

結果として生じる追加のデータ(たとえば、剣によるダメージ、damage_typeなど)をモデル化する方法が本当にわかりません。

アイテム全体が複数の場所に保存されることにも特に不満があります。たとえば、「短い剣」のように別の名前のアイテムのコピーを作成するには、以下からコピーする必要があります。重複するアイテムを作成するための複数のテーブル。

私が見逃しているものをレイアウトするより良い方法はありますか?

編集:私はサイトで使用中のpostgresqlデータベースをすでに持っていることに注意すべきです、それが私のデータストレージにそれを使用したい理由です。

編集:私が現在見ている実装の回答を追加しました。

回答:


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

このデザインでは、すべての(またはほとんどの)アイテムが持つ属性とともに、すべてのアイテムがItemsテーブルにあります。アイテムが実行できる追加の各役割は、個別のテーブルです。

武器として使用したい場合は、武器テーブルで調べます。そこにあれば、武器として使用できます。ない場合は、武器として使用できません。レコードの存在は、それが武器であるかどうかを教えてくれます。そして、そこにある場合、その武器固有の属性はすべてそこに格納されます。これらの属性はエンコードされた形式ではなく直接格納されるため、それらを使用してクエリ/フィルターを実行できます。(たとえば、ゲームのメトリクスページの場合、プレイヤーを武器の損傷タイプ別に集計することができますが、いくつかの結合とgroup-bydamage_typeでそれを行うことができます。)

アイテムは複数の役割を持つことができ、複数の役割固有のテーブルに存在します(この例では、武器と照明の両方)。

「これは保持可能ですか」のようなブール値の場合は、Itemsテーブルに入れます。武器やその他のロールテーブルでルックアップを実行する必要がないように、「これは武器です」などをキャッシュする価値があります。ただし、冗長性が追加されるため、同期を保つように注意する必要があります。

タイプごとにテーブルを追加するというアリの推奨事項も、一部のデータがアイテムごとに変化しない場合は、このアプローチで使用できます。たとえば、武器のダメージがアイテムごとに異なるわけではないが、役割がアイテムごとに異なる場合は、共有されている武器の属性を表にまとめることができます。

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

アイテムタイプと武器タイプはゲーム中に変更されない可能性が高いため、これらのテーブルを一度メモリにロードし、データベース結合ではなくハッシュテーブルでそれらの属性を検索できます。


+1。これは、リレーショナルデータベースをリレーショナルデータベースとして使用する唯一の答えです。これ以外の、またはNevermindのblob提案の極端な例以外のすべて、およびDBをデータストアとして使用することは、恐ろしい計画です。

+1しかし、これらのアプローチは柔軟性に欠けるようです。一貫性がないようですが、基本的に1回だけアイテムのデータベース構造を作成できるようにしたいのですが、それからアイテムの機能をコーディングし、データベースにアイテムの挿入を追加しますが、変更する必要はありません。データベースは将来も再び使用されます(もちろん、まれに例外があります)。
Kzqai 2010年

リレーショナルデータベースにフィールドを認識させ、フィールドの表現を最適化する場合は、どこかに追加する必要があります。静的型のようなものです。コンパイラにフィールドについて知ってもらい、フィールドの表現を最適化したい場合は、型を宣言する必要があります。代替は、動的に型付けされたアプローチであり、一部のドキュメントデータベースで使用されるか、何らかの方法でデータをリレーショナルデータベースにエンコードします。データベースはフィールドが何であるかを知らないか、フィールドのストレージを最適化できませんが、ある程度の柔軟性が得られます。
amitp 2010年

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:追加の視点を提供するために、非リレーショナルデータベースとリレーショナルデータベースの詳細を追加しました


そのシステムから生まれる柔軟性はあまりありません。たとえば、ガラスの小瓶が武器として使用でき、「シャープ」になりたいとしたら... ...私は運が悪かったでしょう。さらに、新しいタイプのアイテムごとに、実際にデータベース構造を作成する必要があります。タイプが1つまたは2つしかなかったとしても。
Kzqai 2010年

あなたが提起するポイントは非常に有効であり、リレーショナルデータベースがこの特定の状況に適さない理由の追加の例を強調しています。私が提示したモデルは、メモリが節約され、SQL機能が維持されるような方法でデータをレイアウトする最良の方法を示しています。複数のタイプのアイテムを保存するために、このモデルでそのデータを表す最良の方法は、適切なポーションと武器のプロパティを含むpotion_weaponテーブルを作成することです。繰り返しになりますが、このソリューションは理想的ではありませんが、リレーショナルデータベースを使用する場合は最もエレガントなソリューションです。
Ari Patrick、

@アリ:軽視するつもりはありませんが、彼の問題は、リレーショナルアプローチが有効である理由です。NoSQL(またはNoRel)データベースは、大容量の読み取り、分散/高可用性データ、または不十分に定義されたスキーマに最適であることはよく知られています。このケースは単に古典的な多対多の関連付けであり、MongoのようなデータベースはRDBMSと同様にこれを行いません。stackoverflow.com/questions/3332093/…を
alphadogg 2010年

@alphadoggこの多対多の関係はどうですか?データベースでは、武器は武器の種類を認識していますが、武器の種類は、関連付けられている武器を認識する必要はありません。何らかの理由で特定の種類の武器であるすべての武器を知りたい場合は、武器のドキュメントコレクションを反復処理するクエリを作成するだけです。
Ari Patrick

@アリ:OPの質問では、1つの武器に多くのタイプがあり、1つのタイプが多くの武器にリンクされている場合があります。たとえば、ポーションと剣は手に持って使えます。剣は持ち手と武器の両方です。
alphadogg 2010年

3

まず、オブジェクト指向の継承アプローチをダンプし、コンポーネントベースのシステムを使います

これを実行すると、SQLレイアウトが突然はるかに簡単になります。共有ID番号を持つコンポーネントタイプごとに1つのテーブルがあります。アイテム#17が必要な場合は、すべてのテーブルで「アイテムID 17」を検索します。キーを持つすべてのテーブルには、コンポーネントが追加されます。

アイテムテーブルには、一般的なアイテムに必要なすべてのデータ(名前、販売価格、重量、サイズ、その他すべてのアイテムで共有されるすべてのもの)が含まれます。武器テーブルには、武器に適切なすべてのデータが含まれ、ポーションテーブルには、適切なすべてのデータが含まれますポーションの場合、アーマーテーブルには、アーマーの適切なデータがすべて含まれています。胸当てが欲しい、それはアイテムのエントリーであり、鎧のエントリーです。あなたが飲むことができる剣の兜が欲しい、あなたは各テーブルにエントリーを置くだけで、バム、それで終わりです。

この同じパターンは特にアイテム固有のものではないことに注意してください。これは、クリーチャー、ゾーン、その他の必要なものに使用できます。驚くほど用途が広いです。


2

SQLの使用は重大な間違いでした。静的なゲームデザインデータの保存には絶対に適していません。

SQLから離れることができない場合は、シリアル化されたfromにアイテムを格納することを検討します。すなわち

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

もちろん、これは醜く、すべてのSQLの「便利さ」をウィンドウの外に投げ出しますが、実際に必要ですか?おそらく、あなたはとにかくゲーム開始時に、あなたのアイテムデータのすべてを読まないし、決してSELECT以外によりますitem_id


3
(私の理解では)ゲームはマルチプレーヤーであり、PvPをサポートしているため、ほとんどのゲームプレイ情報がクライアントに保存されていない可能性があります。それが正確な場合、データがテーブルから読み取られるたびに、リモートで使用する前にデシリアライズする必要があります。結果として、この形式では、プロパティによるアイテムのクエリが非常に高価になります。たとえば、「武器」タイプのすべてのアイテムを取得する場合は、単純なクエリを実行するのではなく、すべてのアイテムを取得し、各アイテムをデシリアライズしてから、「武器」タイプを手動でフィルタリングする必要があります。 。
Ari Patrick、

1
私の経験では、いずれにしても、ゲームの開始時にすべてのアイテムがメモリ内キャッシュに読み込まれます。したがって、起動時に一度デシリアライズするだけで、その後はデータベースをまったく使用しません。この場合、そうでない場合、同意します。シリアル化されたオブジェクトを格納することは悪い考えです。
Nevermind

うーん、唯一のことは、これがデータを編集するために必要なインターフェースを私に残すということです、それは不便でしょう。
Kzqai 2010年

2

必要になる可能性のあるトレイトの数に応じて、さまざまなトレイトに対応するさまざまなビットを持つオブジェクトにシンプルなビットマスクを使用できます。

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

次に、単純なビットテストを実行して、オブジェクトが特定の関数に使用できるかどうかを確認できます。

したがって、「ガラス瓶」の値は101111で、保持可能で、武器として使用でき、簡単に壊れて、投げたり、液体を入れることができます。

アイテム用に作成したエディターには、オブジェクトの特性を有効/無効にするための簡単な一連のチェックボックスを設定できます。


1
私はアイデアが好きです、なぜならそれはアイテムがそれ自身の特性を保持することを可能にするからです、そして私は特性をデータベースで検索する必要があるつもりかどうかわかりませんが、私はその働きをする方法を本当に確信していません関係の文脈で。各ビットはデータベース内の特性の増分IDにマッピングされると思います...
Kzqai

とはいえ、ビットマスキングは一定の数の特性に対してより有用だと思いますか?
Kzqai 2010年

まさにその通り。最後の「ビット」を使い果たした場合はどうなりますか?データ型を増やしてビットを獲得し、それをアプリレイヤーに浸透させること、または列を追加して同じことを行うことはどれほど簡単ですか?また、特定のデータベースエンジンは、ビット操作のインデックス作成のために十分に調整されていますか?リレーショナルデータベースでは、データ列をアトミックに保つのが最適です。複数のドメインを1つの列にまとめないでください。プラットフォームのメリットを活用することはできません。
alphadogg 2010年

1
トレイトのDB内のルックアップテーブルが必要な場合(おそらくエディタに動的に追加できるので、おそらく良いアイデアです)、ID、ビットマスク、説明のようなものになります。1,1、 "保持可能"; 2,2、 "武器"; 3,4、 "Breakable"など。固定数に関しては、そうです。ただし、64ビット整数を使用する場合は、十分なスコープが必要です。
Muttley、2010年

1

私たちのプロジェクトでは、アイテムが持つ可能性のあるさまざまな「追加データ」のitem_attributesがあります。次のようにレイアウトされています。

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

次に、次のような属性テーブルがあります。

id |   name       | description
1    attackspeed    AttackSpeed:

Ari Patrickは正しいですが、最終的にはリレーショナルデータベースは作成されません。欠点は、新しいアイテムを生成するためのかなり複雑な手順があることです(外部ツールを使用して実行します-これを強くお勧めします。これらを手動で追加しないでください。混乱するだけです)。

もう1つのオプションは、スクリプト言語を使用してアイテムテンプレートを作成することです。そうすると、テンプレートを簡単に解析して、それを使用して新しいアイテムを作成できます。もちろん、アイテムデータをデータベースに保存する必要がありますが、その時点で、新しいアイテムの作成の詳細について心配する必要はありません。古いスクリプトファイルをコピーして、いくつかの情報を変更するだけで十分です。トーゴ。

正直なところ、新しい静的アイテムの作成方法をやり直すとしたら、スクリプトアイテムテンプレートを使用して、はるかに簡単な方法をとるでしょう。


1

これは典型的な多対多の関係であり、あらゆる有能なリレーショナルデータベースには難解ではありません。1つのオブジェクトに対して多くの特性があり、1つの特性は1つ以上の異なるオブジェクトタイプで使用できます。3つのリレーション(テーブル)をモデル化し、1つは関連リレーションで、これで完了です。適切なインデックス作成により、迅速なデータ読み取りが保証されます。

コードにORMを重ねれば、DBとミドルウェアの間で行き来する問題はほとんどなくなります。多くのORMは、クラス自体も自動生成できるため、さらに「見えない」ようになります。

NoSQLデータベースに関しては、それを実行できない理由はありません。現在のところ、ぼろやブログでそのテクノロジーを応援するのはファッショナブルですが、それらには固有の問題がたくさんあります。複雑な動的読み取りや更新、不十分なサポートツールチェーン、冗長データ、それに伴う整合性の問題などには不十分です。ただし、トランザクション機能とそのオーバーヘッド、および分散/レプリケーションのモデルが簡単なため、スケーラビリティが優れているという魅力があります。 。

リレーショナルデータベースとNoSQLデータベースの通常のホブゴブリンはパフォーマンスです。RDMBSが異なると、オーバーヘッドの度合いも異なるため、FacebookまたはTwitterのレベルにスケーリングするのにあまり好ましくありません。ただし、これらの問題に直面することはほとんどありません。それでも、シンプルなSSDベースのサーバーシステムでは、パフォーマンスに関する議論が無意味になる可能性があります。

明確にしましょう:ほとんどのNoSQLデータベースは基本的に分散されたハッシュテーブルであり、コード内のハッシュテーブルと同じように制限されます。すべてのデータがそのモデルにうまく適合するわけではありません。リレーショナルモデルは、データ間の関係をモデル化するためのはるかに強力です。(交絡因子は、ほとんどのRDBMSが、人気のあるWebの0.0001%、つまりFacebookなどの要求にうまく対応していないレガシーシステムであることです。)


それらをまとめて参照する場合は、データベースについて話さなくても構いません。NoSQLは意味のある議論が可能なエンティティではありません。この定義されたグループの1つのデータベースに機能がない場合、すべての機能にその機能が欠けているという欠陥のあるロジックを適用するためにSQLファンボーが使用する無駄な大量参照です。
aaaaaaaaaaaa 2010年

NoSQLは、NoSQLプッシャーが適応することを選択した用語です。理由はわかりませんが、あまり説明的ではありません。しかし、それはある種の軽蔑的なことではありません。いくつかを扱ってきたので、alphadoggによるそれらの説明は公平だと思います。

おそらく私のコメントは少し厳しいかもしれませんが、それでもその名前とグループ化は嫌いです。人々がこの簡略化された世界観を維持している場合、さまざまなデータベースシステムの詳細について資格のある議論をすることは不可能です。
aaaaaaaaaaaa 2010年

@eBusiness:反RDBMSキャンプがあるとすれば、彼らのテクノロジーグループを「NoSQL」として自己油塗りしたのは、コメントが面白いと思うからです。確かに、彼らの多くは後からその名前が廃止されることを望んでいます。特に、それらの多くが最終的にSQLを物理的な実装に重ねています。それらについて話すことに関して、私はあなたのコメントを厳しいとは思わなかった、または私は厚い肌を持っている、あなたの選択!:)つまり、ティーパーティーについて話すことができます。なぜデータベースのNoSQLグループではないのでしょうか。
alphadogg 2010年

それは公平です、私は関係が完全に多対多であることを認識しています。本当に正規化されたスタイルで作成するのを妨げているのは、アイテムを構成する "もの"が少なくとも2つのテーブルに分散されるという考えです。アトミックな挿入を作成できないという考えは好きではないと思います。
Kzqai 2010年

0

これは、Entity Framework 4とその(CTP)コードファースト機能のように、より現代的なORマッパーが本当に役立つと思う場所です。クラスとその関係を通常のコードで記述するだけで、それらを装飾したり、ツールを手動で実行したりする必要はありません。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の部分を手動で編集できることですが...データベースにあるデータを使用して編集するのは簡単でも安全でもありません。


1
新しい特性を導入するとどうなりますか?これはどのくらいの頻度で起こりますか?コードメンテナンスのワークロードに対する下流の影響は何ですか?データを保持するためにオブジェクトインスタンスが(選択したORMを介して)動的に構築される多対多として保存するのとは対照的です。
alphadogg 2010年

ありがとう、それは良い点です。新しい特性を追加することは簡単ではありません。あなたは私の分析麻痺に追加しました。:p
Kzqai 2010年

申し訳ありませんが、これはかなり重要です。あなたはそれをじっくりと考える必要があるだけです。ゲームの機能強化が機能するように新しい特性を追加するためにいくつかの武器を更新する必要がある場合に何をしなければならないかを念頭に置いて運動をモデル化します。
alphadogg 2010年

-1

これは少しハックで、私はそれが良いDB設計であることを保証しません。私のDBクラスは少し前のことで、すぐには頭に浮かびません。ただし、たとえばアイテムの数が10未満であることが保証されている場合は、各アイテムにattribute1フィールド、attribute2フィールドなど、最大でattribute10を指定できます。属性の数を制限する代わりに、多対多の項目属性テーブルを使用する必要がなくなります。

振り返ってみると、それはひどいデザインだと思いますが、快適なリレーショナルデータベースを使い続けることができ、未知の領域に行く必要がないことを意味します。トレードオフがそれだけの価値があるかどうかを決めるのはあなた次第です。


これはひどいデザインです。絶対にやらないでください。データベースを使用しないほうがよいでしょう。
ZorbaTHut

-1

Nevermindは良い答えを出しましたが、これのためのデータベースさえ必要ですか?すべてのデータをプレーンファイルに格納し、起動時にプログラムに読み込むことは、これを行う最も賢い方法のようです。

編集:
そうです、ステートレスな要求駆動型の環境では、データをデータベースに保存する方が適切です。データをファイルに書き込み、コードを記述して、Nevermindが提案するタイプのデータベースに変換します。

または、オブジェクトの数が多すぎない場合は、コードファイルで文字どおりにロットを宣言するのが最も速い方法です。


エラー、私はすでにデータベースを使用していて、サイトの他の部分と統合しています。
Kzqai
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.