どうすればこの敵のデータベース実装を改善できますか?


7

私はRPGを開発していて、敵データベースの構築を開始する必要があるところにいます。これに関連するいくつかの課題と、私が検討してきたいくつかの解決策があります。

敵のデータベースで私がしなければならないことは次のとおりです。

次のデータを表す必要がある2つの主要な敵クラスがあります。

以下を含む基本敵クラス:

Base Stats
Status Resistance Table
Elemental Resistance Table
Steal Table
Drop Table
Level
Unique ID
Base XP
AI Hook
Name
Display Name

そして、次のフィールドを追加して、機器を追加する機能を追加する派生クラス:

Main Weapon
Secondary Weapon/Equipment
Armor
Accessories

今後、必要に応じてフィールドやクラスを追加する予定です。敵をデータベース化するための2つの可能な形式を検討しました。

XMLファイル

基本的には次のようにします。

<?xml version="1.0" encoding="utf-8"?>
<Enemies>
  <Enemy name="Red Dragon" type="BaseEnemy" level="56" displayname="Red Dragon">
    <Stats HP="55000" MP="2500" SP="2500" Strength="212" Vitality="125" Magic="200" Spirit="162" Skill="111" Speed="109" Evasion="100" MgEvasion="100" Accuracy="100" Luck="55"/>
    <StatusResistances>
      <Resistance name="Sleep" value="100" />
      <Resistance name="Stop" value="100" />
    </StatusResistances>
    <ElementResistances>
      <Resistance name="Fire" value="75" />
    </ElementResistances>
    <LootTable>
      <Item name="Elixir" rate="0.03" count="1"/>
    </LootTable>
    <DropTable>
      <Item name="Elixir" rate="0.03" count="1"/>
    </DropTable>
    <AIScript value="BasicBehaviour.py" />
    <BaseXP value="4800"/>
  </Enemy>
  <Enemy name="Gaverick 1" type="HumanoidEnemy" level="33" displayname="Gaverick">
  <!--Same Stuff as above here-->
    <Equipment>
      <Weapon name="Dark Eclipse"/>
      <Armor name="Terra Defense"/>
      <Accessory name="Ribbon"/>
    </Equipment>
  </Enemy>
</Enemies>

利点:

  • パラメータを追加/再配置する必要がある場合、拡張が簡単
  • デフォルト値を割り当てるのは簡単
  • 構成ファイル、タイルマップ、リソースの説明の読み込み用に含まれているXMLパーサー(pugixml)を既に持っています

短所:

  • 潜在的に遅い(私のデータベースはおそらく数百の敵にヒットするでしょう)
  • は任意の敵を照会できないため、すべての敵をメモリに保持する必要がある可能性があります。
  • これは、変更された敵データもロードするためにゲームを再起動する必要があることを意味します

SQLite

このため、基本的に、必要なすべてのデータを表す列を含むテーブルを作成し、不要なフィールドは空のままにします

メリット

  • 任意のクエリにより、不要な敵データをメモリから除外できる
  • より構造化された感じ
  • ファイルサイズが小さい

短所

  • パラメータの順序を拡張/再配置するのがより困難
  • 未使用のフィールドへの不要なオーバーヘッド
  • sqliteのデータベースインターフェイスラッパーを作成する必要があります

これを念頭に置いて、私は他の人が何をしたかについてのいくつかの外部の経験を得ることに興味がありました。私はこれをまったく間違っていると思うかもしれません。もしそうなら、ここにある2つの可能性の代替案を提案してください。

さらに、これらの可能性のいずれかを改善する方法についての提案があれば幸いです。本当に、私が正しい軌道に乗っているかどうかを知りたいだけです。

無料のライブラリを自由に使用でき、すでにブーストを組み込んでいます

回答:


12

完全にリレーショナルなデータベースのような複雑さは必要ないと思います。リレーショナルデータベースは、複雑な検索操作を容易にするだけでなく、広大なデータセットの検索を処理するために存在します。ゲーム内で行っていることが、敵がスポーンするたびにこれらの1つにインデックスを付けることである場合は、非常に複雑なツールを使用してこれを行っています。

要するに、SQLiteはやりすぎです。

その時点で、問題はSQLiteとXMLではありません。それは、それをディスクに格納する方法と、ゲーム内でデータを表現する方法です。ゲーム内で行うクエリの種類が名前での検索のみである場合は、単純なstd::mapもので十分です。実際、std::vector高速検索のために、それらをaに貼り付け、ロード後にソートすることができます。

ディスク上での表現については、読み取りではなく編集が最も簡単になる形式に最も注意する必要があります。読み取りは、コードで十分簡単に​​実行できるものです。編集は敵ごとに1回行う必要があります。新しい敵を設計するときは、データを頻繁に編集する必要があります。

私にとって、二分法はXMLとLuaの間にあり、データ記述言語として完全にサービス可能です。たとえば、サンプルのXMLファイルはLuaで次のように表すことができます。

return
{
    {
        name = "Red Dragon", type="BaseEnemy", level=56, displayname="Red Dragon",
        stats = {
            HP=55000, MP=2500, SP=2500, Strength=212, Vitality=125,
            Magic=200, Spirit=162, Skill=111, Speed=109,
            Evasion=100, MgEvasion=100, Accuracy=100, Luck=55
        },
        status_resistances = {
            {name="Sleep", value=100},
            {name="Stop", value=100},
        },
        element_resistances = {
            {name="Fire", value=75},
        },
        loot_table = {
            {name="Elixir", rate=0.03, count=1},
        },
        drop_table = {
            {name="Elixir", rate=0.03, count=1},
        },
        script = "BasicBehaviour.py",
        xp = 4800,
    },
    {
        name="Gaveric 1", type="HumanoidEnemy", level=33, displayname="Gaveric",
        --Same Stuff as above here
        equipment = {
            weapon = "Dark Eclipse",
            armor = "Terra Defense",
            accessory = "Ribbon",
        }
    }
}

Luaスクリプトベースのアプローチの最大の利点は、Luaであることです。純粋なデータでもかまいません、そうである必要はありません。したがって、statブロックの繰り返しがある場合は、Luaスクリプトでこれらのデータを簡単に生成できます。これはすべて、テーブルを返すLuaスクリプトです。テーブルは、メモリ内のデータ構造に読み込むものです。

このアプローチの欠点は、主にこれらのファイルを書き込むためのツールを使用していない場合です。これらの敵のファイルを編集するために使用するツールがある場合、Luaのアプローチは問題ありません。しかし、それらを手動で編集している場合、それはLuaテーブルをウォークするロードコードが入力を確認する必要があることを意味します。必要なフィールドが存在すること、各数値が有効な数値であることなどを確認する必要があります。

そして、それは書くのが難しいコードではありませんが、非常に面倒です。XMLの利点は、RelaxNGまたはWXSスキーマで検証できることです。スキーマに基づいた編集を行うXMLエディターもあるので、無効な XMLファイルを書き込むことができなくなります。

新しいフィールドを追加する必要がある場合は、スキーマを調整するだけで問題ありません。ファイル構造を変更する場合は、スキーマを調整し、ファイルを再検証して、エラーが表示される場所を修正します。XSLTツールを作成して、ファイルをある形式のバージョンから別の形式のバージョンに自動的に変換することもできます。

もちろん、スキーマの記述方法を知っている必要があり、スキーマ駆動型のXML形式が必要です。それ以外の場合は、いずれかの方法でデータを検証する必要があるため、Luaスクリプトが悪くなることはありません。また、Luaスクリプトを使用すると、データを直接照会できるため、間違いなく解析しやすくなります。敵の定義を含むLuaテーブルを指定すると、特定の「要素」を名前でクエリできます。XMLパーサーを使用すると、要素を受け取ったときに処理する必要があります。これは、通常、作成が少し複雑になります。

は任意の敵を照会できないため、すべての敵をメモリに保持する必要がある可能性があります。

... そう?XMLの例から取得したデータ構造は、(圧縮の試みなしで)消費します。

  • 14統計* 4バイトあたり= 56バイト。
  • 内部名:32バイトの文字列。
  • 表示名:64バイトの文字列。
  • タイプ:データ構造の一部。保管する必要はありません。

各武器は32バイト文字列にすることもできます。つまり、最悪の場合の総計は... 248バイトです。それらの3500以上を、MBのRAMの半分に格納できます。気にしない。

これは、変更された敵データもロードするためにゲームを再起動する必要があることを意味します

どうして?それはあなた次第です。敵のデータをダンプしてゲームの途中でリロードできないと言うことは何もありません。それができないのは、それを可能にするためにコードを適切に構造化しなかったからです。


Luaの+1。また、YAMLについても忘れないでください。
michael.bartnett '19年

6

データを表す必要がある2つの主要な敵クラスがあります。基本敵クラス...と派生クラス...

これには2つのクラスが必要だとは思いません。1つで十分です。継承ではなくコンポジションでこれを簡単にモデル化できます。最新のソフトウェア設計手法は、脆弱で維持が難しい傾向があるため、深いクラス階層から離れる傾向にあります。提案された階層はそれほど深くありませんが、それは不要です(これは「深い」への最初のステップです)。派生クラスは、基本クラスと同じように簡単に残り、空のままにできるプロパティのみを追加します。

これにより、後で個々の敵の動作とプロパティをより適切に反復して調整できるようになるだけでなく、単一のパブリックAPIとその処理のみを処理するため、コードがよりシンプルになります。

敵をデータベース化するための2つの可能な形式を検討しました。

これら2つのうち、XMLを選択します。一般に、JSONを確認することをお勧めします。ここで実際のリレーショナルデータベースを使用する必要はないと思います。その理由については、この質問を参照してください。短いバージョンでは、アクセスや編集が複雑で直接的ではありません。また、リレーショナルデータベースが提供する利点をまったく必要としないので、同様のものを使用するほうがよいでしょう。

XMLとJSONはどちらも、専門的なツールを必要とせず、人間が判読しやすく、人間が編集できるという大きな利点があります。これにより、反復時間の増加に役立ちます。XMLでは、スキーマを大幅に変更したり、他の種類の大規模な移行を行う必要がある場合に、XSLTを使用してデータを一括変換することができます。

特にSQLiteに対する3つの「利点」について:

  • ディスク上のファイル(XMLリーダーによって異なる可能性があります)にある場合、データをメモリから簡単に保持できますが、ディスクが「遅い」ため、おそらくこれを実行したくないので、 t実際に多くのメモリプレッシャーを引き起こすのに十分なデータがある。
  • それはあなたが作ったのと同じくらい構造化されているだけであり、本当に良い正規化されたデータベーススキーマを設計することは常に簡単な仕事ではありません。通常、適切なスキーマ設計がある場合にのみ、データベースから最大の利益を得ることができます。スキーマを変更することもはるかに困難です。
  • ゲームを出荷するために、バイナリファイル形式にいつでも移行できます。これにより、XMLやJSONなどのより詳細なテキストベースの形式に固有のオーバーヘッドを削減または排除できます。とても簡単です。

3

これを見る別の方法は次のとおりです。データ構造を作成し、モンスターの値を入力してから、データ構造をバイナリファイルに保存し、次のようにそれをpakファイルに入れます。

ork_footsoldier.npcdat
ork_chief.npcdat
guard.npcdat
 Into..
NPCData.pak

次に、パックをメモリにロードし、npcdatファイルを解析してから、要求に応じてそれらのデータを解析します。XMLやSQLLiteよりも高速で、占有するスペースもはるかに少なくなります。


@Skeith同意する。デリミタ付きのプレーンテキストファイルは、user127817が記述しているジョブを大した問題なく実行します。
クリストファー

@クリストファー:それのためにパーサーを書かなければならないことを除いて。そして、パックファイルをロードする何か。そして、まあ、XMLファイルをロードするだけで行う必要のないことはたくさんあります。「プレーンテキストの削除」も、ガイド付き編集ツールがないため、編集が困難です。あなたは簡単にフォーマットを間違える可能性があり、それをロードしようとするまではわかりません。
Nicol Bolas

バイナリデータのパーサーの記述は、テキストパーサーよりもはるかに高速で簡単です。さらに、プロダクションゲームでは、値を変更してセーブをチートするのがはるかに困難です。
Matt Jensen

0

私が考えることができるSQLiteの唯一の利点は、重複の削減です。つまり、「より構造化された感じ」の利点の一部に該当する場合です。

ただし、必要に応じて、XPathを使用してXMLをクエリできます。しかし、数百(場合によっては数千)のすべてのデータをメモリに保存することは重要ではないと思います。この量のデータファイルでは、dbまたはXMLファイルのどちらも重要ではありません。

XMLに投票します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.