Chapter     17

Where to Go from Here?

You’ve now completed this introduction to Unity and Unity iOS development, from downloading and installing Unity, to developing a simple bowling game in the Unity Editor, to getting it to run on iOS, and finally optimizing it to run with decent performance. Although not every Unity feature or game development topic was covered (this book would start to resemble a multivolume encyclopedia set!), I’ll wrap up with a chapter’s worth of Explore Further.

More Scripting

We just scratched the surface with Unity scripting, not only in the variety of scripting languages offered and the extensive set of Unity classes, but also what can be scripted.

Editor Scripts

If you feel there’s something missing in the Unity Editor, there’s a good chance you can add the feature by writing Editor scripts. These are scripts also written in JavaScript, C#, or Boo, but they’re located in the Editor folder in the Project View. Editor scripts have access to the Editor GUI, project settings, and project assets via a set of Editor classes, documented in the Scripting Reference under “Editor Classes.”

As an example, let’s create a menu on the Unity Editor menu bar with commands for activating and deactivating an entire hierarchy of GameObjects. In other words, the commands call SetActive on every GameObject in the hierarchy and is equivalent to clicking the checkbox of each GameObject in the Inspector View.

Before Unity 4, each GameObject just had one active state represented by the active checkbox in the Inspector View and the GameObject variable active. But starting with Unity 4, that checkbox in the Inspector View represents the local active state of the  GameObject, settable in a script by callingSetActive and read from the GameObject variable activeSelf. The global or “really” active state that depends on all its parents also being locally active. Thus, ensuring all GameObjects in a hierarchy are locally active allows you to deactivate the entire group by toggling the active state of the root GameObject. Clicking all those checkboxes by hand can be laborious and error-prone, so a single command to perform that operation would be a boon, especially for developers upgrading projects to Unity 4.

The process of creating an Editor script is the same as creating a script intended for use in a scene, except the Editor script has to be located in the Editor folder. So let’s create a new JavaScript in the Project View, name it FuguEditor, and place it in the Editor folder (Figure 17-1).

9781430248750_Fig17-01.jpg

Figure 17-1. Creating a new Editor script

Now add the code in Listing 17-1 to the script.

Listing 17-1.  The Complete listing for FuguEditor.js

#pragma strict

@MenuItem ("FuguGames/ActivateRecursively")
static function ActivateRecursively() {
        if (Selection.activeGameObject !=null) {
                SetActiveRecursively(Selection.activeGameObject,true);
        }
}

@MenuItem ("FuguGames/DeactivateRecursively")
static function DeactivateRecursively() {
        if (Selection.activeGameObject !=null) {
                SetActiveRecursively(Selection.activeGameObject,false);
        }
}

static function SetActiveRecursively(obj:GameObject,val:boolean) {
        obj.SetActive(val);
        for (var i:int=0; i<obj.transform.GetChildCount(); ++i) {
                SetActiveRecursively(obj.transform.GetChild(i).gameObject,val);
        }
}

@MenuItem ("FuguGames/ActivateRecursively", true)
@MenuItem ("FuguGames/DeactivateRecursively", true)
static function ValidateGameObject() {
        return (Selection.activeGameObject !=null);
}

The line @MenuItem ("FuguGames/ActivateRecursively") adds an ActivateRecursively item to the FuguGames menu on the Editor menu bar, creating the menu if it hadn’t already been created. The function following the @MenuItem line is invoked when that menu item is selected. ActivateRecursively checks if a GameObject has been selected in the Editor, and, if so, then it passes that GameObject to the function SetActiveRecursively along with the value true. SetActiveRecursively in turn calls SetActive with the passed value true call for each GameObject in the hierarchy.

After that, a similar block of code adds a DeactivateRecursively item to the menu, differing only in name and in passing false instead of true to SetActiveRecursively.

Although you can check if an object is selected before calling SetActiveRecursively, it’s better user interface design to deactivate a menu item if it’s not going to have any effect. You can add this by listing the MenuItem attributes again, but this time with an additional true argument, which indicates the next function is a static function that returns true only if the menu item should be active. Both ActivateRecursively and DeactivateRecursively use ValidateGameObject, which just checks if a GameObject is selected in the Editor, to decide if the menu item should be available or grayed out.

Once this script has been compiled, the new menu should appear as shown in Figure 17-2 (there may be a delay).

9781430248750_Fig17-02.jpg

Figure 17-2. A menu added to the menu bar using an Editor script

This example has minimal user interface, just some menu items, but Editor scripts can create new windows and user interfaces using UnityGUI functions (and the Editor is actually implemented in UnityGUI, which is why occasional Editor errors show up as UnityGUI errors in the Console View). Editor scripts can also be used for batch processing, postprocessing assets after import, or preprocessing scenes before a build. I have simple Editor scripts that remove build files, show GameObject statistics, and even call the OS X say command so I can talk to myself.

C#

I’ve focused on using Unity’s JavaScript as the scripting language in this book because, well, I had to pick one. But seriously, JavaScript does have more example code in the Unity documentation (although the gap is closing) and will likely look more familiar to JavaScript and Flash developers. JavaScript is also more succinct than C#, at least for small scripts. Just compare the empty JavaScript (Listing 17-2) and C# script (Listing 17-3) generated by the Create menu in the Project View.

Listing 17-2.  A Newly Created JavaScript Script

#pragma strict

function Start () {

}

function Update () {

}

Listing 17-3.  A Newly Created C# Script

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

        // Use this for initialization
        void Start () {
        
        }
        
        // Update is called once per frame
        void Update () {
        
        }
}

In the C# script, the class declaration is explicit and you have to ensure that the class name matches the script name. At the top of the C# script, two namespaces that are commonly used in Unity scripts, UnityEngine and System.Collections, are explicitly imported—in JavaScript, they are implicitly imported.

A lot of JavaScript code will run in C# with little modification, but there are some syntactic differences: variables declarations begin with <type> <name> instead of var <name>:<type>. Functions are defined the same way, starting with the return type and name instead of starting with function. There’s no reason to add #pragma strict to a C# file because C# is already strictly typed.

For small scripts, JavaScript is more convenient, but for large-scale Unity development, C# is arguably the more practical choice, given that it’s a better-documented language with industrial-strength testing as the most popular Mono or .NET language. Furthermore, many Unity extension packages are written in C# (the Prime31 plugins, EZGUI, and NGUI among them), and mixing JavaScript and C# in Unity is inconvenient; if you have JavaScript code referencing C# scripts, those scripts must be placed in the Plugins or Standard Assets folders so they load earlier.

Another reason to use C# is the addition of namespace support for C# in Unity 4. This feature allows you to dispense with prefixing class names to avoid conflicts. For example, Listing 17-4 shows a C# version of our FuguFrameRate script, where the class is defined within a namespace declaration.

Listing 17-4.  A C# Version of the FuguFrameRate Script

using UnityEngine;
using System.Collections;

namespace Fugu {
        
sealed public class FrameRate : MonoBehaviour {
                
        public int frameRate = 60;

        void Awake () {
                Application.targetFrameRate = frameRate;
        }
        
}
        
} // end namespace

Because the class is inside the namespace Fugu, you can safely call the class just FrameRate and name the script FrameRate to match. If you had to refer to this class from another script, you would have to use the fully qualified name Fugu.FrameRate unless you added a using Fugu; statement at the top of the script.C# allows you to optionally prepend the class definition with sealed, which specifies that this class is not intended to be subclassed. A compiler error will result if you try to define a subclass of FrameRate. Java programmers will recognize this is the same as the Java final declaration.

In C#, coroutines must explicitly return type IEnumerator (which is in the System.Collections namespace and why it’s convenient to always import that namespace). Although Unity automatically adds a Start callback that returns void in newly created C# scripts, that Start callback can be turned into a coroutine by changing the return type from void to IEnumerator. But it seems kind of weird to change the return type of a callback, so I prefer to move the coroutine out of the Start callback and invoke the coroutine with StartCoroutine, as I did in the C# version of the FuguAdBanner script (Listing 17-5).

StartCoroutine can be called from any callback, so this technique has the advantage of providing a uniform way to invoke coroutines and makes it unnecessary to use callbacks directly as coroutines (and also makes it unnecessary to remember which callbacks can be used as coroutines). This is not a bad practice to apply in JavaScript, too.

Listing 17-5.  A C# Version of the FuguAdBanner Script

using UnityEngine;
using System.Collections;

namespace Fugu {

public class AdBanner : MonoBehaviour {
                
        public bool showOnTop = true;
        public bool dontDestroy = true;
                
        private ADBannerView banner = null;
                
        void Start() {
                StartCoroutine (StartBanner());
        }
                
        private IEnumerator StartBanner () {
                if (dontDestroy) {
                        GameObject.DontDestroyOnLoad(gameObject);
                }
                banner = new ADBannerView();
                banner.autoSize = true;
                banner.autoPosition = showOnTop ? ADPosition.Top : ADPosition.Bottom;
                while (!banner.loaded && banner.error == null) {
                        yield return null;
                }
                if (banner.error == null) {
                        banner.Show();
                }  else {
                        banner = null;
                }
        }
}
} // end namespace

You also can’t just call yield within a coroutine as you did in JavaScript. In C#, you have to call yield return null to equivalently yield for a frame.

Another difference is the syntax of anonymous functions. In our FuguGameCenter script, I passed unnamed success or failure functions in the form of function(success:boolean) {...}, but in C#, anonymous functions are known as lambda functions (dating back to terminology from the Lisp language, which used (lambda (success) ...)) and is specified in the form (bool success) => {...} Listing 17-6 shows a C# version of the FuguGameCenter script.

Listing 17-6.  C# Version of FuguGameCenter

using UnityEngine;
using System.Collections;

using UnityEngine.SocialPlatforms.GameCenter;

namespace Fugu {
        
sealed public class GameCenter : MonoBehaviour {

        public bool showAchievementBanners = true;
        // Use this for initialization
        void Start () {
#if UNITY_IPHONE
        Social.localUser.Authenticate ( (bool success) => {
        if (success && showAchievementBanners) {
                GameCenterPlatform.ShowDefaultAchievementCompletionBanner(showAchievementBanners);
                        Debug.Log ("Authenticated "+Social.localUser.userName);
        }
                else {
                        Debug.Log ("Failed to authenticate "+Social.localUser.userName);
                }
        }
    );
#endif
        }
                
        static public void Achievement(string name,double score) {
#if UNITY_IPHONE
        if (Social.localUser.authenticated) {
                Social.ReportProgress(name,score, (bool success) => {
                        if (success) {
                                Debug.Log("Achievement "+name+" reported successfully");
                        }  else {
                                Debug.Log("Failed to report achievement "+name);
                        }
                } );
        }
#endif
}

        static public void Score(string name,long score) {
#if UNITY_IPHONE
        if (Social.localUser.authenticated) {
                  Social.ReportScore (score, name, (bool success) => {
                        if (success) {
                                Debug.Log("Posted "+score+" on leaderboard "+name);
                        }  else {
                                Debug.Log("Failed to post "+score+" on leaderboard "+name);
                        }
                } );
        }
#endif
}
        
}
}

Finally, what you might miss about JavaScript the most is the ability to easily modify the Vector3 values in a Transform Component. Listing 17-7 shows a simple line of JavaScript that modifies just the x component of a Transform Component’s position.

Listing 17-7.  Modifying a Vector3 of a Transform in JavaScript

function Start () {
        transform.position.x=0;
}

That line of code will generate a compiler error in C# (Figure 17-3), because Vector3 is a struct, not a class, and thus a value type, not a reference type. The fact that it works in JavaScript is a convenience (albeit a misleading one, because it does make Vector3 look like a class, not a struct).

9781430248750_Fig17-03.jpg

Figure 17-3. Error message when modifying a Transform’s Vector3 in C#

Because Vector3 is a struct, you don’t have to worry about the garbage collector having to clean up a bunch of unused Vector3 instances, since, well, there are no instances. But it does mean in C# you have to create a new Vector3 if you want to modify the Transform Component’s position, as shown in Listing 17-8.

Listing 17-8.  A Valid Way to Modify a Transform in C#

void Start () {
        transform.position = new Vector3(0,transform.position.y,transform.position.z);
}

Notice how in C# you have to specify new before the Vector3 constructor, whereas in JavaScript that is optional. That is a minor convenience of JavaScript (these types of conveniences are generally known as syntactic sugar), but C# has the advantage of allowing you to define your own structs, in a manner similar to how classes are defined. In JavaScript, you can use structs, but there’s no way to define new ones. Andrew Stellman and Jennifer Greene’s Head First C# (O’Reilly) is a good introduction to C#, but experienced programmers can get a quick start in C# with the C# in a Nutshell series (also published by O’Reilly). Java programmers in particular will find C# familiar (despite the name, C# has no relation to C or C++, unless you count distant ancestors).

Script Execution Order

Before leaving the topic of scripting, there’s one more issue worth mentioning: specifying the order of execution among scripts. If you look in the SmoothFollow script used to control the Main Camera in the bowling game, you’ll see the Camera position is updated not in an Update callback, but in a LateUpdate callback. LateUpdate is called in every frame just like Update, but all LateUpdate calls in a frame take place after all Update calls have completed. SmoothFollow calculates the Camera position in LateUpdate so it takes into account any position change that’s taken place in its target’s Update callback.

Having both Update and LateUpdate callbacks is a common technique in game engines, but it can only go so far. What if you want a GameObject to update after another GameObject’s LateUpdate? Sorry, there’s no LateLateUpdate callback! But Unity does have a more general solution, the Script Execution Order Settings, which can be brought up from the Project Settings submenu of the Edit menu (Figure 17-4).

9781430248750_Fig17-04.jpg

Figure 17-4. Bringing up the Script Execution Order Settings

The Script Execution Order Settings can also be brought up by clicking the Execution Order button displayed in the Inspector View when a script is selected. Either way, the Script Execution Order Settings show up in the Inspector View as a MonoManager (Figure 17-5) and has a list initially containing just one item, Default Time.

9781430248750_Fig17-05.jpg

Figure 17-5. The Script Execution Order Settings in the Inspector View

Since SmoothFollow uses LateUpdate, you don’t have to make any changes, but let’s say you want to make sure the SmoothFollow script runs its Update callback after all other scripts. You would click the plus sign (+) button at the lower right of the Script Execution Order Settings and select the SmoothFollow script from the resulting menu. By default, the SmoothFollow script would appear in the Script Execution Order Settings below the Default Time, meaning SmoothFollow will run after all other scripts.

And then you might decide that the game controller script, FuguBowl, should run after every other script (since it’s constantly checking the bowling position, for example) except for SmoothFollow. So again, you would click the + button to add the FuguBowl script and then drag FuguBowl, using the handle on the left, above SmoothFollow but below Default Time, resulting in a list as shown in Figure 17-6. Anything dragged above Default Time would execute before all the other scripts.

9781430248750_Fig17-06.jpg

Figure 17-6. Specifing the execution order of FuguBowl.js and SmoothFollow.js

Clicking the minus (-) button on the right of a script will remove the script from the Script Execution Order Settings.

Plugins

Much mention has been made of third-party plugins, some available on the Asset Store, and many from Prime31 Studios. But if you happen to be conversant in C, C++, or Objective-C, you can write your own plugins, either to access more device functionality or to make use of libraries written in C, C+, or Objective-C. Besides including the native library, plugins require additional Unity wrapper scripts. The process is documented on the “Plugins” page in the “Advanced” section of the Unity Manual.

Tracking Apps

Although iTunes Connect provides download and sales figures (and the graph is a lot easier to read than the .csv report, which once was the only option), there are several third-party products that track app sales and other data like app rankings and reviews.

App Annie (http://appannie.com/) and AppFigures (http://appfigures.com/) are two of the more popular web-based products that both automatically perform daily downloads of app data from iTunes Connect (after you provide them your iTunes Connect log-in information during account set up). They both have nice graph presentations of the sales data and send daily email summaries of the app statistics. App Annie is currently free (technically in beta) while AppFigures charges a fee.

Figure 17-7 shows an App Annie graph of downloads over a month for Fugu Bowl. The number of downloads that are updates are overlaid over the downloads that are sales. There are spikes in the update line corresponding to releases of updates for FuguBowl.

9781430248750_Fig17-07.jpg

Figure 17-7. The App Annie sales graph

AppViz (http://appviz.com/) is an OS X application that performs the same function but downloads the data onto your Mac and features a variety of graphs, including sales by geography (Figure 17-8). AppViz is not free but it doesn’t cost much and I think it’s well worth it.

9781430248750_Fig17-08.jpg

Figure 17-8. The AppViz geographic view

There’s no reason to restrict yourself to just one of these products. I use AppViz to ensure I have all of my app sales data available locally and I like its variety of graphics and the App Store review downloads. I use AppAnnie for its automated e-mail reports of daily sales figures, rankings display (downloading of all that ranking data in AppViz takes a long time), and App Annie has support for some Android app stores. Also, it’s free!

Tip   From AppViz, you can export all the data you’ve downloaded and then import that data into another product like App Annie. Since Apple only makes a year’s worth of app sales data available on iTunes Connect, this is a way to ensure you can always import the entire sales history of an app into a sales tracking product.

Promo Codes

I have described how to download promo codes after an app is approved on the App Store, but I didn’t explain what to do with them. There are plenty of options. Nearly every app review site accepts promo codes with review requests. Touch Arcade (http://toucharcade.com/) is one of those sites, but it also has a very active forum where developers issue promo codes. And AppGiveaway (http://appgiveaway.com/) runs promotions where they hand out promo codes over a set period of time. This is a lot more convenient than handing them out yourself, although you should do that, too. Keeping an inventory of promo codes is also a good reason to crank out app updates, since you get a fresh batch of 50 codes with each update.

More Monetization

Given the yearly Apple developer fee and Unity license costs (if you decide to go with the Pro licenses), chances are you want to at least make that money back. Besides iAd and third-party mobile ad services, there are other monetization strategies.

In-App Purchasing

Apple offers one other revenue channel for iOS besides app sales and iAd: an in-app purchasing (IAP) system called Storekit. Unity iOS doesn’t have a script interface for IAP, but Prime31 Studios includes a Storekit plugin among its offerings. The Unity Manual has some tips for how you might download and activate additional content purchased with IAP, and the iTunes Connect documentation explains how to set up in-app purchases.

Asset Store

I use dozens of free and paid Asset Store packages in my apps, and you will surely find the Asset Store useful for your own projects. But the Asset Store is also another place to self-publish products and generate revenue (Unity takes a 30% cut, same as Apple does on the App Store). And you can release packages free on the Asset Store for recognition or as a contribution to the community (I’ve certainly taken advantage of that generosity for the examples in this book).

Conveniently, Asset Store submissions are made from within the Unity Editor, using the Asset Store Tools downloaded from, aptly, the Asset Store. You can submit any collection of assets from a project—audio, textures, models, scripts, plugins, or even the entire project itself.

Tip   Use preprocessor directives to ensure there are no compiler errors when switching build targets, and add a custom UNITY_PRO preprocessor directive if you have Unity Pro features, so that Unity Basic users won’t have problems.

Much of the work in an Asset Store submission, as with an App Store submission, involves creating screenshots, icons, app descriptions, and store artwork. The instructions for becoming a seller on the Asset Store are available at http://unity3d.com/asset-store.

Developing for Android

Unity Android requires an additional license, with the same Indie and Pro structure as for Unity iOS. The good news is that almost everything developed for Unity iOS in this book will work fine with Unity Android. Unsurprisingly, anything in the iPhone class is not available on Android either (e.g., the variable iPhone.generation, which we used to check the device type), but everything in the Input class that works for iOS also works the same on Android, including the touchscreen and accelerometer functions.

Game Center and iAd are Apple technologies, so those features aren’t available on Android, but in the future, the Social, ADBanner, and ADInterstialAd classes might support equivalent offerings on Android. Prime31 Studio and other vendors offer Unity Android plugins to provide more access to device features and mobile services, such as the AdMob mobile ad service.

The build and submission process is different for Android, too. Instead of Xcode, Android requires installation of the Android SDK. You can still invoke a Build and Run from the Unity Editor, but instead of building an Xcode project for the app, Unity Android directly builds the executable app (an APK file). And unlike iOS development, Android development can be targeted to a number of app stores. Google Play in particular has no approval process, so it’s a good avenue to self-publish quickly and a useful fallback channel for apps rejected by Apple.

Contract Work

While working on that next hit app, independent developers often keep the bills paid by taking contract work. Prospective clients can be found in the Paid Work topic of the Unity forum and on http://unity3dwork.com/ and even general contractor web sites like http://odesk.com/. Some marketplaces specialize in mobile app development, such as http://apptank.com/ and http://theymakeapps.com/.

And if you’re looking for full-time game development work, whether it involves Unity or not, experience developing games with Unity, not to mention a portfolio of self-published games, can only help.

Final Words

Now I’ve come to the end, and hopefully you’re ready and excited to start making your own Unity iOS apps. If there’s one lesson that I hope you can take from this book, it’s that you can start with something simple and keep building on it until it turns into something interesting. Don’t be one of those people who want to start with a massively-multiplayer online (MMO) game as a first project!

Remember to keep learning and participate in the Unity community, both contributing and asking for help on the Unity forum and on the Unity Answers site, and don’t forget to check the Unity wiki. It’s fine to promote your work to the community (there is a Showcase topic in the Unity forum) and ask others to spread the word, but remember to return the favor!

As an unintended result of working on my own projects with Unity iOS, I’ve met a population of developers with whom I can commiserate and celebrate and trade valuable tips. I won’t try to list them all, but you can find them on my twitter follow list at @fugugames. Twitter, by the way, is another great way to interact with other Unity developers, on a more personal level.I’ve communicated with many customers over twitter, also, and have had some success with setting up Facebook product pages for my games (in particular, HyperBowl). Although app customers can be legendarily snarky, I’ve found most of them are supportive and sympathetic of us independent developers and are enthused to be part of the process if we let them. In fact, I’ve benefited from a lot of free QA and some localization (translation) help, not to mention a lot feedback. So, set up those twitter feeds and Facebook fan pages and start talking!

Above all, have fun. Any other reward is a bonus!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset