6

ゴール

Texture2Dプロセッサーを使用せずに、デフォルトのインポーター (XML コンテンツ) を使用して、xml ファイルからを含むカスタム クラスをロードしようとしています。


アプローチ

オンラインで多くの調査を行い、他のエラーを処理することで、次の XML にたどり着きました。

<?xml version="1.0" encoding="utf-16"?>
<XnaContent xmlns:Components="Entities.Components">
  <Asset Type="EntitiesContentPipeline.EntityTemplateContent">
    <Name>entity name</Name>
    <TestTexture>
      <Reference>#External1</Reference>
    </TestTexture>
  </Asset>
  <ExternalReferences>
    <ExternalReference ID="#External1" TargetType="Microsoft.Xna.Framework.Graphics.Texture2D">C:\Documents and Settings\GDuckett\My Documents\Visual Studio 2010\Projects\Gravitron\Gravitron\Gravitron\bin\x86\Debug\Content\Bullet.xnb</ExternalReference>
  </ExternalReferences>
</XnaContent>

はい、ハードコードされたパスも好きではありませんが、カスタム リーダーやライターを使用せずにこれを機能させることができれば、それを使用Texture2Dできます。

以下は、クラスのコンテンツ バージョンです (パイプラインで使用されます)。

[ContentSerializerRuntimeType("Entities.Content.EntityTemplate, Entities")]
public class EntityTemplateContent
{
    public string Name;
    public ExternalReference<Texture2D> TestTexture;

    public EntityTemplateContent()
    {

    }
}

以下は私のランタイムバージョンです:

public class EntityTemplate
{
    public string Name;
    public Texture2D TestTexture;

    public EntityTemplate()
    {

    }
}

問題

以下を試してみると、var test = Content.Load<EntityTemplate>("BulletTemplate");次のエラーが表示されます。

「弾丸」の読み込み中にエラーが発生しました。ContentTypeReader Microsoft.Xna.Framework.Content.Texture2DReader、Microsoft.Xna.Framework.Graphics、Version=4.0.0.0、Culture=neutral、PublicKeyToken=842cf8be1de50553 は、既存のハンドラー Microsoft.Xna.Framework.Content.ReflectiveReader`1[[Microsoft .Xna.Framework.Graphics.Texture2D、Microsoft.Xna.Framework.Graphics、Version=4.0.0.0、Culture=neutral、PublicKeyToken=842cf8be1de50553]]、Microsoft.Xna.Framework、Version=4.0.0.0、Culture=neutral、PublicKeyToken Microsoft.Xna.Framework.Graphics.Texture2D 型の場合は =842cf8be1de50553。

ランタイム リーダーは、Texture2Dアセットを処理するための 2 つのリーダー、ReflectiveReader<Texture2D>リーダーとを見つけたようTexture2DReaderです。


質問

この問題を解決するにはどうすればよいTexture2Dですか? ロードされたテクスチャを参照するプロパティを使用して、オブジェクトが正しく取り込まれますか?

注:別の文字列プロパティを追加して、オブジェクトにメソッドなどを作成したくありませんLoadContentContent.Load私は私が電話する必要がある唯一のものになりたいです。

また、プロパティを含むすべての型に対して独自のリーダー/ライターを作成することも避けたいと考えていTexture2Dます。

理想的には、Texture2D またはサブクラスのラッパー クラスを作成することは避けたいと考えていますが、代替手段がない場合は、これを行うソリューションに満足しています。

4

1 に答える 1

0

Texture2D正確なエラー メッセージは、コンテンツ オブジェクトに別のフィールドがあるために発生しました。

ExternalReference<T>コンテンツ タイプからランタイム タイプへの参照を取得するという全体的な問題は、以下で解決されました。

現在、これは実際には概念実証クラスであり、これまでに投げたクラスで機能しますが、おそらくより複雑なものでクラッシュします。

リフレクションを使用して、入力のフィールドまたはプロパティを、適切なバージョンを作成して呼び出すことExternalReference<T>により、要求された型のビルド バージョンに変換します。ContentProcessorContext.BuildAsset<T,T>他のオブジェクトへの参照に対して同じことを行うために、オブジェクト ツリーを下方向に再帰します。

[ContentProcessor(DisplayName = "ExternalRefObjectContentProcessor")]
public class ExternalRefObjectContentProcessor : ContentProcessor<object, object>
{
    private void ReplaceReferences(object input, ContentProcessorContext context)
    {
        Func<ExternalReference<object>, string, object> BuildAssetMethodTemplate = context.BuildAsset<object, object>;
        var BuildAssetMethod = BuildAssetMethodTemplate.Method.GetGenericMethodDefinition();

        foreach (var field in input.GetType().GetFields().Where(f => !f.IsStatic && !f.IsLiteral))
        {
            Type fieldType = field.FieldType;
            object fieldValue = field.GetValue(input);

            if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(ExternalReference<>))
            {
                var GenericBuildMethod = BuildAssetMethod.MakeGenericMethod(fieldType.GetGenericArguments().First(), fieldType.GetGenericArguments().First());

                object BuiltObject;

                try
                {
                    BuiltObject = GenericBuildMethod.Invoke(context, new object[] { fieldValue, null });
                }
                catch (Exception Ex)
                {
                    throw Ex.InnerException;
                }

                field.SetValue(input, BuiltObject);
            }
            else if (fieldValue is IEnumerable && !(fieldValue is string))
            {
                foreach (var item in (fieldValue as IEnumerable))
                {
                    ReplaceReferences(item, context);
                }
            }
            else if (fieldValue != null && !(fieldValue is string))
            {
                ReplaceReferences(fieldValue, context);
            }
        }

        foreach (var property in input.GetType().GetProperties().Where(p => p.CanRead && p.CanWrite))
        {
            Type propertyType = property.PropertyType;
            object propertyValue = property.GetValue(input, null);

            if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(ExternalReference<>))
            {
                var GenericBuildMethod = BuildAssetMethod.MakeGenericMethod(propertyType.GetGenericArguments().First(), propertyType.GetGenericArguments().First());

                object BuiltObject;

                try
                {
                    BuiltObject = GenericBuildMethod.Invoke(context, new object[] { property.GetValue(input, null), null });
                }
                catch (Exception Ex)
                {
                    throw Ex.InnerException;
                }
                property.SetValue(input, BuiltObject, null);
            }
            else if (propertyValue is IEnumerable && !(propertyValue is string))
            {
                foreach (var item in (propertyValue as IEnumerable))
                {
                    ReplaceReferences(item, context);
                }
            }
            else if (propertyValue != null && !(propertyValue is string))
            {
                ReplaceReferences(propertyValue, context);
            }
        }
    }

    public override object Process(object input, ContentProcessorContext context)
    {
        ReplaceReferences(input, context);

        return input;
    }
}
于 2011-12-16T11:09:10.850 に答える