Customizable Navigation Bar

Friday, October 25, 2013

Unity Editor Tools And XML To Save Time

As you develop you will find more and more circumstances where hard coding data into your game becomes a huge pain. Having to recompile your whole game just to change the price of an item, or change a spelling mistake in the description of a trophy will get old really fast, which is where soft coding and dynamic loading comes into play.

With Unity's editor being so extensible, soft coding is a very attractive option for a lot of the data you will need for your game. Within minutes you can have data classes to store your values, and create editor scripts to make data creation extremely simple and user friendly. Today you are going to see a very basic implementation of this.

What we are going to be doing is we are going to create a data class for an in game item, an editor class that will allow you to create and store many items, and a class that will load the data and display it in a log. All of the data is going to be stored as XML files in this tutorial since it is built into .NET.

So the first thing that we want to do is create our data class, so create a new C# script in Unity and call it ItemData.

public class ItemData
{
//Lets make two string variables that will hold the name and description of the trophy
public string name = string.Empty;
public string description = string.Empty;
//Lets say that the item is also purchased from a store in your game, so we can set a price for it as well
public int price = 10;
/*
* This would be as simple as it gets. You could also add in extra detail such as the name 
* of the icon for this item that would be loaded from resources
*/
}

This is a very basic implementation of item data. In your game you may want to put more information in such as the icon name, texture path, and possibly even more. It all depends on what your uses are going to be.


So now that we have the data class, let's create an Editor Window to edit, create and remove data. The editor window is very useful, it can be docked just like your Scene and GameView windows, and it is very customizable. So what we want to do is create a new C# script in Unity called ItemsEditor, and this time we want to put it in the Editor folder of your project. If you don't have one all you need to do is create a new folder called Editor.

using UnityEngine;

//This is going to be an editor window script, so we need to include UnityEditor
using UnityEditor;

//We are going to be using a generic list to store the item data so we need to include the System.Collections.Generic library
using System.Collections.Generic;

//And since we are going to be saving to a file and will be serializing to XML, we need to include those libraries as well
using System.IO;
using System.Xml.Serialization;

//This is an editor window class so we are going to want to make sure that the class extends from EditorWindow
public class ItemsEditor : EditorWindow
{
//first lets create a string variable that will store the file path of the xml file. Lets make it readonly so we know that no one will mess with it int he code, and make it static so it can be used in static functions
private static readonly string s_filePath = Application.dataPath + "/Resources/Items/XML/";
private static readonly string s_fileName = "items.xml";
//Next we need to create the list that will store all of the item data
private static List<ItemData> s_items;
//Thirdly we need to make another list, but this time with boolean values. We are going to make the editor window collapsable, and we need to know which items are currently being viewed
private static List<bool> s_itemFoldouts;
//Lastly we want our editor window to be scrollable if the list gets somewhat large, so we need to store a scroll position for the editor's scroll bar
private Vector2 m_scrollPosition;
//Our first function is going to be an initalization function called init. Here we will create a new ItemsEditor window, and load the XML file. 
//There is also the MenuItem attribute, and we use this to create a menu item within the Unity editor that we can click on
//Notice that this is also a static function
[MenuItem("Game Data/Item Editor")]
static void init ()
{
EditorWindow.GetWindow<ItemsEditor> ();
loadXMLFile ();
}
//Here we will create a static function called loadXMLFile so that it can be used during initialization
private static void loadXMLFile ()
{
//Here we will make sure to initalize the items and item foldouts list
s_items = new List<ItemData> ();
s_itemFoldouts = new List<bool> ();
//if the file exists we load it, otherwise we dont do anything
if (File.Exists (s_filePath + s_fileName)) {
//lets create our XMLSerializer to handle the item data
XmlSerializer serializer = new XmlSerializer (typeof(List<ItemData>));
//Next we create a new FileStream with our file path. Notice that we have the using statement. 
//This allows us to do whatever we want with the Filestream and know that it will close itself automatically
using (FileStream file = new FileStream(s_filePath + s_fileName, FileMode.Open)) {
try {
//Lets try to load the data into the items list and set up the foldouts list as well
s_items = serializer.Deserialize (file) as List<ItemData>;
s_itemFoldouts = new List<bool> (s_items.Count);
for (int i = 0; i < s_items.Count; i++) {
s_itemFoldouts.Add (false);
}
} catch (System.Exception e) {
// if anything weird happens we will let you know here
Debug.LogError (e.Message);
}
}
}
}
//Next us our function to save the data to the disc
private void saveXMLFile ()
{
//First lets check to see if the directory to the file exists, and if it doesn't we create it
if (!Directory.Exists (s_filePath)) {
Directory.CreateDirectory (s_filePath);
}
//Then we serialize the list of items into the XML file and refresh the AssetDatabase
using (FileStream file = new FileStream(s_filePath + s_fileName,  FileMode.Create)) {
XmlSerializer serializer = new XmlSerializer (typeof(List<ItemData>));
serializer.Serialize (file, s_items);
AssetDatabase.Refresh ();
}
}
//Now we need to display all of the item editing stuff, so we need to create an OnGUI function
private void OnGUI ()
{
//First lets create a button that will save all of the item data
if (GUILayout.Button (new GUIContent ("Save Items List"))) {
saveXMLFile ();
}
//Then lets create a button that will add a brand new ItemData to the list and add a toggle to the foldout list
if (GUILayout.Button (new GUIContent ("Add New Item"))) {
s_items.Add (new ItemData ());
s_itemFoldouts.Add (false);
}
//Next lets create the scroll view so that we can scroll the window if the list gets large
m_scrollPosition = GUILayout.BeginScrollView (m_scrollPosition);
//Now what we want to do is go through each item and if the foldout toggle is set to true, display GUI that will let the user edit that specific item
for (int i = 0; i < s_items.Count; i++) {
//This will let the user change the toggle for the current item
s_itemFoldouts [i] = EditorGUILayout.Foldout (s_itemFoldouts [i], new GUIContent (s_items [i].name + " Info"));
//if the toggle is on we let the user edit the item
if (s_itemFoldouts [i] == true) {
s_items [i].name = EditorGUILayout.TextField (new GUIContent ("Item Name"), s_items [i].name);
s_items [i].description = EditorGUILayout.TextField (new GUIContent ("Item Description"), s_items [i].description);
s_items [i].price = EditorGUILayout.IntField (new GUIContent ("Cost"), s_items [i].price);
//We should also allow the user to remove the selected item so lets give them that option here
if (GUILayout.Button (new GUIContent ("Remove Item"), GUILayout.Width (position.width * 0.2f))) {
s_items.RemoveAt (i);
s_itemFoldouts.RemoveAt (i);
i--;
}
}
}
//Lastly we end the scroll view
GUILayout.EndScrollView ();
}
}

And that is it for the Editor script. This should provide you with a simple but useful tool to edit your item data for your game.

Now that we have the item data class and the item editor class, we need a way to use this data in our game at run time. The next script is a very basic class that will load up your item data and display it in the Unity Console. Now let's create a new C# script called LogCustomData.



using UnityEngine;

//We need to load the XML file, make the xml file readable, and put it into a list so we need to include the libraries to do that
using System.Xml.Serialization;
using System.Collections.Generic;
using System.IO;

public class LogCustomData : MonoBehaviour
{
//The XML file will be loaded as a Text asset since it ends with .xml. We are making it public so that we can set it within the editor if we want to
public TextAsset itemXMLFile;
//And lets make the list that will store all of the items
List<ItemData> m_items;
//Now lets create our function to load all of the items
private void loadItems ()
{
//We create our XMLSerializer like normal
XmlSerializer serializer = new XmlSerializer (typeof(List<ItemData>));
//Here we use a StringReader. This puts the string from the TextAsset into a format that can be deserialized using the XMLSerializer
using (StringReader reader = new StringReader(itemXMLFile.text)) {
//Then we can just assign the items list from here
m_items = serializer.Deserialize (reader) as List<ItemData>;
}
}
void Start ()
{
//If we didn't set the xml file in the editor we will load one from a default position, this is where we saved it in the Editor
if (itemXMLFile == null) {
itemXMLFile = Resources.Load ("Items/XML/items", typeof(TextAsset)) as TextAsset;
}
//Call our loading function
loadItems ();
//Log all of the item data so that we can see that it worked
for (int i = 0; i < m_items.Count; i++) {
Debug.Log (m_items [i].name + " is " + m_items [i].description + " and it costs " + m_items [i].price.ToString ());
}
}
}

And that is it. Now you have a very basic implementation of a class to store data, an editor to manipulate that data, and a script that can load that data for you to use within your game. 

That is all for now! If you enjoyed the post or found it useful hit those share buttons for Facebook, Twitter and Google+!