Tuesday, December 20, 2011

Unity3D EditorWindow Formatting Tip

During the past several months I've done a bit of work extending the Unity3D editor to create custom editor functionality for my upcoming game.  Unity makes it ridiculously easy to implement custom functionality in the editor, however their documentation can leave something to be desired.  One of the hidden nuggets I found is EditorGUIUtility.LookLikeControls() , which exposes custom formatting for certain EditorGUILayout elements.




Here's an example of using EditorGUILayout.EnumPopup() and EditorGUILayout.LabelField() to show some enum and string values.  As you can see the description label on the left is cut off, which makes this GUI very unusable.  EditorGUIUtility.LookLikeControls() lets you change the default pixel size of one or both of the content display fields for these methods, so you can make your GUI's much more readable.



Here's the same menu using EditorGUIUtility.LookLikeControls(250), which expands the first content zone to 250 pixels, and provides a nice amount of space to clearly mark the inputs for the menu.  And as you can see at the bottom, calling EditorGUIUtility.LookLikeControls() with no params will restore the GUI system to the default layout for these elements.

Saturday, September 24, 2011

Handling Texture Tiling and Offset in a Unity3d Vertex and Fragment Shader using TRANSFORM_TEX macro

When doing some test work, I found my Cg shader wasn't handling texture offset and tiling properly, resulting in distortion such as below:

The Unity shader on the left properly handles a 3x tiling factor for the texture, while my shader on the right doesn't properly handle the same 3x tiling factor



My shader setup was pretty simple, here's a snippet of the important parts:

#include "UnityCG.cginc"

sampler2D _MainTex;
float4 _Color;

v2f vert ( appdata_base v )
{
    v2f o; 

    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

    // Texture offset - BAD
    o.uv = v.texcoord;
    return o;
}

half4 frag (v2f i) : COLOR
{    
    return tex2D(_MainTex, i.uv) * _Color;
}

I bolded the problematic part, which is the Texture UV handling in the 'vert' program.  Simply grabbing the v.texcoord will work when there is not tiling or offsets in the texture, but breaks once those elements are introduced

After snooping around, I found the solution is to use the TRANSFORM_TEX macro in the UnityCG.cginc to make sure the texture's offset and tiling are properly applied.  A small caveat to this is you must, "declare float4 properties for each texture before the vertex program, with _ST appended" (Quoted from Unity3d shader docs).

Here's a snippet of my shader with the new changes:

#include "UnityCG.cginc"

sampler2D _MainTex;
float4 _Color;
uniform float4 _MainTex_ST; // Needed for TRANSFORM_TEX(v.texcoord, _MainTex)

v2f vert ( appdata_base v )
{
    v2f o; 

    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

    // Texture offset - GOOD
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    return o;
}

half4 frag (v2f i) : COLOR
{    
    return tex2D(_MainTex, i.uv) * _Color;
}

With this happy little change, our shader now properly handles the tiling and offsets of the texture.
 

For those curious, I gleaned this information from the Unity3D Vertex and Fragment Programs and the ancient Unity 2.x Shader Conversion Guide.  The info pulled from the Shader conversion guide regards the _MainTex_ST property needed for TRANSFORM_TEX to work. 

UPDATE - Dec 1, 2013 - The Unity 2.x Shader Conversion Guide seems to have disappeared from the internet.  This information is all still relevant for Unity 4.x release versions

Sunday, June 5, 2011

Oddness with Unity3D Android's MD5 support

I ran into an odd problem when messing around with generating MD5 hashes in Unity Android recently.

When testing on my PC I was creating an MD5 object by using MD5.Create() and it worked fine. However I then published over to my Android phone to test to make sure it works, good thing I did, because I got a NullReferenceException. Looking at the code, the only thing that jumped out at me was the MD5 object wasn't being created, so I added a check for that condition, and indeed the MD5 object didn't exist. So for some reason MD5.Create() didn't work on the Android hardware.

A little bit of Googling landed me on unifycommunity.com, where I noticed the author, Matthew Wegner, wasn't using the MD5.Create() method but new'ing a MD5CryptoServiceProvider object. Curious, since they both seem to return the same object, I tried out this new method and to my amazement it worked! Horray!

Before I was about to close the book on this and forget MD5.Create() forever, I noticed my project had the Stripping Level set to use micro mscorlib. That turned on a light in my head, so out of curiosity I disabled stripping, re-enabled MD5.Create and re-published to my phone. This time MD5.Create worked.

TL;DR
MD5.Create() doesn't return an object on Unity Android when the Stripping Level is set to Micro mscorlib, but 'new MD5CryptoServiceProvider()' does. When Stripping is disabled MD5.Create() does work as expected. I haven't tested this on the iPhone but the results are probably the same.

Even Short Version:
It's seems safest to 'new MD5CryptoServiceProvider()' whenever you need an MD5 object as it appears to be the most portable.

Thursday, January 13, 2011

Valid, yet scary, code syntax

I recently encountered a scary piece of code syntax which is actually valid, but nevertheless, very confusing. The gist of it was like this (sorry for code formatting):

 if ( myobject == null )  
 {  
   *misc stuff done here*  
   myobject->PrintInfo();  
 }  

For most programmers taking a first look at snippet, it would be a huge red flag of a potential crash waiting to happen. Why the heck would you check for a pointer being null, then call a method on it? However, it ends up this is perfectly valid because the 'PrintInfo()' method looks something like this:

 void PrintInfo ()  
 {  
   if ( this != null )  
   {  
     *do work*  
   }  
 }  

PrintInfo does a null check on the this pointer before accessing any members, which will prevent a crash if the pointer you call the method on is null. This will compile and run without errors, but is sure to drive other programmers up a tree when they see it :D

Friday, November 12, 2010

Dangers of multi-threaded applications

Creating multi-threaded applications can be a scary task (at least for me). The common problems such as deadlocks, race conditions and corrupted memory are pretty intimidating, especially if you're just starting out. One less famous, but still nefarious, problem you can run into in multi-threaded development are continuously spinning threads. I'm sure there's a more common name for these, but essentially they are threads running in a non-stop loop, polling for new stuff to work on. Threads like these run as fast as possible in this loop, and greedily consume as much CPU time as they can; which leads to horrendous performance loss.

We ran into a very bad version of this today on new server software we mass tested for the first time. A lot of people complained about being unable to connect to the server, move through our game levels, or interact with items in the levels. I confirmed the server processes were running at 100% CPU, so we started looking at where our performance was going. What made matters frustrating was on our (beefy) dev machines, the servers were only running at about 8-10% usage. We spent a little time looking at some game engine profile output, and looking at some asset issues we thought were the culprit. In the end I wish I could say we found the problem through some tool or cool debug technique; but rather it came about when, on a whim, I asked another developer about some threaded functionality he recently added. I asked him to check his code to make sure his new thread was yielding if there was nothing for it to do, and voila, we found the problem.

It ended up he originally built the threaded task with some blocking functionality which had controlled the thread CPU usage (unintentionally), but later changed it to use non-blocking calls. After this change he forgot to add a thread sleep call, so the thread was simply spinning in a loop looking for something to do. While he fixed the code, I figured out how to setup a test case for our dev machines to reproduce the problem of 100% CPU usage on the servers. I remembered we could set the processor affinity through the task manager, which would allow us to simulate a single-core machine to run the game server on. Once I did this, I was able to see the game server spiking up to 100% CPU usage on the assigned core. Next I integrated our fix, which was to simply sleep the thread for 10ms every loop, and re-ran my test. The server dropped from 100% CPU usage to between 0 and 1% while idling!

Overall, it was a good day today, long as hell though.