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.