XNA-コンテンツパイプライン拡張の後に実行されるゲームライブラリの静的クラス、問題を回避する方法


8

わかりました、このように定式化されているので、はっきりしないように思えますが、問題を説明するために最善を尽くします。

まず、実際のソースコードの何が問題かを説明しましょう。

私はプラットフォーマーを作っています。これが私のゲームの現在のコンポーネントです:タイルの独自のグリッドとタイルシートを保持しているLevelと、タイルシートの名前とインデックスを保持するTileだけです。タイルシート。これらはすべて、GameLibという名前の個別のXNAゲームプロジェクト内にあります。

レベルがテキストファイルでフォーマットされる方法は次のとおりです。

x x . . . . .

. . . b g r .

. . . . . . .

X X X X X X X

ここでは.、空のタイルを表すb青色のプラットフォームを表し、Xランダムな色のブロックを表し、等

Levelクラス内でレベルをロードするすべての作業を行う代わりに、コンテンツパイプライン拡張を利用して、LevelContentPipelineExtensionを作成することにしました。

始めてすぐに、次のような20行以上をハードコーディングしたくないことにすぐに気付きました。

if (symbol == 'b')
    return new Tile(/*...*/);

したがって、すべてのコンテンツ作成を少なくとも1つのクラスに集中させることを期待して、どのシンボルが何を作成し、この種のすべてのものを作成するかに関する情報を保持するLevelContentCreationと呼ばれる静的クラスを作成しました。また、後で読み込むために、タイルシートのすべてのアセットパスのリストを保持します。このクラスは、私のGameLibゲームライブラリプロジェクト内にあります。

問題は、このLevelContentCreation静的クラスをLevelクラス(実際のタイルシートを読み込むため)とコンテンツパイプライン拡張(どのシンボルがどのタイルを表すかを知るため)の両方で使用していることです...

そして、LevelContentCreationクラスは次のようになります。

public static class LevelContentCreation
{
    private static Dictionary<char, TileCreationInfo> AvailableTiles =
        CreateAvailableTiles();

    private static List<string> AvailableTileSheets { get; set; }

    /* ... rest of the class */
}

これでLevelContentCreationクラスはゲームライブラリの一部であるため、コンテンツパイプライン拡張機能がそれを使用しようとしますが、CreateAvailableTiles()の呼び出しは発生せず、コンテンツパイプラインからLevelオブジェクトをロードできません。

これは、実際のコンパイルなどの前にコンテンツパイプラインが実行されるためですが、問題を解決するにはどうすればよいですか?

LevelContentCreationクラスをパイプライン拡張に実際に移動することはできません。それは、Levelクラスがそれを使用できないためです...私はかなり迷っていて、どうすればいいのかわかりません...これらはわかっています。かなり悪い設計上の決定です。誰かが私を正しい方向に向けてくれたらいいのですが。

貴重なお時間ありがとうございました。

不明な点がある場合や、詳細情報やコードを提供する必要がある場合は、下にコメントを残してください。


私のプロジェクトに関するもう少し情報:

  • ゲーム -XNA Windowsゲームプロジェクト。への参照が含まれています:Engine、GameLib、GameContent
  • GameLib -XNAゲームライブラリプロジェクト。への参照が含まれています:エンジン
  • GameContent -XNAコンテンツプロジェクト。次への参照が含まれています:LevelContentPipelineExtension
  • エンジン -XNAゲームライブラリプロジェクト。参照なし。
  • LevelContentPipelineExtension -XNAコンテンツパイプライン拡張。への参照が含まれています:EngineおよびGameLib

XNA 4.0についてはほとんど何も知りませんが、コンテンツがどのように機能するかについて少し迷っています。さらに情報が必要な場合は、教えてください。


何を言っているのかよくわかりませんが、情報が不足しているようです。所有しているプロジェクト(ゲーム、ゲームライブラリ、コンテンツパイプライン拡張、コンテンツプロジェクト)と、何を参照しているのかを正確に説明できますか?
Andrew Russell

確かに、私はそれが十分に明確ではないことを知っていました。現在編集しています。
Jesse Emond、2011年

ちなみに、パイプライン拡張内のコンパイル時に例外がスローされることを除いて、すべてが正常に見えます。使用可能なシンボルがLevelContentCreationファイル内にまだロードされていないため、例外により、実際にはレベルファイルのコンテンツを変換できないことがわかります。パイプライン拡張と静的クラスの両方に例外を設定してテストしたところ、パイプライン拡張の例外が最初にスローされたため、問題が実際に発生している可能性があり、コードを再構築する必要があります。
Jesse Emond、2011年

回答:


11

シングルトン、静的クラス、またはグローバル変数を使用している場合、実際に使用すべき時間の99%はゲームサービスです。ゲームサービスは、クラスの1つのインスタンスを他のすべてのクラスで使用できるようにしたい場合に使用されますが、他のオプションの密結合により、面白い味が得られます。また、サービスには、これまでに見たインスタンス化の問題はありません。サービスは再利用可能であり、モック化 することができます(自動化された単体テストに関心がある場合)

サービスを登録するには、クラスに独自のインターフェースを与える必要があります。次に、電話をしてくださいGame.Services.AddService()。後で別のクラスでそのサービスを使用するには、を呼び出しますGame.Services.GetService()

あなたの場合、あなたのクラスは次のようになります:

public interface ILevelContentCreation
{
    void SomeMethod();
    int SomeProperty { get; }
}

public class LevelContentCreation : ILevelContentCreation
{
    private Dictionary<char, TileCreationInfo> availableTiles =
        CreateAvailableTiles();

    private List<string> AvailableTileSheets { get; set; }

    public void SomeMethod() { /*...*/ }
    public int SomeProperty { get; private set; }

    /* ... rest of the class ... */
}

プログラムの開始時点で、サービスをに登録する必要がありますGame.Services私はの最初にこれを行うことを好みますLoadContent()が、一部の人々はそのクラスのコンストラクタに各サービスを登録することを好みます。

/* Main game */
protected override void LoadContent()
{
    RegisterServices();

    /* ... */
}

private void RegisterServices()
{
    Game.Services.AddService(typeof(ILevelContentCreation), new LevelContentCreation());
}

これLevelContentCreationで、他のクラスのクラスにアクセスしたいときはいつでも、これを行うだけです。

ILevelContentCreation levelContentCreation = (ILevelContentCreation) Game.Services.GetService(typeof(ILevelContentCreation));
levelContentCreation.SomeMethod(); //Tada!

補足として、このパラダイムはInversion of Control(IoC)として知られており、XNA開発以外でも広く使用されています。.NetでのIoCの最も人気のあるフレームワークはNinjectで、Game.Servicesメソッドよりも構文が優れています。

Bind<ILevelContentCreation>().To<LevelContentCreation>(); //Ninject equivalent of Services.AddService()
ILevelContentCreation levelContentCreation = kernel.Get<ILevelContentCreation>(); //Ninject equivalent of Services.GetService()

IoCの形式である依存性注入もサポートしています。IoCの形式は少し複雑ですが、操作がはるかに簡単です。

[編集]もちろん、XNAを使用してその構文を取得することもできます。

static class ServiceExtensionMethods
{
    public static void AddService<T>(this GameServiceContainer services, T service)
    {
        services.AddService(typeof(T), service);
    }

    public static T GetService<T>(this GameServiceContainer services)
    {
        return (T)services.GetService(typeof(T));
    }
}

1
うわー、問題は修正しましたが、これによりコードがよりクリーンになります。あなたの答えを受け入れて、助けてくれてありがとう。
ジェシーエモンド2011年

1

実際に問題を修正する方法を見つけました。静的クラスを削除して、通常のクラスにしました。このようにして、適切なステートメントが適切なタイミングで実行されると確信しています。

そんなに時間を無駄にしたくなかったらよかったのですが、実際にコメント/回答を読んでそうすることを考えました。

とにかくありがとうございました。


上記/下記の私の回答とは無関係の別のメモ:単に異なるタイプを返すif/ switchステートメントを実行していることに気付いた場合は、代わりにそれらの値をクラスの一部にする必要があります。あなたの場合、TileTypeタイルの各タイプに関する情報(テクスチャ、save-file-letterなど)を含むクラスを作成します。次にTileType、文字からを取得するには、のリストを検索するTileTypesか、パフォーマンスが心配な場合Dictionary<char, TileType>は、TileTypeクラスに静的を格納します(TileTypeコンストラクターに自動的に入力できます)。
BlueRaja-Danny Pflughoeft、2011年

次に、それぞれTile(画面に表示される単一のタイルを表すクラス)が単一のを参照しTileTypeます。十分なタイルに特別なコードを必要とする独自の動作がある場合は、代わりにクラス階層を使用する必要があります。
BlueRaja-Danny Pflughoeft、2011年

0

これを見てくださいhttp : //msdn.microsoft.com/en-us/library/bb447745.aspx

レベルレイアウトの場合、「LoadFromFile(string name)」という関数をLevelクラス内に配置するだけです。後でゲームにカスタムマップのサポートを追加する場合は、とにかく独自のファイル形式からレベルを保存してロードする必要があるためです。

また、コンテンツインポーターを作成して、含まれているアセットやユーザーが提供したファイルから読み込むこともできます。インポーターの作成方法の詳細については、このページをご覧ください。

http://msdn.microsoft.com/en-us/library/bb447743.aspx


ご回答有難うございます。私の問題は実際にはコンテンツパイプライン拡張の作成が原因ではなく、レベル作成クラスがレベル​​クラスとコンテンツパイプライン拡張の両方で使用されているため、静的クラス宣言の前にパイプラインコードが実行され、そこでは何も初期化されません。
ジェシーエモンド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.