Fun with Shaders and the Depth Buffer

Recently, I read this presentation by Kuba Cupisz and Ole Ciliox and I wanted to try my hand at implementing some of the techniques mentioned in the slides. So far, I’ve written two successful shaders: Rendering the depth buffer and highlighting intersections (the scan field). Since there is no source code to accompany the presentation, I had to do a bit of digging to get everything to work.

The End Result
Here is a quick video of what these two shaders accomplish. Both cubes use the Depth Shader, while the sphere and the plane use the Intersection Highlighting Shader

Continue reading

Posted in Uncategorized | 4 Comments

Saving Game Data Using XML and Reflection

Preface

Currently, I am in the midst of a game design project (details of which are unimportant). My most recent task was tracking the player’s progress in a save file, which I decided could be easily done with an XML file. In previous projects, I used an XmlDocument and just stepped through the structure storing/retrieving data one node at a time. Out of laziness/desire to learn more about reflection, I set out to make a miniature version of JAXB (a java XML-binding framework).

Design

As an end result, I wanted to be able to create a “SaveFile” class that just had properties of the data as well as a marshaller/unmarshaller to put the data into an XML file and get it out. The entire setup came out as three classes for the un/marshaller (the third was an attribute class).

Early on, I wanted to limit the scope of my project as I only had a few days to get it fully working (game design projects require a complete game to be done in a matter of weeks so spending the entire time doing save files was not a good use of my time). To save time, I decided to restrict collections to only basic arrays and no arrays of primitives. I also used a string property named “InnerText” to access the text between an XML tag.

For the actual format of the XML, I went a fairly straight-forward data to XML conversion. Each custom data class was it’s own tag. Any properties that were primitives were attributes of the tag (with the exception of “InnerText” tags). Arrays and other custom data classes were child tags.

Implementation Basics: Attribute Tags

The first thing I went about coding was the attribute tags mainly because I was uncertain how they worked and I wanted everything to be setup when I want to write the marshaller. Writing a custom attribute in C# turned out to be a complete breeze, especially when the attribute didn’t need to have any data. ┬áThe entire class was two lines of code:

using System;
public class XMLElementAttribute : System.Attribute { }

This tag let me easily differentiate at runtime data storage classes with a simple if statement.

Marshalling the data into a XML File
Putting the data into an XML file turned out to be a pretty big challenge. The marshaller was allowed to make very few assumptions about the data (it could really only know that arrays were of custom-objects). The final implementation ended up being a fairly simple recursive formula: Given an object, process each property and convert it into XML. That property could either be an array, inner text, a custom data class (tagged with XMLElement), or a primitive. As discussed earlier, arrays and data classes became child nodes, primitives became attributes, and inner text became inner text. One of the biggest hurdles I had when implementing the code was trying to deal with differentiating the possibilities, although I think the final solution turned out to be concise and easy to follow.

using System.Xml;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;

public class XMLMarshaller : System.Object {
	
	private XmlDocument document;
	private XmlWriter writer;
	
	public XMLMarshaller(System.Object root) {
	
		Marshall(root);
	}
	
	public void Marshall(System.Object root) {
		
		document = new XmlDocument();
		document.AppendChild(createNode(root));
	}
	
	
	public void WriteToFile(string fileName) {
		
   		XmlTextWriter writer = new XmlTextWriter(fileName,null);
		writer.Formatting = Formatting.Indented;
    	        document.Save(writer);
		writer.Flush();
		writer.Close();
	}
	
	private XmlElement createNode(System.Object currentObject) {
		
		System.Type objectType = currentObject.GetType();
		XmlElement node = document.CreateElement(objectType.ToString());
		node.RemoveAll();
		
		//Get all of the classes properties 
		PropertyInfo[] myPropertyInfo = 
			objectType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
		
		foreach(PropertyInfo currentProperty in myPropertyInfo) {
			processNodeProperty(node, currentObject, currentProperty);
		}
		
		return node;
	}
	
	private void processNodeProperty(XmlNode node, System.Object currentObject, 
	                                 PropertyInfo currentProperty) {
		
		System.Type propertyType = currentProperty.PropertyType;
		
		//Process an array of XMLElements
		//In the current implementation, there is no support for Arrays of primitives
		var getMethod = currentProperty.GetGetMethod();
		if(getMethod.ReturnType.IsArray) {
			
			string arrayName = currentProperty.Name;
			XmlElement arrayNode = document.CreateElement(arrayName);
			node.AppendChild(arrayNode);
			
			System.Object[] arrayObject = (System.Object[])getMethod.Invoke(currentObject, null);
			foreach(object element in arrayObject) {
				
				arrayNode.AppendChild(createNode(element));
			}
			
			return;
		}
			
		//"InnerText" is a reserved property name that holds the inner text of the node
		if(currentProperty.Name.Equals("InnerText")){
			
			node.InnerText = currentProperty.GetValue(currentObject, null).ToString();
			return;
		}
		
		//Properties with the "XMLElementAttribute" are classes that also hold data. 
		//create them as child nodes
  		object[] attributes = propertyType.GetCustomAttributes(typeof(XMLElementAttribute), true);
		if(attributes.Length > 0) {		
			node.AppendChild(createNode((System.Object)
			                            currentProperty.GetValue(currentObject, null)));
			return;
		}
		
		//All other properties are added as attributes
		XmlAttribute newAttribute = document.CreateAttribute(currentProperty.Name);
		newAttribute.Value = currentProperty.GetValue(currentObject, null).ToString();
		
		node.Attributes.Append(newAttribute);
	}
}

Loading the Data
Simply storing the data into a file isn’t enough; it also has to be unmarshalled. The task of converting back turned out to be a lot easier than I had initially anticipated. The structure of the unmarshaller is almost exactly the same. The biggest hurdle I had was creating the arrays at runtime. I wound up using Array.CreateInstance and then casting it to System.Object[], which worked out perfectly.

using UnityEngine;
using System.Xml;
using System.Reflection;
using System.Collections.Generic;
using System;

public class XMLUnMarshaller : System.Object {
	
	private XmlDocument document;
	private Assembly assembly;
	
	public XMLUnMarshaller() {
		
		document = new XmlDocument();
		assembly = Assembly.GetExecutingAssembly();
	}
	
	public XMLUnMarshaller(string filename) {
		
		document = new XmlDocument();
		assembly = Assembly.GetExecutingAssembly();
		loadFile(filename);
	}
	
	public void loadFile(string filename) {
		
		document.Load(filename);
	}
	
	public System.Object unmarshall() {
		
		if(document.FirstChild == null && document.ChildNodes.Count == 2)
			return null;
		
		return processNode(document.ChildNodes[1]);
	}
	
	private System.Object processNode(XmlNode node) {
		
		//The name of the node is the class name. Try and create an instance
		System.Object nodeObject = assembly.CreateInstance(node.Name);
		if(nodeObject == null) {
			
			Debug.Log("Error: unable to process node \" " + node.Name + "\"");
			return null;
		}
		
		System.Type nodeObjectType = nodeObject.GetType();
		
		//Get all of the classes properties 
		PropertyInfo[] myPropertyInfo = 
			nodeObjectType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
		
		foreach(PropertyInfo currentPropertyInfo in myPropertyInfo) {
			
			processProperty(node, nodeObject, nodeObjectType, currentPropertyInfo);
		}
		
		return nodeObject;
	}
	
	private void processProperty(XmlNode currentNode, System.Object currentObject, 
	                             System.Type objectType, PropertyInfo currentProperty) {
		
		System.Type currentPropertyType = currentProperty.GetGetMethod().ReturnType;		
		
		//Process an array of XMLElements
		//In the current implementation, there is no support for Arrays of primitives D:
		if(currentPropertyType.IsArray) {
			
			XmlNode arrayNode = findChildNode(currentNode, currentProperty.Name);		
			System.Object[] childObjectArray = 
				(System.Object[])Array.CreateInstance(currentPropertyType.GetElementType(), 
				                                      arrayNode.ChildNodes.Count);
			
			for(int i = 0; i < childObjectArray.Length; i++) {
				
				childObjectArray[i] = processNode(arrayNode.ChildNodes[i]);
			}
			
			currentProperty.SetValue(currentObject, 
			                         Convert.ChangeType(childObjectArray, currentPropertyType), 
			                         null);
			return;
		}
			
		//"InnerText" is a reserved property name that holds the inner text of the node
		if(currentProperty.Name.Equals("InnerText")){
			
			currentProperty.SetValue(currentObject, currentNode.InnerText, null);
			return;
		}
		
		//Properties with the "XMLElementAttribute" are classes that also hold data.
		//create them as child nodes
  		object[] attributes = 
			currentPropertyType.GetCustomAttributes(typeof(XMLElementAttribute), true);
		
		if(attributes.Length > 0) {		
			
			XmlNode elementNode = findChildNode(currentNode, currentPropertyType.ToString());
			currentProperty.SetValue(currentObject, processNode(elementNode), null);
			return;
		}
		
		//All other properties are added as attributes
		string nodeValue = currentNode.Attributes[currentProperty.Name].Value;
		currentProperty.SetValue(currentObject, 
		                         Convert.ChangeType(nodeValue, currentPropertyType), null);
	}	
			        
	private XmlNode findChildNode(XmlNode parentNode, string nodeName) {
	
		foreach(XmlNode childNode in parentNode.ChildNodes)
			if(childNode.Name.Equals(nodeName))
				return childNode;
		
		return null;
	}
}

Sample Test Case
In case any reader wants to see a sample implementation (I know that always helps me with a new topic), here’s a quick example of all of the major features. There’s two custom data classes: TestXMLNodeRoot and TestXMLNodeChild. They are posted below:

using UnityEngine;
using System.Collections;
using System;

[XMLElement]
public class TestXMLNodeRoot : System.Object {

	public bool IsEnabled { get; set; }
	public TestXMLNodeChild[] Children { get; set; }
	public TestXMLNodeChild Child { get; set; }
}

[XMLElement]	
public class TestXMLNodeChild : System.Object {

	public int TestInt { get; set; }
	public string InnerText { get; set; }	
}

Here’s the script to test marshalling and unmarshalling the above classes:

using UnityEngine;
using System.Collections;

public class test : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
		TestXMLNodeRoot expectedRoot = new TestXMLNodeRoot();
		expectedRoot.IsEnabled = false;
		
		TestXMLNodeChild[] child = { new TestXMLNodeChild(), new TestXMLNodeChild() };
		child[0].TestInt = 0;
		child[1].TestInt = 1;
		
		child[0].InnerText = "inner text 1";
		child[1].InnerText = "inner text 2";
		
		expectedRoot.Children = child;
		expectedRoot.Child = child[0];
		
		XMLMarshaller m = new XMLMarshaller(expectedRoot);
		m.WriteToFile("test.xml");
	
		XMLUnMarshaller um = new XMLUnMarshaller(Application.dataPath + "/" + "test.xml");
		TestXMLNodeRoot actualRoot = (TestXMLNodeRoot)um.unmarshall();
		
		assertEqual(expectedRoot.IsEnabled, actualRoot.IsEnabled);
		assertEqual(expectedRoot.Children.Length, actualRoot.Children.Length);
		
		for(int i = 0; i < expectedRoot.Children.Length; i++) {
			
			assertEqual(expectedRoot.Children[i].TestInt, actualRoot.Children[i].TestInt);
			assertEqual(expectedRoot.Children[i].InnerText, actualRoot.Children[i].InnerText);
		}
		
		assertEqual(expectedRoot.Child.TestInt, actualRoot.Child.TestInt);
		assertEqual(expectedRoot.Child.InnerText, actualRoot.Child.InnerText);
	}
	
	private void assertEqual(System.Object expected, System.Object actual) {
		
		if(!expected.Equals(actual)) {
			
			Debug.Log("Assertion failed: " + expected.ToString() + " != " + actual.ToString());
		}
	}
}

And to wrap it up: here is what the final product (the XML file) looks like

<?xml version="1.0"?>

<TestXMLNodeRoot IsEnabled="False">
  <Children>
    <TestXMLNodeChild TestInt="0">inner text 1</TestXMLNodeChild>
    <TestXMLNodeChild TestInt="1">inner text 2</TestXMLNodeChild>
  </Children>
  <TestXMLNodeChild TestInt="0">inner text 1</TestXMLNodeChild>
</TestXMLNodeRoot>

Final Thoughts/Future Work
Overall, I had a lot of fun learning more about reflection. It is a topic that I have wanted to get into for awhile and this save file project was a great introductory lesson. After this game project is over I will probably revisit this and try to make the system more robust. I definitely want to add support for other collections as well as collections of primitive data types; however, for now, I am fairly proud of the final result.

Apologies if this post was a little code-heavy: I’m just getting started with this whole blogging thing. If you have any questions or suggestions on how to improve please feel free to comment.

Posted in Programming, Uncategorized, Unity | Leave a comment