• Register
Post tutorial Report RSS Unity iOS Essentials: Flyby Background

One of the best ways to make the menu system engaging is to have the player fly through one or more of the game scenes as a backdrop to the menu system.

Posted by on - Basic Animation

In this article by Robert Wiebe, author of Unity iOS Essentials, we will learn the following:

  • How to set up a background scene that gives the player a feel for the expansive nature of our game?

  • How to create a path that a camera can follow?
  • How to create a main menu that overlays the camera, flying through our scene?
  • How to save time by creating a menu that can be tested as easily in the editor as on an iOS device?
  • How to set up Unity3D for iOS build settings to create an App that will run on all iOS devices?
  • How to deploy an iOS app on multiple devices?


(Develop high performance, fun iOS games using Unity 3D with this book )

Set up a background scene Typically, the background scene for the menu system will also be a scene from the game. But because the player will not be interacting with the menu's background scene, they will not be able to see it from all angles, jump on objects, and so on. It is possible to strip out many of the components from the scene to optimize it for use as a menu background.

Another option is to create a brand new background scene that incorporates game assets from multiple game levels into a single menu background to give the player a full picture of the scope of our game. Again, we can achieve this by being selective about the game assets that we use and stripping out all but the most essential visual components of those assets.

The background scene can be a teaser, it can include Easter Egg hints, it can contain character cameos from other games, and it can contain parts of other games.

Finally, the background scene can be a selling opportunity. It can contain advertising or it can contain objects that the player may be interested in purchasing, using game purchase. The contents of the menu background scene are limited only by what we can imagine.

In the following image, we show the original first level of a game that we have selected to be used as the background scene for our game:

The scene is adequate and we could use it as the background for our game menu, but we want to give the game player a better idea, right on the main menu, of just how large the game is. So instead, we add some buildings from a second level (we could do more; we could make our menu level huge, but this should do for now), and come up with the new, much larger scene, as shown in the following screenshot to use as the background for our main menu:

Set up the camera path Once we have decided on the final scene that we want to use as the background for our main menu, we need to decide on the path that the camera will follow as it moves through the scene.

While it is not a requirement that the camera moves along a path, it adds interest to the menu, and so we will continue with that menu theme for our game.

This is an opportunity for us to tease the player, since it may be some time before they arrive at a specific part of the menu scene during actual gameplay. We can flyby Easter Eggs or we can flyby specific objectives. Again, this is the part of the game where we get to be creative and draw the player into the game.

If we want to, we can even defy the physics of the game and fly through things, so that we don't give away the secret of how to achieve a game objective or how to obtain an Easter Egg, but we do let the player know that they are there. We need to be careful to do this in such a way as to fade through scenes rather than clip through the geometry, so that we don't disturb the immersive feel of the game in order to hide things from the player's view.

To set up a path, the first thing we need to do is create a gizmo. A gizmo is an image that appears in the Unity3D editor at the position of a game object's transform, but otherwise will not be visible in the game. As a game designer, we use gizmos to arrange these game objects graphically in the editor, rather than having to find each object in the game hierarchy, and enter x, y, and z values to position the transformation. Gizmos, while not necessary, are very useful because they allow us to create a visual representation of an otherwise empty game object in the Unity3D editor. To create a gizmo, you need two things:

  • A gizmo image

  • A gizmo script

The following is an example of a gizmo image. It can be anything you want. In this case, we have simply chosen an image of the letter W – for waypoint. The image file is named Waypoint.tif, and it has been imported into Unity3D as a standard GUITexture as follows:

The following is an example of a gizmo script that draws a waypoint gizmo image at the location of the game object transform to which it is attached:

// Draw the waypoint gizmo at the Game Object's
// transform position.

// You can use any image that you want

// This gizmo will be pickable which means you
// can click on it in the editor to select
// the attached game object

function OnDrawGizmos ()
{
//The image must be in Assets/Gizmos – the size of the image
//is how large it will be drawn in the scene view
Gizmos.DrawIcon (transform.position, "Waypoint.tif");
}

To use a waypoint in the editor, we need to do the following:

  • Create an empty game object

  • Attach the waypoint.js script to the game object

    The following image shows our example level with a number of waypoints added to define the path that we want the camera to follow for our menu flyby. Everywhere you see the letter W in this image, denotes a waypoint along the path:

    Adding waypoints and drawing a gizmo at a waypoint's location in the editor is helpful, but one more thing that we can do to make the order of the waypoints clear is to draw a line between the waypoints. So, for example, we would draw a line from wp1 to wp2, from wp2 to wp3, and so on. The following script fragment, which is part of the SeekSteer script, shows how to draw the lines between the waypoints:

    // draws a line from waypoint to waypoint

    public void OnDrawGizmos()
    {
    Vector3 l_prevWaypointPosition;
    Vector3 l_currentWaypointPosition;

    // Choose a color, it can be any color you like
    Gizmos.color = Color.red;
    // Draws a line between the last waypoint
    // and the first waypoint
    l_prevWaypointPosition =
    waypoints[waypoints.Length-1].position;
    l_currentWaypointPosition = waypoints[0].position;

    Gizmos.DrawLine(l_prevWaypointPosition,
    l_currentWaypointPosition);

    // Choose another color, it can be any color you like
    // except the first color
    Gizmos.color = Color.green;
    // For each remaining waypoint, in the waypoints array
    // draw a line between the two points
    for (int i=1;i < waypoints.Length;i++)
    {
    l_currentWaypointPosition = waypoints[i].position;
    l_prevWaypointPosition = waypoints[i-1].position;

    Gizmos.DrawLine(l_prevWaypointPosition,
    l_currentWaypointPosition);
    }
    }

  • The following screenshot shows the new scene with lines being drawn between the waypoints. It's easy to identify the first and last waypoint, because they are the points with the red lines between them:

    Once the waypoints have been set up, we need something to follow them. There are literally dozens of ways that we can use to follow a path, and many kinds of paths that we can follow. In fact, there are complete Unity3D projects devoted to path finding.

    In this case, we have chosen to use the SteerSeaker method of following a path. The SteerSeaker creates an array (or list) of all the waypoints, and moves from one waypoint to the next in the same amount of time. In order to keep the time between waypoints constant, the SteerSeaker speeds up or slows down based on the distance between the waypoints, which makes it easy for us to predict the total time it will take to follow our path and create sections of both slow and fast movement. The rest of the SteerSeaker script (remember we looked at the previous image that draws lines between the waypoints) is shown as follows:

    This script is written in C# rather than JavaScript. While many people new to Unity3D prefer to work in either JavaScript or C#, it's important that we become familiar with both scripting languages, so that we can take advantage of all the open source resources available in the Unity3D community. While we don't need to be able to program in both languages, we do need to be able to read both languages.

// SeekSteer.cs
// Based on the original SeekSteer by Matthew Hughes
// -- 19 April 2009
// -- Uploaded to Unify Community Wiki on 19 April 2009
// -- URL:

Unifycommunity.com
//
// Changes by BurningThumb Software
// -- March 2010
//

using UnityEngine;
using System.Collections;

public class SeekSteer : MonoBehaviour
{
// This is the array of waypoints
public Transform[] waypoints;

// This is the radius, in meteres of a waypoint
public float waypointRadius = 1.5f;

// Damping is used to limit the rate at which
// the object turns towards the next waypoint.
// Smaller numbers slow down turns, larger
// numbers speed up turns
public float damping = 0.1f;

// Set loop to true if the object loops
// continuously around the waypoints and
// to false if the object only goes around
// one time
public bool loop = false;

// The time between waypoints is constant
// so the object will speed up or slow down to
// achieve this time regardless of the distance
// between the points
public float transittime = 2.0f;

// Set faceHeading to true to make the object
// turn to face the forward direction and to
// false to not turn to face the forward
// direction
public bool faceHeading = true;

// The current heading of the object
private Vector3 currentHeading;

// The desired heading of the object
private Vector3 targetHeading;

// The array index of the waypoint that the
// object is heading toward
private int targetwaypoint;

// A reference to the transform of the object
// used to speed up the script by caching the
// reference which is used many times
private Transform thisTransform;

// If the object has a rigid body then this
// is a reference to the rigid body of the object
// used to speed up the script by caching the
// reference which is used several times
private Rigidbody thisRigidbody;

// Use this for initialization
protected void Start ()
{

// If the waypoints array is empty this script
// logs a message and disables itself. You need
// to add waypoints to the array in the Unity3D
// editor

if (waypoints.Length <= 0)
{
Debug.Log("No waypoints on "+name);
enabled = false;
}

// Cache a reference to the transform to speed
// up the execution of the script
thisTransform = transform;

// Set the current heading to be forward
currentHeading = thisTransform.forward;

// The first target waypoint, is the first
// one in the array
targetwaypoint = 0;

// Cache a reference to the attached Rigidbody to
// speed up execution of the script. If
// no Rigidbody is attached this will be null
thisRigidbody = rigidbody;

}

// calculates a new heading. This is done in
// fixed update just in case there is a
// Rigidbody attached and physics are
// involved
protected void FixedUpdate ()
{
// A simple Lerp with damping to adjust
// the heading towards the current target
// waypoint
targetHeading = waypoints[targetwaypoint].position -
thisTransform.position;
currentHeading = Vector3.Lerp(currentHeading,
targetHeading,
damping * Time.deltaTime);
}

// moves us along current heading
protected void Update()
{
// If a Rigidbody is attached then
// physics is in use so add velocity
if (thisRigidbody)
{
thisRigidbody.velocity = currentHeading *
transittime;
}
// Otherwise set the position directly
else
{
thisTransform.position = thisTransform.position +
(currentHeading *
Time.deltaTime *
transittime);
}

// If the object needs to face the heading,
// make it look that way
if (faceHeading)
{
thisTransform.LookAt(thisTransform.position +
currentHeading);
}

// Check to see if the object is inside the
// waypoint radius
if (Vector3.Distance(thisTransform.position,
waypoints[targetwaypoint].position) <=
waypointRadius)
{
// Add one to the target waypoint to select
// the next waypoint
targetwaypoint++;

// if the next waypoint is past
// the end of the array
if(targetwaypoint>=waypoints.Length)
{
// set it back to the beginning
targetwaypoint = 0;

// If the object is only supposed
// to transit the waypoints one
// time then disables the script
if (!loop)
{
enabled = false;
}
}
}
}

  • With this final script, we create the SteerSeeker game object and attach not only the SteerSeaker script, but also a gizmo script to display the letter S. This is done so that we can see the position of the game object in the Unity3D editor. The following image shows the SteerSeaker object settings in the Unity3D editor:

    The variables declared as public in the script are the ones that will appear in the Unity3D editor.

    The following image shows its position on the waypoint path, as we run the game in the editor:

    Finally, we need to have the main camera follow the SteerSeaker object as it moves along the waypoint path.

    The advantage of using a script, instead of a simple timeline, is that we can create more dynamic camera movement, for example, a rollercoaster effect where the camera changes angles as it moves, or a script that "gobbles" up jewels along the path as it moves. It's important to understand the concept of using a script and its benefits, rather than simply looking at what this specific example does. Often, as in this case, example code is kept simple to convey the concept or idea, and is not meant to be the final solution that we would deploy in a game.

    This is done by attaching the FollowTransform script from the iPhone Standard Assets to the main camera, while assigning the SeekSteerObject to the Target Transform and checking the box Face Forward on that script. This is shown as follows:

    The output is shown in the following image:


Set up the main menu Now that our game has an interesting flyby background, we need a menu that allows the player to select some options. Because we are using iOS, we want the menu to have a touch interface where appropriate. First, though, we need to decide what options we want to present on the menu. We will put the following things on our main menu:

  • The title of our game

  • The URL of our website
  • An option to choose from various available player avatars
  • An option to practice playing
  • And an option to play the game

We have also decided to use GUITextures for our menu items, so that we can use any kind of graphics that we want to represent these options. It is often the case that we would choose to use icons rather than text to represent these choices. However, for our prototype, we are going to use text that could easily be replaced with icons before releasing the game.

Because we will be touching the menu items, we need to create an image that will be displayed both when the menu item is touched and when it is not touched. The following screenshot shows the textures that we will use:

The following screenshot shows an example of how they will be imported into Unity3D using the built-in Texture Importer, where the Texture Type has been set to GUI:

Since each menu item will have a touched and untouched image, we need to create both images. The following image shows the untouched image for the Play menu item:

The following image shows the touched Play menu item that will be used by our menu script to make the item pop, when the player touches it:

When we place the GUITextures in the Unity3D editor, we need to pick one of the standard iOS device layouts. A good choice to work with is the original iPhone (in wide mode for this particular game) because it is the smallest display both in terms of points and pixels. So if our GUI looks good on it, then it will look good on the other displays.

It is important to remember that the final placement of the textures will be altered using the script AAA_PlaceGUIItem, so that items are placed on the screen in a display resolution-independent manner. In other words, the placement in the Unity3D editor is for reference during the design of our menu, and we will use the pixel placement to calculate the fractional representation of the script.

The following image shows an example of how we have placed our menu items:

The following screenshot shows the script settings in the Unity3D editor for one of the menu items:

Once the menu GUITextures are placed, we need a script to display the correct texture based on a touch on the iOS screen. Conveniently, Unity3D will return a mouse down event when the player is touching the screen, so we can take advantage of that to create a script that will display the correct texture for both a mouse over and a touch. The script is shown as follows:

// Display one of the two textures
// This is an array of textures. The script
// expects there to be exactly 2 textures.
// The first texture is displayed when the
// player is not holding the mouse over or
// touching the menu item.
// The second texture is displayed when
// the player is holding the mouse over
// or touching the menu item
var textures : Texture[];
function Update ()
{
// For iOS devices, the player must have a finger
// on the screen, which Unity3D will also report as
// Mouse Button 0 being held down. So we can use
// that to display the second texture only if the
// iOS device screen is being touched
#if IPHONE_UNITY
if (Input.GetMouseButton(0))
{
#endif
// If there is touch, which Unity3D also reports
// as mousePosition, then display the correct
// texture
guiTexture.texture =
textures[guiTexture.HitTest(Input.mousePosition)];
// This is needed to complete the conditional if given above
#if IPHONE_UNITY
}
#endif
}

An example of AAA_Show Texture (Script) attached to a menu item game object is shown in the following screenshot. Notice that there we have two textures assigned to the Textures array in the inspector. This is done by setting the Size element of the Textures array to 2, and selecting the desired textures into Element 0 and Element 1, which is shown as follows:

Finally, we need a script to determine if a texture has been touched or clicked. The way we want this to work is that a click in the Unity3D editor, or a finger up on an iOS device, will be used to select a menu item.

This means that in the editor, when we mouse over a menu item, its second texture is displayed. Then when we click on it, the action will take place. Alternatively, on an iOS device, as we slide our finger on the display, the menu item will display its second texture, and when we lift our finger, the action will take place. The script that accomplishes both these tasks is shown as follows:

// This is the message that will be
// sent when the menu item is selected
// either by click in the editor or
// touch on the iOS device
var menuMessage : String;
// This is a variable that helps us
// identify if we are running in the
// editor or on a real device
enum myPlatform { Mobile, MacOSX, Windows }
// By default we assume the game is
// running on an iOS mobile device
private var currentPlatform = myPlatform.Mobile;
function Awake ()
{
// If the game is running in the OS X editor
// or as an OS X standalone deployment
// we consider it to be MacOSX
if ((Application.platform == RuntimePlatform.OSXEditor) ||
(Application.platform == RuntimePlatform.OSXPlayer))
{
currentPlatform = myPlatform.MacOSX;
}
// If the game is running in the Windows editor
// or as a Windows standalone deployment
// we consider it to be Windows
if ((Application.platform == RuntimePlatform.WindowsEditor) ||
(Application.platform == RuntimePlatform.WindowsPlayer))
{
currentPlatform = myPlatform.Windows;
}
}
function Update ()
{
// If the mouse button was clicked or the iOS
// device screen was touched during this frame
// the this will be true
var l_mbd : boolean = Input.GetMouseButtonDown (0);
// For iOS devices this will be the last touch
// event otherwise its always 0
var lastTouchEvent : int = 0;
// This block of code only runs on iOS devices
// since it has the touch interface
#if UNITY_IPHONE
// A temporary place to store the last
// touch event
var l_touch : Touch;
// If there was one or more touches
if (Input.touchCount)
{
// The last touch event is the only one
// we want to examine
l_touch = Input.GetTouch(Input.touchCount - 1);
// But we only want to know if the touch
// was not cancelled
if (l_touch.phase != TouchPhase.Canceled)
{
lastTouchEvent = l_touch.phase;
}
}
#endif
// This is the tricky part
// Basically in the editor a mouse click will be true
// and on an iOS device a finger up will be true
if ((currentPlatform != myPlatform.Mobile &&
l_mbd) ||
(currentPlatform == myPlatform.Mobile &&
lastTouchEvent &&
!Input.GetMouseButton(0)))
{
// So if there was a mouse click or a finger up
// and the mouse or finger is in the texture
// rectangle, then send the message
if (guiTexture.HitTest (Input.mousePosition))
{
SendMessage(menuMessage);
}
}
}
// These are the messages that are implemented
// for ALL the menu buttons
// They could be in separate scripts but for a simple
// menu system its fine to put them all here
// When the select menu is picked, load the
// scene to allow the player to select
// the player avatar
function selectMenu()
{
Application.LoadLevel("MenuSelect");
}

Because we have created scripts that allow player controls directly in the Unity3D editor, testing in the editor will be very easy for us to do.

The steps are straightforward. They are as follows:

  1. Enable the Stats option in the Game view.

  2. Under the Edit menu, select the Choose Graphics Emulation option and select the appropriate emulator for the device.
  3. Click the Play button.
  4. Repeat steps 1-5 for different devices.

      Enable the Stats option

    The Unity3D editor's Stats option, shown as follows, is a good first step in analyzing the performance of our game on iOS. While some of the stats are not useful in the editor, others, most notably the number of Draw Calls, are very useful in understanding how our application may perform when deployed on an actual device:

    One of the things we notice right away, however, is that the Statistics window is overlaying our game screen. This makes it difficult to see and can interfere with some of the game options that we may want to try.

      Choose Graphics Emulation

    The Unity3D editor with the Choose Graphics Emulation option, shown in the following screenshot, has the benefit of allowing us to test the functionality of any custom shaders in the editor, prior to deployment on an iOS device:

      Click the Play button and repeat

    It will always reduce our development time, if we can test in the Unity3D editor rather than deploying to a device. By developing games following these guidelines, we can dramatically reduce the amount of device debugging and speed up our time to market.

    Unity Remote Unity3D does include an application called Unity Remote, which allows you to remotely control the editor from a device. While we do occasionally use Unity Remote (for example, for a quick test of accelerometer functionality), we primarily test it in the editor and on the devices. While Unity Remote is a good idea, it is not something we have found that we really rely on during game development.

Summary In this article, we have covered the following:

  • The development of a complete and engaging menu system that allows our player to fly through our game environment, and experience a level that truly gives a feel of the large scale of our games' many levels

  • The ways in which we can manage the unique challenges and features of the different screen resolutions of iOS devices
  • The ways in which we can save time by continuing to develop a robust set of scripts that work both in the Unity3D editor and on iOS devices
  • How to configure Unity3D and deploy our game as a universal application that will run on all iOS devices


Unity iOS Essentials

Develop high performance, fun iOS games using Unity 3D with this book

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: