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+!

Friday, October 18, 2013

Stuck? Maybe It's Time To Redesign

One of the most important things you can do as a developer, is have people play your game as soon as possible. Generating feedback is crucial to making sure that what you are doing isn't a waste of your time and money, and it allows you to catch bugs and design flaws early on.

Most importantly, it also allows you to see whether or not your game is even fun to play. One of the worst things you could do is finish your game only to realize that absolutely no one else likes it, or even worse YOU don't even like it. If that is the case, and you have discovered part way through development that your game isn't fun, what do you do? 

Do you start from scratch? I wouldn't say starting completely from scratch is a good idea, unless the basic concept of the game doesn't make sense, or if there is absolutely nothing that can be salvaged from your game. In that case, you may not have a choice.

Do you start adding things to see if it is fun? No. Adding things to make it fun is going to lead you down a long road of disappointment. Your game needs to be fun at its core, not at the tacky addons that you tossed in there to zazz it up. Adding things in to make it fun will only drain you of your time and money, and in the end you will probably end up in the exact same position you started with.

Do you abandon all hopes? Never, unless you would rather change hobbies/jobs and become the cat food specialist you always dreamed of.

Recently at LoneWolfPack Interactive, we came across this exact issue with our current project, formerly known as Sudoku Rush. The other day we were playing the game, talked with each other, and decided that we needed a complete redesign. There was absolutely nothing redeeming about our current idea, and we needed to overhaul the entire game. 

Sudoku Rush was our take on the action puzzle genre. We wanted to make Sudoku more exciting by adding a base defence element to it, which sounded like a good idea at the time. Unfortunately, it didn't turn out that way.

There were good things about it though. Since Sudoku has been gaining popularity over the years, having Sudoku attached to the game would have made it come up on search results fairly often on the store, so it would have gotten easy exposure. It was also very different, and since the market is getting saturated with very similar titles, it was nice to see and to work on something new. It was also an easy concept to get the hang of. Filling in the Sudoku board gave you money that you could use to spawn units.

There were a lot of large problems with it. For one, the game just wasn't fun at all. There was no 'this is it' moment, where we knew we had something good. There wasn't enough player control, if you sucked at Sudoku, you stood no chance, and if you had enemies right at your base you needed to solve a ton of numbers on the puzzle to spawn units. In general the game was overwhelming as well. Solving Sudoku puzzles, spawning units and controlling them, and keeping track of what was on the field was just too much for the player. Finally there was no real direction for the game.

So seeing all of the issues, we decided to come up with a completely different direction for the game. We stripped Sudoku right out of the game, and changed how the base defence works. Since we wanted to give the player more control during the game, we decided to give the player hero units to control. With this set up there is a lot more action, and having hero characters provides another avenue for future content. The game is also set up in a similar fashion to games like League of Legends or AirMech, so there is that appeal as well.

We are excited with the new idea, and it will definitely be more fun and more appealing to players. There are a few new small problems, but they are much more manageable, and they are not problems with design, but they are just problems with changing the game at the last minute. The scope of the game is much larger than the old version. Giving the player more control over the hero units, allowing the player to upgrade these units and keeping everything balanced is going to be much more work. On top of that the AI needs a lot of work. The AI needs to transition from figuring out which units to spawn to controlling it's own hero unit and doing things intelligently. It will be interesting to implement.

Overall though, we are happy with the new direction. It will be much more fun to play and to build, and it shows you that you shouldn't be afraid to try new things, even after you've invested time into them. 

Friday, October 11, 2013

Using Protobuf In Unity3D

Recently in our projects at LoneWolfPack Interactive, we looked for faster and more efficient ways to store data. In the past we had fiddled with XML and .Net's binary formatter, but so far we weren't very satisfied.

XML is  nice. You can store practically any data, and then edit it afterwards once the file is stored. The problem with it is that the readability of the files can make it extremely bloated with certain data sets.. You could go from small manageable files to massive uncontrollable files depending on the amount of data you need to store. The speed of serialization and deserialization in the XMLSerializer is also fairly slow, so you can get small chugs here and there when you need to use it.

Binary formatter was the temporary solution. It stored small files and was relatively quick. There are a few problems with the binary formatter though, such as the lack of flexibility. If you save data using the binary formatter and then change the saved class, you come across issues and you have to scrap the data that was there. The other problem is that binary formatter isn't supported on iOS. Binary formatter requires the ability to JIT compile (Just In Time), where Apple requires AOT compilation (Ahead Of Time).

Looking for another solution I hit the net, and came across protobuf-net which was developed by Marc Gravell of Stack Overflow. If you are a geek and like stats you can look up the performance info here. Basically protobuf solved all of the issues that we were having with binary formatter. It was even faster than binary formatter, it was flexible, it was supported on iOS and we could change around our classes all we wanted and it wouldn't matter, things would still load.


There is a little bit of set up with protobuf though, and for that we are going to use Visual Studio. The only reason I am using Visual Studio here instead of MonoDevelop is because VS handles a few things automatically for us and the whole process is quicker. Plus I like Visual Studio, and I miss using it. If you don't have Visual Studio, you can grab it here. In this tutorial I am using VS 2010, but everything should be similar if you grab VS express 2012 .

The first thing you want to do is download protobuf from Marc's Google Code site. As of the date of this post, the version is r668, but unless Marc goes crazy and restructures everything, future versions should work with this tutorial as well.

Once the zip file containing protobuf is downloaded, you are going to want to grab the protobuf-net.dll file located under Full/unity/. and put it somewhere where you will remember so that you can reference it in future projects.



Once we have the dll stored on our computer in an accessible place, let's open Visual Studio, and create a brand new Class Library project. This project is used for creating a dll, and this will allow us to access the classes and structures we want to save within our Unity project. So since we are saving game data, let's call this project GameSaveData.


Since we are using this in Unity, we want to set a few things up beforehand. The first thing we want to do is make sure that the project is compiled under .Net Framework 2. In order to do this, in the menu go to Project -> GameSaveData properties, and set the target framework to 2.0. You will get a warning saying that Visual Studio needs to reopen to make the changes. Hit yes and you can continue on.


Next we want to reference protobuf in our project. In order to do this, right click on your references folder in the Solution Explorer and hit Add Reference. From here you can browse to where you stored the protobuf and select it.


Now that our project is set up, here is a sample version of what your GameSaveData might look like. This sample uses a basic item class, a few enums and a character customization class:


using System;

//we need to use specific attributes so that ProtoBuf knows what references what, so we add a using statement for the ProtoBuf namespace.
using ProtoBuf;

namespace GameSaveData
{
//when creating a class use the ProtoContract attribute. While not completely necessary if you look through the documentation you can see that these attributes can control what goes on when things are serialized and deserialized
    [ProtoContract]
    public class Item
    {
//Protobuf also needs to associate data with keys so that it can identify data on serialization and deserialization. We do this with the ProtoMember attribute, and in that attribute be assign an integer value
        [ProtoMember(1)]
        public string name;
        [ProtoMember(2)]
        public int price;
        [ProtoMember(3)]
        public EntityType applicableEntities;
        [ProtoMember(4)]
        public ItemType itemType;

//then we can just make the class as normal
        public Item()
        {
            this.name = "Item";
            this.price = 10;
            this.applicableEntities = EntityType.ALL;
            this.itemType = ItemType.Hat;
        }

        public Item(string name, int price,EntityType applicableEntities, ItemType currentItemType)
        {
            this.name = name;
            this.price = price;
            this.applicableEntities = applicableEntities;
            this.itemType = currentItemType;
        }
    }

    [ProtoContract]
    public enum EntityType
    {
        NONE = 0,
        ROCK = 1,
        PAPER = 1 << 1,
        SCISSORS = 1 << 2,
        NEUTRAL = 1 << 3,
        ALL = ROCK | PAPER | SCISSORS | NEUTRAL
    }

    [ProtoContract]
    public enum ItemType
    {
        Hat = 0,
        Cape = 1,
        Weapon = 2,
        Shoe = 3,
        Arm_Armour = 4,
        Leg_Armour = 5,
        Shirt = 6,
    }

    [ProtoContract]
    public class CharacterCustomizationData
    {
        [ProtoMember(1)]
        public Item hat;
        [ProtoMember(2)]
        public Item cape;
        [ProtoMember(3)]
        public Item shoes;
        [ProtoMember(4)]
        public Item weapon;
        [ProtoMember(5)]
        public Item armArmour;
        [ProtoMember(6)]
        public Item legArmour;
        [ProtoMember(7)]
        public Item shirt;

        public CharacterCustomizationData()
        {
            this.hat = this.cape = this.shoes = this.weapon = this.armArmour = this.legArmour = this.shirt = null;
        }
    }

//just a very basic trophy implementation for those who would put trophies in their game and need a way to store the data
    [ProtoContract]
    public class Trophy
    {
        [ProtoMember(1)]
        public string trophyName;
        [ProtoMember(2)]
        public string trophyDescription;

        public Trophy()
        {
            trophyName = "Trophy1";
            trophyDescription = "Description";
        }

        public Trophy(string trophyName, string trophyDescription)
        {
            this.trophyName = trophyName;
            this.trophyDescription = trophyDescription;
        }
    }
}

From here, just build the project and you are done with GameSaveData.

Now, if you are deploying on a platform that handles JIT compilation, this is all you need. From here you can go to the bin folder in your GameSaveData project and drag the GameSaveData.dll and Protobut-net.dll into your Unity Project under the plugins folder.


From here, using Protobuf in your project is really simple. If you are trying to read data from a while:

using (FileStream f = new FileStream("testpath", FileMode.OpenOrCreate))
{
     mySerializedObject = ProtoBuf.Serializer.Deserialize<ObjectType>(f);
}

and if you are saving to a file:

using (FileStream f = new FileStream("testpath", FileMode.OpenOrCreate))
{
  ProtoBuf.Serializer.Serialize<ObjectType>(f, mySerializedObject);
}


Now, if you are deploying to a platform that does not support JIT, you have a little bit more work to do. In order for you do use protobuf in an AOT environment, you need to pre-compile the serializer that will be handling all of your data. Thankfully this is a very simple process, and takes about the same time to complete as our first Visual Studio project.

So what we want to do is create another new Visual Studio project, and this time it is going to be a console application. We will want to call this SaveDataSerializer, since that is what we are serializing.


We want to set the project up very similarly to our first one, so once again we are going to change the target framework to .Net 2.0 in  Project -> SaveDataSerializer properties.

We also want to add references to the project again, but this time, we want to add references to the GameSaveData.dll and protobuf-net.dll that were compiled in our last project. They should be in the bin/Debug or bin/Release folder, depending on the settings that you had the project in.


Now that our project is set up, we can create our program that will compile our serializer for us. Using the sample, our serializer code would look something like this:

using System;

//we need to reference the content of our old project so that we can add the data to the serializer
using GameSaveData;
//we need to reference the ProtoBuf.Meta library so that we can create a TypeModel that will tell protobuf exactly how the data is serialized
using ProtoBuf.Meta;

namespace GameDataSerializer
{
    class Program
    {
        static void Main(string[] args)
        {
            var model = TypeModel.Create();

            model.Add(typeof(EntityType), true);
            model.Add(typeof(ItemType), true);
            model.Add(typeof(Item), true);
            model.Add(typeof(CharacterCustomizationData), true);
            model.Add(typeof(Trophy), true);


            model.AllowParseableTypes = true;
            model.AutoAddMissingTypes = true;

            model.Compile("GameDataSerializer", "GameDataSerializer.dll");
        }
    }
}

From here, you can use Protobuf in any of your projects, whether it is iOS, Android, PC, whichever. If you need to modify your classes, just open up your Visual Studio projects, make the modifications and go through the tutorial again. That is the nice thing about Protobuf, is that it is so easy to manipulate and change, and all of your data is safe.

Now normally I would say that I am done, but there is one more thing I want to show you, and that is a wrapper class that I created for protobuf. This class allows you to use protobuf anywhere in your project, is much more manageable, and much more convenient to use. So what I want you to do is create a new c# script in your Unity project, and call it ProtoLoader.

using UnityEngine;
using System.IO;

//this is a static class. We create this because we dont need an instance of it to make anything work, everything works independently.
public static class ProtoLoader
{
//We need to make a reference to the GameDataSerializer that wil be handling all of the data
  private static GameDataSerializer m_serializer = new GameDataSerializer();

//our first function will load an object from resources. This is useful for if you had an editor script that created data that would be stored for later usage in the game, and you had saved it with the .bytes extension
  public static T loadObjectFromResources<T>(string resourcePath)
  {
     TextAsset objectAsset = Resources.Load(resourcePath, typeof(TextAsset)) as TextAsset;
     if(objectAsset == null)
     {
        return default(T);
      }

     T deserializedObject = default(T);

//basically we just load the bytes of the text asset into a memory stream, and the deserialize it from there
     using(MemoryStream m = new MemoryStream(objectAsset.bytes))
     {
        deserializedObject = (T)m_serializer.Deserialize(m, null, typeof(T));
     }

     return deserializedObject;
  }

//our next function is used for loading an object from a path. Basically for when you are loading progress and other sorts of stuffs
  public static T loadObjectFromPath<T>(string path)
  {
//if the file doesn't exist we just return null. Since we are using a templated function the best we can do is default(T)
     if(!File.Exists(path))
     {
        return default(T);
     }

     T deserializedObject = default(T);

     using(FileStream f = new FileStream(path, FileMode.Open))
     {
        deserializedObject = (T)m_serializer.Deserialize(f, null, typeof(T));
     }

     return deserializedObject;
  }

//Our third function will save the object to a file path. This is basically for saving game options, progress, whatever you can think of that you would need saved
  public static void saveObjectToPath<T>(string objPath, string fileName, T serializedObject)
  {
     if(!Directory.Exists(objPath))
     {
        Directory.CreateDirectory(objPath);
     }

//after checking if the directory is there we serialize the object into a filestream to save it.

     using(FileStream f = new FileStream(objPath + fileName, FileMode.OpenOrCreate))
     {
        m_serializer.Serialize(f, serializedObject);
     }
  }

//This function is actually useful for sending objects over an RPC function. Unity allows you to send a byte[]  through an RPC even though its not documented, so here you can serialize object data, send it over the network and then deserialize the object on the other end.
  public static byte[] serializeProtoObject<T>(T obj)
  {
     using(MemoryStream m = new MemoryStream())
     {
        m_serializer.Serialize(m, obj);
        return m.ToArray();
     }
  }

//this is the opposite of the previous function. It allows you to send in a byte[] and return the object that it originally was. Very useful for networking
  public static T deserializeProtoObject<T>(byte[] bytes)
  {
     using(MemoryStream m = new MemoryStream(bytes))
     {
        return (T)m_serializer.Deserialize(m, null, typeof(T));
     }
  }
}

And that is it! You now have fast, efficient and easy to use serialization for your project!

Overall we are really happy with how well protobuf works, and it is definitely something for you to look into for data serialization. Now for those of you who may have had an issue here or there, or just want the projects, I have included a link here to a zip file that has both of the visual studio projects, and a unity package that has all of the classes and dlls created in this project, at your convenience, because I am super nice and awesome like that. LINK

Remember to share if you liked it, whether its on Twitter, Facebook, Google+ or to your technically savvy grandma!

Friday, October 4, 2013

Exploring Unity With Reflection And The Assembly Browser

Reflection is a very powerful tool in programming. It allows you to access classes, methods and variables that you wouldn't normally have access to, and gives you an unlimited amount of control over your projects, and the libraries that you use.

 In Unity, there are a large number of classes and methods that are private or internal. Most APIs and tools do the same, and for most people, this is perfectly fine. Not everyone is going to need an extremely specific function that can only be found in an internal class, and only a few people are going to need access to specific internal classes while creating editor tools. Personally, I like to explore, see what things are capable of, and find hidden gems that are throughout the libraries that I am using.

If you are developing with MonoDevelop, one of the extremely nice features is comes with is the assembly browser. In the assembly browser you can look through all of the dlls that your project is referencing, and find out which classes do what, and how exactly they do them. Taking some time to explore through the assembly browser can help give you a better understanding of how Unity does its thing, and how you can approach certain situations. Another option you can take is finding a .Net decompiler, but I'm pretty sure that is against the EULA.

When looking through the assembly browser normally, the only thing you are going to see is the public classes that you already have access to. You can look through all of their functions, and even see a few protected variables and functions that you might not have known about. For the most part those are just helper functions that don't have much of a purpose for the average user.

There is also another option for view available. At the top of the assembly browser you will see a visibility property, and it will default at Only Public Members. If you set it to All Members, suddenly you will see a massive list of new classes appear that you never would have known about beforehand. You can take a look at all of the inspectors for classes like Transforms and Cameras. You can take a look at the Asset Store Window class, or take a look at how all of the animation components work. Just about everything is there for you. The only thing you don't have access to is the C++ code that some of the functions make a reference to.

There are quite a few internal classes to look through. It will take quite a few hours to go through it all.


One of the useful finds I made while looking through the assembly browser was the GameView class. This is the class that represents the game view, or the view you see when you hit play within the editor. While I was working on LGUI, I came across the issue of not having the correct aspect ratio while positioning the 2D objects within the editor. The Screen class was very unreliable, and half of the time I would get a very random aspect ratio that didn't match up at all, or I would get a value of something like 0. 

When I discovered this GameView class, I noticed a function that would become extremely handy to me, called GetSizeOfMainGameView. As you can tell from the name it returns the size of the main game view, which was exactly what I needed when sizing my 2D GUI. Since this was an internal method, reflection came to the rescue. This is what I ended up with:


    public static Vector2 getMainGameViewSize ()
    {
        //The first thing I needed to do was get the GameView type. Since this was an internal type, I needed to make a special call using Type.GetType, which passes the class and the assembly that the class belongs to. The GameView class is within the UnityEditor assembly
        Type gameViewType = Type.GetType ("UnityEditor.GameView,UnityEditor");

        //Next I needed to get the MethodInfo of the GetSizeOfMainGameView function. MethodInfo holds all of the information about a function, including parameters, return types, and attributes. Also since GetSizeOfMainGameView is a static internal function, I needed to pass the NonPublic and Static binding flags so that the code knew where to look
        MethodInfo getSizeOfGameView = gameViewType.GetMethod ("GetSizeOfMainGameView", BindingFlags.NonPublic | BindingFlags.Static);

       //Now that I had the MethodInfo I wanted, I could invoke the function. The Invoke call has two parameters, the first is for the object that the function is being called on, and the second is for the parameters. Since this was a static function, I pass null for the first parameter, and since the function has no parameters, I pass null again for the second parameter
        return (Vector2)getSizeOfGameView.Invoke (null, null);
    }


Now if that function is used in an editor class, it will return a Vector2 that has the width and height of the main game view window. Pretty handy!

Another option that is available with reflection is adding to a pre-existing inspector. The example I am going to show is the Transform inspector. The inspector works the exact same as the original one, but now you can add things and remove things from it, and customize it however you like! Most of this was copied and pasted from the assembly browser, but there were a few instances where I needed reflection to accomplish what the old editor did. 

First create a folder in your project called Editor. Within that folder, create a new C# file called NewTransformInspector, and add this code to it.


using System;
using UnityEngine;
using System.Reflection;
using UnityEditor;

[CustomEditor (typeof(Transform)), CanEditMultipleObjects ]
internal class NewTransformInspector : Editor
{
    private Vector3 m_EulerAngles;
    private Quaternion m_OldQuaternion = new Quaternion (1234f, 1000f, 4321f, -1000f);
    private SerializedProperty m_Position;
    private SerializedProperty m_Scale;
    private SerializedProperty m_Rotation;

    //We need to grab some MethodInfo. There were a few calls that were internal
    MethodInfo tempContentInfo;
    MethodInfo sendScaleMethodInfo;

    public void OnEnable ()
    {
        //There is a few references to a temporary GUIContent object that are not visible to the public, so we get the method for that here. This function is internal and static, so we use the NonPublic and Static binding flags
        tempContentInfo = typeof(EditorGUIUtility).GetMethod ("TextContent", BindingFlags.NonPublic | BindingFlags.Static);

        //There is also an internal call to scale and modify the transform, so we grab that here. This function is used on a specific Transform, so we need to use the Instance binding flag.
        sendScaleMethodInfo = typeof(Transform).GetMethod ("SendTransformChangedScale", BindingFlags.NonPublic | BindingFlags.Instance);
        //Here we just grab the position and scale properties of this transform
        this.m_Position = base.serializedObject.FindProperty ("m_LocalPosition");
        this.m_Scale = base.serializedObject.FindProperty ("m_LocalScale");
    }

    public override void OnInspectorGUI ()
    {
        Transform transform = base.target as Transform;
        base.serializedObject.Update ();
        this.Inspector3D ();
        Vector3 position = transform.position;

        //Floats only have a certain amount of precision, so we throw a warning in here if the number gets too big
        if (Mathf.Abs (position.x) > 100000f || Mathf.Abs (position.y) > 100000f || Mathf.Abs (position.z) > 100000f) {
            EditorGUILayout.HelpBox ("Due to floating-point precision limitations, it is recommended to bring the world coordinates of the GameObject within a smaller range.", MessageType.Warning);
        }
        EditorGUI.showMixedValue = false;
        base.serializedObject.ApplyModifiedProperties ();
    }

    private void Inspector3D ()
    {
        Transform transform = base.target as Transform;
        GUILayout.Space (3f);
        //This uses the call to the temporary GUIContent object. Basically the function takes a string and returns a GUIContent object with that string.
        EditorGUILayout.PropertyField (this.m_Position, (GUIContent)tempContentInfo.Invoke (null, new object[]{"BRAND NEW POSITION" }), new GUILayoutOption[0]);
        Quaternion localRotation = transform.localRotation;
        if (this.m_OldQuaternion.x != localRotation.x || this.m_OldQuaternion.y != localRotation.y || this.m_OldQuaternion.z != localRotation.z || this.m_OldQuaternion.w != localRotation.w) {
            this.m_EulerAngles = transform.localEulerAngles;
            this.m_OldQuaternion = localRotation;
        }
        bool flag = false;
        UnityEngine.Object[] targets = base.targets;
        for (int i = 0; i < targets.Length; i++) {
            Transform transform2 = (Transform)targets [i];
            flag |= (transform2.localEulerAngles != this.m_EulerAngles);
        }
        EditorGUI.showMixedValue = flag;
        EditorGUI.BeginChangeCheck ();
        this.m_EulerAngles = EditorGUILayout.Vector3Field ("BRAND NEW ROTATION", this.m_EulerAngles, new GUILayoutOption[0]);
        if (EditorGUI.EndChangeCheck ()) {
            Undo.RegisterUndo (base.targets, "Inspector");
            UnityEngine.Object[] targets2 = base.targets;
            for (int j = 0; j < targets2.Length; j++) {
                Transform transform3 = (Transform)targets2 [j];
                transform3.localEulerAngles = this.m_EulerAngles;
                if (transform3.parent != null) {
                    //Here is our other reflection call. Basically we check to see if something changed in the transform and if it did we tell the engine to make the change
                    sendScaleMethodInfo.Invoke (transform3, null);
                }
            }
        base.serializedObject.SetIsDifferentCacheDirty ();
        this.m_OldQuaternion = localRotation;
        }
    EditorGUILayout.PropertyField (this.m_Scale, (GUIContent)tempContentInfo.Invoke (null, new object[]{"BRAND NEW SCALE" }), new GUILayoutOption[0]);
    GUILayout.Space (1f);
}


And that is your new transform inspector! Now when you select an object you will see the regular transform, but with new property labels, and since you have the inspector right in your project you can now add to it, and customize the functionality of it to fit your needs.


Well, that's all for this week. Hopefully you guys learned something about reflection, or at least gained some interest in it, and what it is capable of. Hope you guys enjoyed it, until next week!