コメントでは、古い答えの要約版を投稿しました:
使用するDrawableGameComponent
と、メソッドシグネチャの単一セットと特定の保持データモデルにロックされます。通常、これらは最初は間違った選択であり、ロックインは将来のコードの進化を大きく妨げます。
ここで、現在のプロジェクトからコードを投稿することで、なぜそうなのかを説明します。
ゲームワールドの状態を維持するルートオブジェクトには、Update
次のようなメソッドがあります。
void Update(UpdateContext updateContext, MultiInputState currentInput)
Update
ゲームがネットワーク上で実行されているかどうかに応じて、にいくつかのエントリポイントがあります。たとえば、ゲームの状態を前の時点にロールバックしてから再シミュレートすることができます。
次のような追加のアップデートのようなメソッドもいくつかあります。
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
ゲームの状態には、更新するアクターのリストがあります。しかし、これは単なるUpdate
呼び出し以上のものです。物理を処理する前後に追加のパスがあります。また、レベルトランジションだけでなく、アクターのスポーン/破壊の遅延のためのいくつかの派手なものもあります。
ゲームの状態以外に、独立して管理する必要があるUIシステムがあります。ただし、UIの一部の画面は、ネットワークコンテキストでも実行できる必要があります。
描画の場合、ゲームの性質上、これらのメソッドから入力を受け取るカスタムソートおよびカリングシステムがあります。
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
そして、何を描画するかがわかると、自動的に呼び出します:
virtual void Draw(DrawContext drawContext, int tag)
tag
一部のアクターは他のアクターを保持できるため、このパラメーターが必要です。したがって、保持されているアクターの上下に描画できる必要があります。並べ替えシステムは、これが正しい順序で行われるようにします。
描画するメニューシステムもあります。そして、HUDを中心に、ゲームを中心にUIを描画するための階層システム。
次に、これらの要件をDrawableGameComponent
提供するものと比較します。
public class DrawableGameComponent
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
public int UpdateOrder { get; set; }
public int DrawOrder { get; set; }
}
DrawableGameComponent
上記のシステムを使用しているシステムから取得する合理的な方法はありません。(そして、それらは控えめな複雑さのゲームにとっても珍しい要件ではありません)。
また、これらの要件は、プロジェクトの開始時に単に発生したものではないことに注意することが非常に重要です。彼らは何ヶ月もの開発作業を経て進化しました。
人々が一般的に行うことは、彼らがDrawableGameComponent
ルートを開始する場合、彼らはハックで構築を開始することです:
メソッドを追加する代わりに、独自のベースタイプにsomeいダウンキャストを行います(それを直接使用しないのはなぜですか)。
Draw
/ をソートして呼び出すためのコードを置き換える代わりにUpdate
、順序番号をその場で変更するためにいくつかの非常に遅い処理を行います。
そして最悪なのは、メソッドにパラメーターを追加する代わりに、スタックではないメモリー位置で「パラメーター」を「渡す」ことです(これの使用Services
は一般的です)。これは複雑で、エラーが発生しやすく、遅く、壊れやすく、スレッドセーフではないため、ネットワークを追加したり、外部ツールを作成したりすると問題になる可能性があります。
また、API境界で公開されるのではなく、「コンポーネント」内に依存関係が隠されるため、コードの再利用が難しくなります。
または、彼らはこれがどれほどクレイジーであるかをほとんど理解しており、DrawableGameComponent
これらの問題を回避するために「マネージャー」を始めます。「マネージャー」の境界でこれらの問題がまだあることを除いて。
常にこれらの「マネージャー」は、必要な順序で一連のメソッド呼び出しに簡単に置き換えることができます。それ以外のものは複雑すぎます。
もちろん、これらのハックの使用を開始したら、提供する以上の機能が必要であることがわかったら、それらのハックを元に戻すには実際の作業が必要DrawableGameComponent
です。「ロックイン」されます。そして、誘惑はしばしばハッキングを積み重ね続けることです-それは実際にはあなたを動かし、あなたが作ることができるゲームの種類を比較的単純なものに制限します。
多くの人がこの点に到達し、内臓反応を持っているようです- DrawableGameComponent
「間違っている」ことを受け入れ、それを受け入れたくないからでしょう。そのため、彼らは少なくとも「動作」させることが可能であるという事実を把握しています。
もちろんそれはすることができます作られた「仕事」に!私たちはプログラマーです-物事を異常な制約の下で機能させることは、実際には私たちの仕事です。しかし、この制約は必要ではなく、有用でも合理的でもありません...
特に驚くべきことは、この問題全体を回避することは驚くほど簡単なことです。ここでは、コードも提供します。
public class Actor
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
}
List<Actor> actors = new List<Actor>();
foreach(var actor in actors)
actor.Update(gameTime);
foreach(var actor in actors)
actor.Draw(gameTime);
(に従ってソートに追加することのシンプルDrawOrder
かつUpdateOrder
。1つの簡単な方法は、二つのリスト、および使用を維持することであるList<T>.Sort()
。処理するためにも簡単ですLoad
/ UnloadContent
。)
そのコードが実際にどのような重要ではありませんです。重要なのは、私がそれを簡単に修正できることです。
に別のタイミングシステムが必要であることがわかった場合gameTime
、またはより多くのパラメーター(またはUpdateContext
約15個のオブジェクトを含むオブジェクト全体)が必要な場合、描画のためにまったく異なる操作順序が必要な場合、または追加のメソッドが必要な場合さまざまな更新パスについては、先に進んでそれを行うことができます。
そして、私はすぐにそれを行うことができます。私はDrawableGameComponent
自分のコードを選ぶことを気にする必要はありません。さらに悪いことには、そのような必要性が次に発生したときにさらに難しくなる何らかの方法でハックします。
実際に使用するのが適切なシナリオは1つだけです。それは、XNA用のドラッグアンドドロップコンポーネントスタイルのミドルウェアをDrawableGameComponent
文字通り作成および公開している場合です。それが本来の目的でした。これ-私は追加します- 誰もやっていない!
(同様に、のカスタムコンテンツタイプを作成するServices
ときに使用するのは本当に意味がありますContentManager
。)