例としてリアルタイムマルチプレイヤーを使用したXMLおよびJSONの代わりのスクリプトによる構成





ショートカット: githubtiles.js tiles.groovy tiles.ruby



ゲーム内のオブジェクトが可能な動作よりも一桁大きいことは秘密ではありません。 オブジェクト記述のプロトタイプを作成するとき、Java、C ++、またはC#コードで直接記述できますが、すべてがすぐに混乱します。 次に、オブジェクトはXMLまたはJSON構成でデータベースに取り出されます。 構成を編集した後、コードを再構築する必要がないため、これは非常に役立ちます。これはプログラマーだけでなく、主題の専門家によっても行うことができます(ゲームの場合、これらはゲームデザイナーとコンテンツデザイナーです)。 チームが成長するか、オブジェクトの数が一線を越えると、プログラマーは便利なエディターを作成して、このJSON-configを視覚的に編集できるようにします。 その結果、出力はある種のハードサポートされたモンスターになります。



コーディングの方法がまったくわからない多くの人を雇わない場合は、別の方法、つまりドメイン固有言語を使用してメタデータを記述することができます。



base / json / xmlを編集するとき、ビジュアルエディターには何が与えられますか?



より高度な記述言語を支持して、視覚的な編集を完全に拒否することができるものは何ですか?



目的:これらすべてのパンを入手します。



数値IDといえば。 プロジェクトでオブジェクトの数値IDがコードに定数として記述されている場合、これを変更することをお勧めします。そうしないと、数十個ある場合に問題が発生します。 Minecraftサーバーの所有者は、ブロックIDと異なるMODのエンティティとの競合により、涙を流しました。 この定数のリストが1か所に格納されている場合でも、このリストを変更するアドオンの作成を開始すると、多くのhemo核が発生するため、これも悪いことです。 また、マルチプレイヤーゲームがあり、これらのIDがプロトコルに接続されている場合は、地獄で燃える準備をしてください。



結果:本番環境から引き裂かれたコード


githubには 、2つのスクリプトtiles.js tiles.groovy tiles.rubyからデータをロードするモデルとテストを備えたJavaコードが含まれています



思考の列車


タイルのモデルがあります。たとえば、いくつかのクラスがあります。



//  static final String[] BLOCK_NAMES = new String[] { "floor", "building", "arrow", "hideout", "abyss", "tunnel", "solid", "box"}; public class ScriptClass { int id = -1; String name; } public class Tile extends ScriptClass { int group = -1; //  int indexInGroup; //   int type = -1; //:        int image = -1; //   int background = -1; //  int subground = -1; //   ,     int onDamage = -1; //      int onBomb = -1; //     ? int onAtomic = -1; //  ? int direction = -1;// ,      } //      public class TileChange extends ScriptClass { public static class Variant { int p = 1; //   int item = -1; //ID     int effect = -1; //  -  int result = -1; //ID     ,   //  } int totalP = 0; //   List<Variant> variants; // }
      
      







クライアントのTileSet:







また、オートマトンのように動作するコードもあります。これらのオブジェクトからのデータに従って、爆破の場所に配置するタイルと作成する賞品を決定します。



これをSQLのテーブルとして、またはエンティティのリストを含むJSONファイルとして編集するのは非常に不便であり、より強力なものが必要です。



構成をオブジェクトのリストとしてではなく、ツリーとして説明してみましょう。 頂点はさまざまなタイプになります-どこかで新しいタイルが宣言され、どこかで新しいグループが、どこかで爆発に見舞われた場合のタイルの変化が記述されます。 この場合、次のものが必要です。

  1. テキストからこのツリーを作成するパーサー
  2. ツリー内を回り、頂点に対して、モデル内で何かを行うタイプに対応するメソッドを呼び出す部分
  3. 構成上の規則のロジック:合意されたロジックに従って空白フィールドに入力します。たとえば、グループの対応するフィールドから値を取得できます。


パーサーを通常どおりに使用できる場合-JSON、XML、2番目の段落で自分で記述する必要があるもの。 かどうか? これが秘密です。スクリプトエンジンをすぐに使用できます。モデルを構築するメソッドを呼び出すようにバインディングを作成するだけです。



結果は、ドメイン固有の言語というフィールドにより適した言語になります。



最も一般的なタイルであるレンガの壁について説明します。レンガの壁から、さまざまな確率で賞品が落ちる場合があります。

グルービーやコーヒースクリプトの括弧が少なくなることに注意してください。



 newTile("brick", { type: "solid", image: 44, //         onDamage: newChange("prise_in_brick", { "variants": [ //    { p : 60, item : getSlot("bomb")}, { p : 40, item : getSlot("power")}, { p : 40, item : getSlot("scate")}, { p : 10, item : getSlot("kick")}, { p : 10, item : getItem("random")}, //      ,     -     { p : 10, item : newItem("surprise", {image: 11, effect: ITEM_EFFECT_SANTA_CANT_TAKE, variants: [ {p: 5, slot: getSlot("bomb") }, {p: 5, slot: getSlot("power") }, {p: 4, slot: getSlot("scate") }, {p: 4, slot: getSlot("kick") }, {p: 3, slot: getSlot("jelly") }, {p: 5, slot: getSlot("detonator") }, {p: 6, slot: getItem("ball") }, {p: 0, slot: getSlot("money"), count:20} ]}) }, { p : 10, item : getSlot("heart")}, { p : 430 } ], //        result: getChange("destroy_rocky") //   ,        }) }); newChange("destroy_rocky", {effect: BLOCK_EFFECT_DESTROY_BLOCK, variants: [ {p:2, result: getTile("grass")}, {p:1, result: getTile("grass2")}, {p:1, result: getTile("grass3")}, {p:1, result: getTile("rocky")} ]});
      
      







実装


タイプnewXXXおよびgetXXXの関数は、ペア(オブジェクトID、オブジェクトタイプ)を返すことにより、エンティティを作成および検索します。 また、newXXXは、オブジェクトのフィールドのタイプかどうかをチェックします。 すべてのget-sは名前で実行され、まだ作成されていない場合でもオブジェクトを返します。これにより、モジュール性が保証されます。つまり、設定をゲームのさまざまな側面に対応するさまざまなファイルに分割できます。 この場合、これらのスクリプトの実行順序は正確には関係ありません。



構成の結果、ScriptClassTableテーブルは、ScriptClassから継承したオブジェクトで作成されます。 この場合、オブジェクトIDはスクリプトの実行中に自動的に生成されます。 オブジェクト名なしでIDのみがネットワークを介して送信され、クライアントとサーバーで同じです。1つのスクリプトがあちこちで実行されたためです。



ブラウザクライアントはJSで構築されているため、最初はJavaScriptを使用しました。 サーバーはRhinoエンジンを使用しました。 抽象化は、ScriptUtilsインターフェイスの異なる実装のために実行されました。これは、ゲームモデルについてはまったく知りませんが、スクリプトには「オブジェクト」、「文字列」、「リスト」、「数値」の種類があり、スクリプトはメソッドを呼び出すことができることを知っています。



その後、ネットワークを介してテーブルを転送し始めましたが、スクリプトはサーバー上でのみ実行され始めました。 現在、構成をgroovyに変換し、データを記述するだけでなく、クロージャーを使用していくつかの特別なエンティティの動作を設定できるようにしています。 もちろん、JSを使用してこれを行うこともできますが、Javaでgroovyスクリプトからコードを呼び出すのは、ランタイムで直接バイナリにコンパイルされるため、はるかに簡単です。 また、Groovy構文はより洗練されており、多くの場合、括弧は省略できます。 ただし、このためにコーヒーに切り替えることができました。



選択された抽象化のため、RhinoからGroovyへの転送には20分かかりました。ScriptUtilsクラスの別の実装を登録するだけで十分でした。 スクリプト自体はほとんど変更されていません。オブジェクトの説明と関数を説明する構文が中括弧で四角に変更されています。 移管後、すぐに生産に入りました。



サーバー上のブートストラップ:

  1. サーバーは、使用する構成を見つけ、httpを介して取得する場合もあります。
  2. スクリプトが実行され、オブジェクトタイプのテーブルが生成されます
  3. マップジェネレーターが作成され、テーブルから必要なタイルを名前で取得します。
  4. 残りのコントローラーが起動し、誰もが必要とするオブジェクトのタイプを要求して記憶します
  5. ゲーム開始
  6. ログインした各ユーザーには、テーブルと、ゲームクライアントが必要とするオブジェクトのフィールドのみが与えられます


幸いなことに、ゲームの読み込みには非常に短い時間がかかります。これにより、設定をすばやくデバッグできます。 ビジュアルツールはゲーム自体にまだ存在していますが、それらの助けを借りてまだ構成を編集することはできません。



この構成の一部のオブジェクトは、プレーヤー用に収集される分析形式を記述することに注意する価値があります。「マネー」スロットがある場合、ラウンドごとに収集されるこの「マネー」を増やすアイテムは、「マネー」列の統計データベースに移動するという事実にも影響します「。



すべてが好きです。


もちろん、このDSLは、ベースとなっている言語の喜びをまだ活用していません。 モデルを構築するだけでなく、対応するコントローラーにコードを注入することで、マップとモデル上の構造の生成を記述する構成を作成することもできます。 また、BLOCK_XXXやEFFECT_XXXなどのいくつかの定数は、バインディングを介してスクリプトに渡すことができます。



良い質問をする人は、ログインを書くと、ゲーム内のスキンにアクセスできるようになります:)そして、批判を恥ずかしがらないでください。



PSこの記事を書いている間、オンサイトのTopcoder Openに行くために中断する必要がありました



PSありがとうakzhan 、今はJRubyもあります



All Articles