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!");
	}
}
Advertisements

Unity3D for iOS: Black Screen After Loading Screen on iPad 3, iOS 6

I’ve heard of this error happening when you accidentally have a sprite sheet set to about 2048 pixels in width or height.

This just started happening to me, after I upgraded my Mac OS from 10.6.8 to 10.7.5, running Unity 3.5.6f4 and upgraded to TexturePacker Pro 3.0.1.

The error didn’t show for me on iPhone 4S or iPad 2. As soon as I tried to load my dev .ipa onto an iPad 3 running iOS 6 via iTunes, I started seeing the black screen.

Then I opened Xcode and hit Run with my iPad 3 plugged in and here’s the error that came up in Xcode’s Console:

- Completed reload, in  0.212 seconds
-> applicationDidBecomeActive()
UI texture is being autoloaded and it doesn't exist. Cannot find texturePackerConfigName: imgSheet4x

(Filename: /Applications/buildAgent/work/14194e8ce88cdf47/Runtime/ExportGenerated/iPhonePlayer-armv7/UnityEngineDebug.cpp Line: 43)

(lldb) 

Saw this in Thread 1 too:

My_App_Name`UISpriteManager_loadTextureAndPrepareForUse + 260 at Assembly-CSharp-firstpass.dll.s:17293: <<<<<< Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

Now, I’m not using any 4x sprite sheets in this particular project due to file size constraints.

I just had my production machine upgraded from OS X 10.6.8 to OS X 10.7.5. Looks like this is what did it. 10.6 couldn’t really detect iPad3’s high res display, so stuff published out of Unity3D on 10.6 just auto scaled everything and used the 2x sprite sheet. Now that I’m on OS X 10.7, the OS suddenly allows iPad3 detection, UIToolkit responds by trying to load the 4x texture, which I don’t have due to .ipa file size constraints.

Changing the following in UI.cs:

	isHD = true;
				scaleFactor = 4; 
				hdExtension = "4x"; 

to this, by itself, just makes everything super tiny on iPad3’s display:

	isHD = true;
				scaleFactor = 2; //scaleFactor = 4
				hdExtension = "2x"; //hdExtension = "4x";

The quick fix here seems to be to republish from Unity installed on a machine w/ OS X 10.6.8. Your Unity license allows up to 2 installs per license. Remember, that operating system upgrades on the same machine count as “an install” in this case. If you’ve reached your limit, you can email Unity’s support and ask to deactivate one machine associated with your email address. They seem to be able to do it within a day or so. Then you can install on a new machine as needed. Not pretty, but an option.

Long term, you have to either add a 4x sprite sheet or update code to auto scale your UI.

Unity3D for Android: Porting a Unity3D iOS Project to Android

This is a Notes-To-Self work in progress. Why share? Several reasons, among them, my desire to see Unity become a more popular platform for mobile app development in the near future. If any part of this post saves someone dev time or helps them pick Unity as the dev platform for their mobile project, that means more work opportunities for developers that took the time to learn Unity.

Basic checklist

  • For on-device debugging, add Android’s adb tool to your environment variable. For me, on OS X, it was fairly easy.
  • Set up logcat w/ adb to get on-device debugging on Android. Xcode’s Console lets you do this pretty much by default when you “Run” on a device that’s attached to your machine via USB.
  • Enable your Android phone for development & USB debugging.
  • Figure out an approach for dealing with the numerous Android pixel densities & screen sizes. Daniel Brauer’s DisplayMetricsAndroid class looks pretty good. Haven’t tested enough yet. As a commenter states, it’s implemented like a “plugin” via the Plugins folder in your project’s Assets folder. Then there’s Screen.dpi, in, at least, Unity 3.5.
  • If you’re using Prime31 plugins like Etcetera and Social Networking, you’ll need to get the Android versions. Their Google Analytics plugin works on both.
  • If you’re using the open source UIToolkit for menus, screens, HUD, etc, look into how to get 1.5x graphics to work. Perhaps, 1.5x sprite sheet and tweak(s) to or subclasses of the UI or UIToolkit classes. Not sure yet.
  • The ProgressDialog is Android’s version of the Activity indicator, UIActivityIndicatorView on iOS.

Potentially useful formulas & numbers

A review aspect ratio formula might come in useful. For example, if you have a screen that’s “1280 x 720”, you can assume width/height = 1280/720 = 1.78 = 16/9, aka it’s a 16:9 aspect ratio. This calculator can be useful at times.

  • So, for HTC Legend’s 480×320, you can do 480/320 = 1.5 = 3/2 to get an aspect ratio of 3:2.
  • For Motorola Droid’s 854 x 480 – 854/480 = 1.78 (1.779) = 16/9 (roughly) yields a 16:9 ratio.
  • For Nexus One’s 800 x 480, do 800/480 = 1.6 (1.666) = 16/10 (roughly) to get 16:10 aspect ratio.
  • Samsung Galaxy S3 is at 1280×720, with a 16:9 ratio.
  • Most Galaxy S2 phones are at 800×480, 16:10 ratio. At least one is at 1280×720, 16:9 ratio.
  • iPad 3 has the horizontal dimensions of 2048 × 1536. 2048/1536 = 1.33, which is roughly 3/2. 3:2 aspect ratio

So if I’m using sprite sheets w/ UIToolkit, does this mean I should create android versions of the sprite sheets at 1.5x, 1.78x and 1.6x versions of the basic size of the smallest iOS images (sheets)? Mmmmm… more research needed.

Try Using Density Independent Pixels via Unity’s Screen.dpi

Screen.dpi is one of the improvements in Unity 3.5 (or from what I hear, even 3.4). If you’re using UIToolkit, you can try using public methods like pixelsFromTopLeft( float x, float y) coupled with the Density-independent pixel technique recommended by the Android developer guide:

Density-independent pixel (dp)
A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a “medium” density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application’s UI, to ensure proper display of your UI on screens with different densities.

So to get the x, y values for the pixelsFromTopLeft( float x, float y) method, use the formula px = dp * (Screen.dpi / 160), assuming 160 dpi is your minimally supported screen dpi. This also means that dp = px / (Screen.dpi / 160).

Note, on some Android devices the system fails to generate a dpi and returns a ZERO. In those cases, the above formula breaks, as noted here.
Here’s a free DP to PS calculator.

SD / HD screen sizes for Android, UIToolkit Sprite Sheets & PSDs

In 2012, 800×480 seems like the most common Android resolution for phones. I’ve heard some rumors on forums w/ no links to sources. Any sources I find, I’ll post here:

For UIToolkit, 800×480 can be our SD resolution and the size we should create the first sprite sheet for. So the .PSD files should be created at 16:10 aspect ratio for 800×480, or the SD UIToolkit sprite sheet in Unity.

I personally don’t care about the low density older Android phones – since I’m making 3D games using Unity, those phone’s processors & memory probably won’t be able to handle my app anyway, so why waste time on creating yet another sprite sheet for it?

Depending on whether or not you care about all of Android’s aspect ratios, supporting slightly older Android tablets or only the latest ones, or just the latest high density phones, you can set your UIToolkit HD sprite sheet(s) to, for example 1280×720 for the latest 2012 Samsung phones like the Galaxy SIII. Using 1280×720 or slightly less like 1160×720 to catch devices like the Galaxy Nexus phone, seems to also work on a few Android tables (like Motorola’s).

Remember, and prep your Designers for this ahead of time, it won’t look pixel perfect on every device. Android device screen size fragmentation is probably worse to deal with than cross-browser CSS problems (yes, even with IE6,7.8 vs the standards browsers on Mac and PC). Gently, remind Designers that an Android layout is not a PSD, PDF, an InDesign file or a printed page.

Some common Android phones with the medium 800×480, or the very close 854×480, screen size:

  • HTC Inspire 4G
  • Motorola DroidX
  • HTC Evo 4G

Sprite sheet problem

UIToolkit worked mostly well for iOS with 3 large, full-screen-sized background images even fitting inside the regular (SD) and 2x (HD) sprite sheet at 2048×2048 in TexurePacker Pro.

I just tried using a similar approach for Android and ran into a wall. The 2x sprite sheet is a problem – if for 2x, I’m sizing my graphics for HD Android phones like the Galaxy SIII at 1280×720… those 3 full-screen-sized background images don’t fit anymore inside 2048×2048. From what I’ve heard on the Unity forums Android has problems with images (sprite sheets) larger than 2048×2048. It’s not ideal, but I guess I can always proportionally scale those down for the sprite sheet and then use Screen.width and Screen.height to scale them. Given the client approved design, it’s nearly impossible, not to have them. The other approach would be to add a second sprite sheet, may be one for buttons & smaller UI stuff and one for unavoidable large background images. This will increase production time since extra sprite sheets will need to be created for SD and HD resolutions. & you’ll need to be careful which sprite sheet you’re targeting when from code.

Some Android-related posts on the main UIToolkit forum thread

Testing in Unity’s Game panel

  • To fake the 1280×720 screen size of the Samsung Galaxy SIII and similar new phones, switch the Game panel’s aspect ratio to Android Wide (16:10) and then stretch the panel till it approximates those dimensions. You can test them in the Console via Screen.width & Screen.height or use something like Screen Ruler or a PSD that’s at that size to approximate. Note: 1280×720 is actually 16:9, the 16:10 Android devices at that size are 1280×800, so keep that in mind.

On-device testing

  • I had trouble with “adb devices” not recognizing an HTC Inspire 4G and then a Motorola DroidX. I googled the crap out of it, read through every post on http://forum.xda-developers.com/ and still nothing. Then I borrowed a USB cable from a coworker who had an Android phone & boom! everything magically worked: adb devices listed both phones, Unity3D was able to publish directly to the device via “Build & Run”. Fun times.

PlayerPrefs.txt & Releasing Updates to iOS & Android Apps

  • Android: Looks like if you release an update to your app via Google Play, your PlayerPrefs.txt file will not be overwritten. I was able to confirm this from my own experience with a Google Play app.
  • Same deal for iOS, looks like an App Store update doesn’t overwrite PlayerPrefs.txt. I was able to confirm this from my own experience with a Unity3D-built app in Apple’s App Store.
  • Some Stuff About Google Play Sucks

    • If you’ve uploaded a new version of your app (a new APK), realize that this version has a bug, and decide to revert to the previous version, you can’t. Google Play doesn’t allow it as of October, 2012. If you’re the developer, you can compile your older source code with a newer Version Code (versionCode) and upload it as a NEWER APK and overwrite the problematic version. What a waste of my time!

    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 for iOS: Augmented Reality with a Beer Bottle

    I used the beer label as the Image Target for Vuforia for iOS. Qualcomm’s Target Management System (TMS) rated the label image with 3 out of 5 starts, which is better than I thought but still leaves room for a little unwanted jitteriness. To create the image, I simply took a photo of the beer label with my phone camera, silo’d it out in Photoshop & exported as a 24-bit (transparent) PNG, uploaded to the TMS.

    Next, I added some random 3D models, an empty GameObject w/ Box Collider behind the beer label (ImageTarget prefab) and basic text & button sprites via UIToolkit, as well as some mind-numbingly simply logic.

    The UIToolkit UI object (& it’s child UIToolkit objects) seems to work fine when nested inside Vuforia’s ImageTarget prefab:

    I set the ARCamera’s frustum to look at the global Y axis in this case (ARCamera is Vuforia’s version of Main Camera):

    Unity3D for iOS: “IndexOutOfRangeException: Array index is out of range” Error While Adding a New BMP Font for UIToolkit

    
    	var font1Txt:UIText = new UIText( textToolkit, "interstatereg", "interstatereg2x.png" ); 
    
    /*
    IndexOutOfRangeException: Array index is out of range.
    UIText.loadConfigfile (System.String filename) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:135)
    UIText..ctor (.UIToolkit manager, System.String fontFilename, System.String textureFilename) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:86)
    Instructions+$animateIn$18+$.MoveNext () (at Assets/Scripts/MyScriptFile.js:42)
    */
    

    In UIText.cs line 135 contains the _fontDetails array (defined in the constructor to hold 256 items or id’s from 0-255). Also in line 135 “idNum” being used to specify the array’s index during the loop:

    ...
    _fontDetails = new UIFontCharInfo[256];
    ...
    _fontDetails[idNum].charID = new int();
    

    To get more info, I added a log statement to UIText.cs on a little before line 135 (mentioned in the error above) like so:

    Debug.Log("++++ idNum = " + idNum);
    

    This showed me the exact line from the .fnt file was causing the problem, “char id=8482”:

    /* //from the Console: 
    ++++ idNum = 8482
    UnityEngine.Debug:Log(Object)
    UIText:loadConfigfile(String) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:132)
    UIText:.ctor(UIToolkit, String, String) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:86)
    $:MoveNext() (at Assets/Scripts/MyScriptFile.js:42)
    */
    
    //from .fnt (.txt) file: 
    char id=8482   x=145     y=83     width=20     height=11     xoffset=0     yoffset=5    xadvance=18     page=0  chnl=0 
    

    8482 looks like the “id” for the trademark “TM” character & UIText.cs can’t use it since the _fontDetails array is defined to only use the 0 – 255 range (see constructor method in UIText.cs).

    ASCII v. Unicode

    I don’t fully understand the reason why my attempt at adding a “TM” character resulted in char id of 8482 but I’m guessing Bitmap fonts via UIToolkit work with ASCII characters, hence the 0 – 255 range, while my computer used Unicode (which goes well beyond 0-255) when I typed “TM” into Hiero’s “Sample text” text box.

    I was hesitant to click the ASCII button in Hiero to add “TM” because every time I do that Hiero grinds to a halt and basically stops working. So, replacing the Unicode “TM” with Hiero is doable but takes forever on any of my machines (including a 2.7 GHz Intel Core i5 iMac w/ 8G RAM). May be you’ll have better luck with low priced alternative software on Mac or Angelcode on PC (free).

    Copy/Pasting Text from Photoshop

    I got a similar “IndexOutOfRangeException: Array index is out of range” error when copy/pasting a paragraph from a .PSD file:

    IndexOutOfRangeException: Array index is out of range.
    UIText.drawText (.UITextInstance textInstance, Single xPos, Single yPos, Single scale, Int32 depth, UnityEngine.Color[] color, UITextAlignMode instanceAlignMode, UITextVerticalAlignMode instanceVerticalAlignMode) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:235)
    UIText.addTextInstance (System.String text, Single xPos, Single yPos, Single scale, Int32 depth, UnityEngine.Color[] colors, UITextAlignMode alignMode, UITextVerticalAlignMode verticalAlignMode) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:579)
    UIText.addTextInstance (System.String text, Single xPos, Single yPos, Single scale, Int32 depth, Color color, UITextAlignMode alignMode, UITextVerticalAlignMode verticalAlignMode) (at Assets/Plugins/UIToolkit/UIElements/UIText.cs:569)
    Instructions+$animateIn$12+$.MoveNext () (at Assets/Scripts/Instructions.js:57)
    

    Pretty sure it’s the ASCII v. Unicode issue again. In this case, characters like curly quotes caused the error.

    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 ); 	
    ...