7

自分が書いているゲームのファイルパーサーを作成して、ゲームのさまざまな側面(キャラクター/ステージ/衝突データなど)を簡単に変更できるようにしました。たとえば、次のような文字クラスがあるとします。

class Character
{
public:
    int x, y; // Character's location
    Character* teammate;
}

C++に似た構文のデータ構造をファイルから読み込むようにパーサーを設定しました

Character Sidekick
{
    X = 12
    Y = 0
}

Character AwesomeDude
{
    X = 10
    Y = 50
    Teammate = Sidekick
}

これにより、2つのデータ構造が作成され、マップに配置されます<std::string, Character*>。ここで、キー文字列は、私が付けた名前(この場合はSidekickとAwesomeDude)です。私のパーサーがチームメイトポインターのようなクラスへのポインターを見ると、そのデータ構造へのポインターをフェッチするためにマップを検索するのに十分賢いです。問題は、Sidekickのチームメイトがまだキャラクターマップに配置されていないため、AwesomeDudeであると宣言できないことです。

マップにまだ追加されていないオブジェクトをデータ構造で参照できるように、これを解決するための最良の方法を見つけようとしています。私が考えることができる2つの最も簡単な解決策は、(a)データ構造を前方宣言する機能を追加するか、(b)パーサーにファイルを2回読み取らせることです。1回目は空のデータ構造へのポインターをマップに入力し、2回目は通過し、それらを記入します。

(a)の問題は、クラスで呼び出すコンストラクターも決定できることです。前方宣言を行う場合は、コンストラクターを残りのデータから分離する必要があり、混乱を招く可能性があります。(b)の問題は、SidekickとAwesomeDudeをそれぞれのファイルで宣言したい場合があることです。パーサーが一度に1つだけではなく、読み取るファイルのリストを取得できるようにする必要があります(これはそれほど悪くはないと思いますが、読み取り先のファイルのリストを取得したい場合もあります。ファイル)。(b)コンストラクター自体で後で宣言されたデータ構造を使用できないという欠点もありますが、それは大したことではないと思います。

どちらの方法がより良いアプローチのように聞こえますか?私が考えていなかった3番目のオプションはありますか?ポインタ参照やバインディングなどを使用して、これに対する巧妙な解決策があるはずです...:-/これは、自分に提供したい機能に基づいてある程度主観的だと思いますが、どのような入力でも構いません。

4

6 に答える 6

13

初めて参照に遭遇したときは、参照として保存するだけです。次に、文字や参照などを「後で解決する必要のある参照」のリストに追加できます。

ファイルが完成したら、参照があるものを実行して解決します。

于 2009-04-28T19:58:42.020 に答える
5

さて、あなたは 3 番目のオプションを要求しました。XML を使用する必要はありませんが、次の構造に従えば、SAX パーサーを使用してデータ構造を構築するのは非常に簡単です。

とにかく、チームメイトを参照する代わりに、各キャラクターはチーム (この場合は青チーム) を参照します。これにより、循環参照の問題が解消されます。キャラクターの前にチームをリストするようにしてください.

<team>Blue</team>

<character>
    <name>Sidekick</name>
    <X>12</X>
    <Y>0</Y>
    <teamref>Blue</teamref>
</character>

<character>
    <name>Sidekick</name>
    <X>10</X>
    <Y>50</Y>
    <teamref>Blue</teamref>
</character>
于 2009-04-28T20:22:13.757 に答える
2

個人的には、b) を選びます。コードを Parser クラスと Validator クラスに分割し、どちらも同じデータ構造で動作します。パーサーはファイルを読み取って解析し、データ構造を埋めてオブジェクト参照をテキスト名として保存し、構造内の実際のポインターを今のところ null のままにします。

ファイルのロードが終了したら、Validator クラスを使用して参照を検証および解決し、「実際の」ポインターを入力します。これらのルックアップを適切かつ高速にするために、データを構造化する方法を検討する必要があります。

于 2009-04-28T20:03:27.843 に答える
1

ウィルは私が書こうとしていたことを正確に言った。未解決の参照を含むリストまたは何かを保持するだけです。

ファイルの読み取りが完了したら、未解決の参照がある場合はエラーをスローすることを忘れないでください = P

于 2009-04-28T20:02:14.617 に答える
0

1 つのオプションは、義務を無効にすることです。マップは、参照を埋める責任があります

template<T> class SymbolMap // I never could rememeber C++ template syntax
{
   ...

   /// fill in target with thing name
   /// if no name yet, add it to the list of thing that will be name
   void Set(T& target, std::string name);

   /// define name as target
   /// go back and fill in anything that needs to be name
   void Define(T target, std::string name);

   /// make sure everything is resolved
   ~SymbolMap()
}

それは価値/移動セマンティクスとうまく相互作用しませんが、あまりそうではないと思います。

于 2009-04-28T22:47:13.237 に答える
0

Character オブジェクトをマップに格納する代わりに、Character のプロキシを格納します。オブジェクトが読み込まれると、プロキシには実際の Character オブジェクトへのポインタが含まれます。Character::teammate のタイプは、このプロキシ タイプに変更されます。マップにまだない参照を読み込む場合は、プロキシを作成してそのプロキシを使用します。マップに既に空のプロキシがあるキャラクターをロードする場合は、新しくロードしたキャラクターをそのプロキシに入力します。カウンターを追加して、マップ内にある空のプロキシの数を追跡し、参照されているすべてのキャラクターがいつ読み込まれたかを確認することもできます。

間接的な別のレイヤー....それは常にプログラミングをより簡単にし、遅くします。

于 2009-04-28T20:44:03.517 に答える