从XML文件中对构建内容的引用加载纹理

时间:2011-12-08 08:52:06

标签: c# xna xna-4.0 content-pipeline

目标

我正在尝试使用默认导入程序(XML Content)从xml文件加载包含Texture2D的自定义类,没有处理器。


方法

许多在线研究和大量处理其他错误的问题都让我想到了这个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");下面是我得到的错误:

  

加载“Bullet”时出错。 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 = 842cf8be1de50553,类型为Microsoft.Xna.Framework.Graphics.Texture2D。

看起来运行时阅读器找到了2位读者来处理Texture2D资产,ReflectiveReader<Texture2D>读者和Texture2DReader


问题

我如何解决这个问题,所以我最终得到了一个正确填充的对象,Texture2D属性引用了加载的纹理?

注意:我不想添加另一个字符串属性,并在我的对象上创建一个名为LoadContent的方法。我希望Content.Load成为我唯一需要打电话的人。

我还想避免为包含Texture2D属性的每种类型编写自己的读者/编写者。

理想情况下,我想避免为Texture2D或子类创建包装类,但如果没有其他选择,那么我很高兴能找到解决方案。

1 个答案:

答案 0 :(得分: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;
    }
}