我正在尝试使用默认导入程序(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或子类创建包装类,但如果没有其他选择,那么我很高兴能找到解决方案。
答案 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;
}
}