If you want to avoid adding attributes to your classes (required for automatic reading/writing) then you'll want to implement a ContentTypeReader and ContentTypeWriter
Here's my runtime class that I want to load:
public class ComponentPropertyGetMathExpression : MathExpression
{
public Type ComponentType;
public string PropertyName;
public ComponentPropertyGetMathExpression()
{
}
// methods removed
}
I have 2 fields I want to get the values for.
One interesting thing here is, one of them has the type Type
, which doesn't have a public parameterless constructor. You'd get weird errors if you tried to use the default reader/writer because of this, so this is a real example of when you would need to implement your own.
The first thing, is to create a Content version of this class in a seperate project, that will references the project with the runtime type and will be referenced by the content project:
[ContentSerializerRuntimeType("FuzzyDuckEntertainment.Entities.Templates.Expressions.ComponentPropertyGetMathExpression, FuzzyDuckEntertainment.Entities")]
public class ComponentPropertyGetMathExpressionContent : MathExpressionContent
{
public string ComponentType;
public string PropertyName;
public ComponentPropertyGetMathExpressionContent()
{
}
// Runtime methods not needed - this class is simply a container of fields/properties
}
The ContentSerializerRuntimeType attribute tells the pipeline what type this will represent at runtime.
Note that the ComponentType
field was a Type
at runtime, but here it's a string
. This is because of the lack of public parameterless constructor as noted above. Another time you might have different types is when the runtime type is something GPU related, for example a Texture2D
(in which case you might have ExternalReference<Texture2D>
and use a ContentProcessor).
Now we have our 2 types.
I'm going to use the built in XML Importer to go from an XML file to a ComponentPropertyGetMathExpressionContent
object and no Processor. A case where you might use your own importer is if you have a heightmap BMP and want to generate vertex data. An example of using a processor is the Font Texture Processor that creates a game Font from a Texture.
Now we need to get from the content type, to the runtime type.
First, lets create the class that writes the XNB
file at build time:
[ContentTypeWriter]
public class ComponentPropertyGetMathExpressionContentWriter : ContentTypeWriter<ComponentPropertyGetMathExpressionContent>
{
protected override void Write(ContentWriter output, ComponentPropertyGetMathExpressionContent value)
{
output.Write(value.ComponentType);
output.Write(value.PropertyName);
}
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return typeof(ComponentPropertyGetMathExpressionReader).AssemblyQualifiedName;
}
public override string GetRuntimeType(TargetPlatform targetPlatform)
{
return typeof(ComponentPropertyGetMathExpression).AssemblyQualifiedName;
}
}
Hopefully the above should be self explanatory (note the order we write the fields in).
The reader is implemented as below:
public class ComponentPropertyGetMathExpressionReader : ContentTypeReader<ComponentPropertyGetMathExpression>
{
protected override ComponentPropertyGetMathExpression Read(ContentReader input, ComponentPropertyGetMathExpression existingInstance)
{
if (existingInstance == null)
existingInstance = new ComponentPropertyGetMathExpression();
existingInstance.ComponentType = Type.GetType(input.ReadString());
existingInstance.PropertyName = input.ReadString();
return existingInstance;
}
}
Note the order we read things in. Also note that the first thing we read is the string that correspond to the ComponentType
property. As the runtime type in this case is Type
, not string
I need to do some simple translation. Ideally you want any translations like this to be as minimal as possible, as unlike anything before, this will run when the game is loading.
See also:
What Is Content? (MSDN)
Using an XML File to Specify Content (MSDN)
Shawn Hargreaves' Blog Index #Content Pipeline