6

目标

我正在尝试使用默认导入器(XML 内容)加载包含Texture2D来自 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,版本=4.0.0.0,文化=中性,PublicKeyToken=842cf8be1de50553]],Microsoft.Xna.Framework,版本=4.0.0.0,文化=中性,PublicKeyToken =842cf8be1de50553 用于 Microsoft.Xna.Framework.Graphics.Texture2D 类型。

看起来运行时阅读器找到了 2 个用于处理Texture2D资产的阅读器,ReflectiveReader<Texture2D>阅读器和Texture2DReader.


问题

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

注意:我不想添加另一个字符串属性并在我的对象上创建一个名为LoadContent或其他东西的方法。我想Content.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 回答