Unity3D for iOS: Relative Positioning with UIToolkit

Here’s one way to horizontally center a UIToolkit object, such as a UIButton, using positionCenterX(). Should work pretty good on the various iPad sizes, iPhone 4, iPhone 5. The one thing to keep in mind is the scale of the actual graphic will need to be adjusted, either via UIToolkit’s UI.cs class that listens for HD, SD or supper HD screen sizes or manually via your own code.

Javascript version

#pragma strict
public var buttonToolkit:UIToolkit;
private var newGameBtn:UIButton;

function Start() {
    newGameBtn = UIButton.create( buttonToolkit, "playBtnOff.png", "playBtnOn.png", 0, 0 );	
	newGameBtn.zIndex = 0;

	//centers it horizontally on iPad and iPhone: 
	newGameBtn.positionCenterX();
	//relatively positions it along y axis; slightly differnt actual position on iPads, iPhone 4, iPhone 5 
	newGameBtn.position.y = UIRelative.yPercentFrom( UIyAnchor.Bottom, 1.25f );	

	newGameBtn.highlightedTouchOffsets = new UIEdgeOffsets( 10 );	
	newGameBtn.onTouchUpInside += newGameBtnHandler;	
	newGameBtn.alphaFrom( 1.0f, 0.0f, Easing.Quintic.easeOut );
}

function newGameBtnHandler( sender:UIButton ) 
{
	Debug.Log("newGameBtn clicked!");
}

C# version

using UnityEngine;
using System;
using Prime31;

public class ExampleManagerScript : MonoBehaviour {

	//set via Inspector panel
	public UIToolkit buttonToolkit; 

    private UIButton newGameBtn;

	void Start() {	

		newGameBtn = UIButton.create( buttonToolkit, "playBtnOff.png", "playBtnOn.png", 0, 0 );	
		newGameBtn.zIndex = 0;

		//centers it horizontally on iPad and iPhone: 
		newGameBtn.positionCenterX();
		//relatively positions it along y axis; slightly differnt actual position on iPads, iPhone 4, iPhone 5 
		newGameBtn.position.y = UIRelative.yPercentFrom( UIyAnchor.Bottom, 1.25f );	

		newGameBtn.highlightedTouchOffsets = new UIEdgeOffsets( 10 );	
		newGameBtn.onTouchUpInside += newGameBtnHandler;	
		newGameBtn.alphaFrom( 1.0f, 0.0f, Easing.Quintic.easeOut );
	}

	void newGameBtnHandler( UIButton sender ) 
	{
		Debug.Log("newGameBtn clicked!");
	}
}

Unity3D for Android: Using a Native Java Plugin to Grab the Android Device ID

The java code returns an MEID (Android Device ID) on CDMA phones (Verizon, Sprint), an IMEI on GSM phones (AT&T, T Mobile) and an Android ID on WIFI-only devices (no cellular network).

This code is based on Android Java Plugin for Unity example project in the Unity manual.

Why bother with a plugin?

I tried using Unity 3.5’s built-in SystemInfo.deviceUniqueIdentifier getter for obtaining a unique device ID but it didn’t meet our ad network’s requirements.

The set up in Unity 3.5

  • Drag the AndroidJava.jar file inside /Assets/Plugins/Android/. /Assets/Plugins/Android/bin/ worked too. This .jar file is created via Eclipse or command line javac based on the Java source code
  • Drag the example libjni.so file inside /Assets/Plugins/Android/. Make sure it’s inside /Plugins/Android or you’ll get a “DllNotFoundException: jni” error when you test on device with adb.
  • Drag JNI.cs into /Assets/Plugins/
  • Drag JavaVM.cs into /Assets/Plugins/
  • Make sure your project has an AndroidManifest.xml file inside /Assets/Plugins/Android/ with android.permission.READ_PHONE_STATE permission set. android.permission.READ_PHONE_STATE is needed to access an Android phone/tablet’s Android ID, IMEI or MEID. If you’re making HTTP calls you’ll also need additional permissions, etc.

Code that worked:

...
	private static IntPtr	JavaClass; 
	private static int		GetDeviceId;
...

	///if you're calling the plugin from a static method, you still have to make sure an instance of this script is attached to 
	///an empty GameObject in Hierarchy so Start() method runs & the below code initializes:	
	void Start() {
		
		#if UNITY_ANDROID 		
		
			// attach our thread to the java vm; obviously the main thread is already attached but this is good practice..
			JavaVM.AttachCurrentThread();
			
			// first we try to find our main activity..
			IntPtr cls_Activity		= JNI.FindClass("com/unity3d/player/UnityPlayer");
			int fid_Activity		= JNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
			IntPtr obj_Activity		= JNI.GetStaticObjectField(cls_Activity, fid_Activity);
			Debug.Log("obj_Activity = " + obj_Activity);		
			
			// create a JavaClass object...
               //"com/yourCompany/yourProjectName" should be your Bundle ID in Unity's Player Settings & should be the package name in your Java class
			IntPtr cls_JavaClass	= JNI.FindClass("com/yourCompany/yourProjectName/JavaClass"); 	
			int mid_JavaClass		= JNI.GetMethodID(cls_JavaClass, "<init>", "(Landroid/app/Activity;)V");
			IntPtr obj_JavaClass	= JNI.NewObject(cls_JavaClass, mid_JavaClass, obj_Activity);
			Debug.Log("java Helper object = " + obj_JavaClass);		
			
			// create a global reference to the JavaClass object and fetch method id(s)..
			JavaClass					= JNI.NewGlobalRef(obj_JavaClass);
			GetDeviceId					= JNI.GetMethodID(cls_JavaClass, "GetDeviceId", "()Ljava/lang/String;");
			Debug.Log("JavaClass global ref = " + JavaClass);
			Debug.Log("JavaClass GetDeviceId method id = " + GetDeviceId);						
		
		#endif			
	}	
...

	/// <summary>
	/// Gets the unique device for Android devices (IMEI, MEID or Android ID).
	/// </summary>
	/// <returns>
	/// Returns the id as a string.
	/// </returns>
	private string GetTheDeviceID()
	{		
		//didn't work... supposed to return IMEI but didn't 
		//return SystemInfo.deviceUniqueIdentifier;
		
		String dID = "";
		
		#if UNITY_ANDROID 
	
			// again, make sure the thread is attached..
			JavaVM.AttachCurrentThread();
						
			// get the Java String object from the Helper java object
			IntPtr str_cacheDir 	= JNI.CallObjectMethod(JavaClass, GetDeviceId);
			Debug.Log("str_cacheDir = " + str_cacheDir);		
			
			// convert the Java String into a Mono string
			IntPtr stringPtr = JNI.GetStringUTFChars(str_cacheDir, 0);
			Debug.Log("stringPtr = " +stringPtr);
			dID = Marshal.PtrToStringAnsi(stringPtr);
			JNI.ReleaseStringUTFChars(str_cacheDir, stringPtr); 		
					
		#endif

		Debug.Log(" Device ID value is = " + dID);
		
		return devID;		
	}		

/// call GetTheDeviceID() when you need to use it
...

The Java Code

The below code is based on requirements by ad networks that need to use a specific device ID for metrics like conversion tracking. It’s the main source file for generating the AndroidJava.jar you need to import into Unity’s Plugins folder.

The Device ID & hashing methods implementations come directly from Millennial Media‘s public wiki.

package com.yourCompany.yourProjectName;

import android.app.Activity;
import android.content.Context;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class JavaClass
{
	private Activity mActivity;
	
	public JavaClass(Activity currentActivity)
	{
		Log.i("JavaClass", "Constructor called with currentActivity = " + currentActivity);
		mActivity = currentActivity;
	}
	
	public String GetDeviceId()
	{		
		// Get the device ID
        String auid = android.provider.Settings.Secure.ANDROID_ID + "android_id";
        
        Context context = mActivity.getApplicationContext();
        
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        if(tm != null)
        {
             try{
             auid = tm.getDeviceId();
             }
             catch (SecurityException e){
                  e.printStackTrace();
             }
             tm = null;
        }
        if(((auid == null) || (auid.length() == 0)) && (context != null))
             auid = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
        if((auid == null) || (auid.length() == 0))
             auid = null;
        
        return auid;	
	}
	
	public String GetHashedDeviceIdSHA1()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputSHA1(deviceId);
		
		return hashedDeviceId;
	}
	
	public String GetHashedDeviceIdMD5()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputMD5(deviceId);
		
		return hashedDeviceId;
	}

	
	/**
     * <p>
     * Hashes the given plain text using the MD5 algorithm.
     * </p>
     * 
     * <p>
     * Example:
     * </p>
     * 
     * <p>
     * 098f6bcd4621d373cade4e832627b4f6
     * </p>
     * 
     * @param input
     *            The raw input which must be hashed.
     * @return A String representing the MD5 hashed output.
     */
    public static String hashInputMD5(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("MD5");
                  
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
                 
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
                    
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                         
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
         
        return rv;
    }
    
    /**
     * <p>
     * Hashes the given plain text using the SHA-1 algorithm.
     * </p>
     *
     * <p>
     * Example:
     * </p>
     *
     * <p>
     * a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
     * </p>
     *
     * @param userId
     *            The raw input which must be hashed.
     * @return A String representing the SHA-1 hashed input.
     */
    public static String hashInputSHA1(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("SHA1");
             
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
              
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
              
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                 
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
             
        return rv;
    }
}

AndroidManifest.xml

If you don’t specify the correct permissions for your app and try to do things like make an HTTP request or grab the Device ID, you’ll get an error, like this:

10-02 15:06:49.223  7776  7784 W System.err: java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10109 nor current process has android.permission.READ_PHONE_STATE.
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1322)
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1276)
10-02 15:06:49.223  7776  7784 W System.err: 	at com.android.internal.telephony.IPhoneSubInfo$Stub$Proxy.getDeviceId(IPhoneSubInfo.java:150)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java:216)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.yourCompany.yourProjectName.JavaClass.GetDeviceId(JavaClass.java:44)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)

I grabbed the default one from inside Unity.app on my Mac:

/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidDevelopmentPlayer/AndroidManifest.xml

Kept all as is, just added my permissions and it worked:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
	android:installLocation="preferExternal"
    android:versionCode="1"
    android:versionName="1.0">
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <application
		android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:debuggable="true">

        //...

    </application>

	
	<!-- PERMISSIONS -->
		<uses-permission android:name="android.permission.INTERNET"/>
		<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
		<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
	
</manifest>

Some Other Possible Errors

Here’s a fun one, based on a dumb mistake I made. I put the libjni.so into the wrong folder, /Assets/Plugins/, instead of /Assets/Plugins/Android/:

10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.155  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.163  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.179  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.186  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.897  1410  1410 I wpa_supplicant: WPS-AP-AVAILABLE 
10-02 14:43:17.905  1320  1411 V WifiMonitor: Event [WPS-AP-AVAILABLE ]
10-02 14:43:17.905  1320  1411 D WifiMonitor: WPS Event: WPS-AP-AVAILABLE 
10-02 14:43:18.264  1391  1391 D RadioSignalLevel: evdo dbmLevel: 4, snrLevel: 3
10-02 14:43:18.522  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:18.522  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:18.522  7587  7596 I Unity   :   at MyUnityDeviceIdScript.Start () [0x00000] in <filename unknown>:0 
10-02 14:43:18.522  7587  7596 I Unity   :  
10-02 14:43:18.522  7587  7596 I Unity   : (Filename:  Line: -1)
10-02 14:43:18.522  7587  7596 I Unity   : 
10-02 14:43:21.561  7587  7596 I Unity   : checkDeviceID() called
10-02 14:43:21.561  7587  7596 I Unity   :  
10-02 14:43:21.561  7587  7596 I Unity   : (Filename: ./Runtime/ExportGenerated/AndroidManaged/UnityEngineDebug.cpp Line: 43)
10-02 14:43:21.561  7587  7596 I Unity   : 
10-02 14:43:21.671  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.694  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:21.694  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.GetUniqueDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.callTheUrlWithDeviceIDParam () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at GameManager.checkDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :  

As far as I’ve seen, this code only works on a device. If you try to test inside Unity, you’ll get a similar error:

DllNotFoundException: jni
MyUnityDeviceIdScript.Start () (at Assets/Scripts/MyUnityDeviceIdScript.cs:21)

Once you test on a device, if all else if good, this error should go away.

NOTE: looks like this code requires some time to load up the JNI .so file. Not absolutely sure yet, but it appears if I have a fairly complex project and I try to call a JNI related method very soon after app loading, the app crashes with weird mystery output in adb logcat. Need to look into this more to be sure about the details.

Unity3D for iOS: Storing a New High Score in Parse via UniParse while Adding a Value to a _ptr Field

So far this is the only way I’ve been able to successfully do this. I wonder if I’m missing something & there’s actually a better way to do this using only 1 call to Parse. Anyone? Feel free to prove me wrong. Please.

...
// localStorage is a PlayerPrefs instance that lets us grab values stored on the device that's running the app
...			 

// MyGameUsers is a Parse Class that stores unique user ID's as the parse Class' objectId's
// change MyGameUsers to your own Class name where appropriate.
var usersTablePointer = new ParseClass("/classes/MyGameUsers");

//"f2H4saD8" is an arbitrary value for demo purposes, a typical objectId from a Parse table  
string myQuery = "where={\"objectId\": \""+ "f2H4saD8" + "\" }";             				
			
ParseInstanceCollection prseList = gamerTablePointer.List(myQuery);		

//wait to get the Gamer object w/ our objectID specified in myQuery 
while(!prseList.isDone) yield return null;
			
ParseInstance[] items = prseList.items;		
				
// post a new record to the Score table in Parse:
// change MyGameScores to your own Class name where appropriate.
var myGameScoresClass = new ParseClass("/classes/MyGameScores");

//create a new record in the db table (represented by the ParseClass)
var myNewScore = myGameScoresClass.New();

//1034 is just an arbitrary score value; you can grab it from your own variable value			
myNewScore.Set( "Score", 1034 ); 	
myNewScore.Set( "MyGameUsers_ptr", items[0] as object );

//create the new record in the server side Parse Class (table) called "MyGameScores" (or your own class name)
myNewScore.Create();
				
//Wait until it's finished the save in Parse.
while(!myNewScore.isDone) yield return null;						

Debug.Log("myNewScore is done; check it in the MyGameScores table in the relevant Parse.com account.");		 

Unity3D for iOS: ParseInstance.GET(string) throws “InvalidCastException: Cannot cast from source type to destination type” for Date objects from Parse

The Error

//UniParse boiler plate:
var pObj = new ParseClass("/classes/Tournaments");			
string query = "...your query here..."; 	
ParseInstanceCollection MyParseList = pObj.List( query );			
while(!MyParseList.isDone) yield return null;					
ParseInstance[] items = MyParseList.items; 		

//get custom Date from a Parse.com Class (database table)
string MyDateFromParse = items[0].Get<string>("MyCustomDate"); 

The last line above throws this error because MyCustomDate is of type “date” in the Parse class:

/*
InvalidCastException: Cannot cast from source type to destination type.
ParseInstance.Get[String] (System.String key) (at Assets/Plugins/ParseClass.cs:238)
MyManager+<getMyCustomDateFromParse>c__Iterator15.MoveNext () (at Assets/Scripts/MyManager.cs:9)				
*/

The Solution

Read up on Data Types in Parse.com’s documentation.

Hashtable d = (Hashtable)items[0].Get<object>("MyCustomDate");
string MyDateFromParse = d["iso"];

Unity3D for iOS: “MissingReference” Error After Calling Application.LoadLevel()

I got a MissingReference error recently when loading a scene for the 2nd time via Application.LoadLevel(). In other words, I had MENU scene that handled some game settings, etc. When you hit the PLAY GAME button, Unity loaded a GAMEPLAY scene. After finishing the game, the MENU scene would load again. Without quitting the game, if I hit the PLAY GAME button inside MENU again, it would once more call Application.LoadLevel() on the GAMEPLAY scene. GAMEPLAY scene loaded ok for the 2nd time but shortly after calls to certain UIToolkit objects would generate a “MissingReference” error, similar to this one:

MissingReferenceException: The object of type ‘Transform’ has been destroyed but you are still trying to access it.

Both scenes had a GameStructureManager script. Inside the GameStructureManager script I created some members of type GameObject inside Start() via GameObject.Find() and grabbed the script component via GetComponent() in a C# project. I also assigned Events via Delegates, a technique I learned from Prime31’s UIToolkit plugin:

    void Start() {          
    ...          
       MyGameObject1 = GameObject.Find("HUD_Manager");     
       MyHUD = (HUDscript) MyGameObject1.GetComponent<HUDscript>();       

        HUDScript.onContinue += onContinueStuffHandler;         
    ...
    }

Solution

Adding an OnDestroy() and using it to unsubscribe from events subscribed to in Start() fixed the MissingReference error in this case:

    void OnDestroy() {
    
       HUDscript.onContinue -= onContinueStuffHandler;     

       Destroy( MyGameObject1 );
       Destroy( MyHUD );
    }

Unity3D for iOS: UIToolkit & Setting a Custom RGB Color Value for UITextInstance

Using Color.red or Color.green is quick and easy in a lot of cases, but if you’re working with a Designer, you often have to choose a more customized color value based on what’s in a .PSD file.

Basic UIToolkit text set up (assuming the UI and UIToolkit objects are properly set up in the Hierarchy & Inspector):

var font1 = new UIText( textToolkit, "arialregular", "arialregular.png" );			
var myText = font1.addTextInstance( "SCORE 0", 0.0f, 0.0f, 0.6f, 0, Color.green, UITextAlignMode.Center, UITextVerticalAlignMode.Middle );							

Using R,G,B values as params for Color class’ constructor

Tried a bunch of variations and they didn’t work:

myText.setColorForAllLetters( new Color( 169, 238, 3 ) ); //RGB: 169, 238, 3		
myText.setColorForAllLetters( new Color( 0.78f, 0.99f, 0.93f ) ); //HSB: 78, 99%, 93%

This didn’t work either:

	
private Color cGreen;

void Start() {
		
	//cGreen = new Color( 0.6f, 0.2f, 0.03f); //169.0f, 238.0f, 3.0f
	//cGreen = new Color( 169.0f, 238.0f, 3.0f, 255.0f); //169.0f, 238.0f, 3.0f
	cGreen = new Color( 0.5f, 0.25f, 0.25f );
...
myText.setColorForAllLetters( (Color)cGreen );
}

Convert the R, G and B to individual Percentage values

This finally worked:

	
myText.setColorForAllLetters( new Color( 0.69f, 0.93f, 0.01f) ); 

I had to take the RGB value from the .PSD and convert it to percentages. There are sites that already have this conversion for you, like this one.

This works too:

var myText = font1.addTextInstance( "SCORE 0", 0.0f, 0.0f, 0.6f, 0, new Color( 0.69f, 0.93f, 0.01f), UITextAlignMode.Center, UITextVerticalAlignMode.Middle );

Unity3D: Converting from Javascript to C#, Arrays.

Javascript works ok for some things but many Unity developers have switched over to C# because it offers more powerful features like Events & Delegates.

Here’s an example of Javascript Arrays in Unity:

private var months= new Array();
private var monthsXpos= new Array();
...

	months.Push("January");
	months.Push("February");
	months.Push("March");
	months.Push("April");
	months.Push("May");
	months.Push("June");
	months.Push("July");
	months.Push("August");
	months.Push("Septemeber");
	months.Push("October");
	months.Push("Novemeber");
	months.Push("December"); 	
	
	monthsXpos = [  UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.38f ),  //January
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.376f ), //February
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.395f ), //March
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //April
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.409f ), //May
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //June
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //July
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.389f ), //August
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.357f ), //September 
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.387f ), //October
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.363f ), //November
					UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.372f )];//December	

Note, in C# a simple array’s length is immutable. In other words, they have a set length that can’t be changed. There are other Collections objects in C# that allow for a list of variable length. Here’s the same thing in C# syntax:

	private string[] months;
	private float[] monthsXpos;
         ...
		months = new string[12] { "January",  
								  "February",	
								  "March",
								  "April",
								  "May",
								  "June",
								  "July",
								  "August",	
								  "Septemeber",
							      "October",	
							      "Novemeber",	
								  "December"};
		
		monthsXpos = new float[12] { UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.38f ),  //January
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.376f ), //February
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.395f ), //March
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //April
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.409f ), //May
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //June
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.405f ), //July
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.389f ), //August
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.357f ), //September 
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.387f ), //October
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.363f ), //November
									UIRelative.xPercentFrom( UIxAnchor.Left, Screen.width, 0.372f )};//December	

Unity3D for iOS: Etcetera plugin, “error CS0103: The name `EtceteraBinding’ does not exist in the current context””

"Assets/Plugins/Etcetera/testSupport/EtceteraGUIManager.cs(38,38): error CS0103: The name `EtceteraBinding' does not exist in the current context"

The error poped up from this line inside EtceteraGUIManager.cs’s Start() method (Line 11):

EtceteraBinding.setPopoverPoint( 500, 200 );

The solution turned out to be simple: I forgot to set the project to the iOS Platform. Go to File > Build Settings, choose iOS and click Switch Platform.

If you open the EtceteraBinding.cs class in MonoDevelop (/Plugins/EtceteraBinding/EtceteraBinding.cs) before you switch your project to target iOS, you’ll notice that much of the code is grayed out. That’s because the entire class is enclosed in iOS Preprocessor Statements like #if…#endif:

#if UNITY_IPHONE
...
#endif

Once you switch your platform in Build Settings, the code inside the #if will have normal highlighting and the above error will go away.

Unity3D for iOS: AR Flyer/Poster Example with Basic Sound Spectrum Analyzer

Here’s a quick Unity3D Augmented Reality example I made this week using Qualcomm’s free Vuforia for iOS plugin.

In addition to Vuforia, I used the handy open source iTween library & Unity’s AudioListener.GetSpectrumData on a dummy model I made using Blender. The 3 robots are acting as the sound spectrum analyzer, each being tweened in response to a float (decimal) that’s put together from an average from a sound frequency range.

I used a Creative Commons graffiti image from Flickr in the flyer/poster background & a Creative Commons mp3 from SoundCloud.

In this case the entire flyer/poster acts as the Image Target. I was surprised to see that it got a 5 star rating in the Target Management System (TMS) on QDevNet (TMS shows up under My Trackables after you create an account).

Unity3D for iOS: Tweening the Alpha on UIToolkit’s UIProgressBar – Border Fades, Bar Remains

Had to use UIToolkit’s UIProgressBar component today and ran into an issue. After the progress bar was done, I called an alphaTo() method on my “var progressBar:UIProgressBar” to fade it out. The border graphic faded out as expected but the actual progress bar stayed on screen.

As a quick workaround, the following worked for me:

  1. Added a Getter method called theBar() for the “private _bar:UISprite” variable in the UIProgressBar class.
  2. Added a second alphaTo() call to my code, using “progressBar.theBar” as the animation target
using UnityEngine;
using System;

public class UIProgressBar : UISprite
{
...
	public UISprite theBar
	{
		get { return _bar; } 
	}
...
}

Usage:

...
function Start()
{
...
	progressBar = UIProgressBar.create( "loadBar2x.png", "loadBarBorder2x.png", 6, 7, 0, 0);
	progressBar.positionFromBottomLeft( .1f, .3f );
	progressBar.resizeTextureOnChange = true;
	progressBar.zIndex = 0;
	progressBar.value = 0.4f;
...
}
...
	progressBar.alphaTo( 1.5f, 0.0f, Easing.Linear.easeOut ); 			
	progressBar.theBar.alphaTo( 1.5f, 0.0f, Easing.Linear.easeOut ); 	
...