Загрузка текстур из ссылки на встроенный контент в файле XML
Цель
Я пытаюсь загрузить пользовательский класс, который содержит 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= нейтральный, PublicKeyToken=842cf8be1de50553 конфликтует с существующим обработчиком Microsoft.Xna.Framework.Content.ReflectiveReader`1.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 или подкласса, но если нет альтернативы, я рад за решение, которое делает это.
1 ответ
Точное сообщение об ошибке было вызвано наличием другого 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;
}
}