Sledgenet.co.uk Banner

Unity iPhone Quickstart: Mesh Pick

While being able to set up a user interface with GUI Textures is all well and good, there are times when you’ll want regular on-screen objects to respond to the tender caresses of your users. Determining which 3D mesh falls under a particular screen co-ordinate is known as “picking” and Unity has a set of commands specifically to help you out here. Hold on, though, ‘cos things are about to get interesting!

1: Prefabulous
As before, create a new project and don’t bother with the standard assets. Our intention is to create a set of cubes that will spin individually when tapped so, similarly to last time, let’s start by creating an object prefab.

In the Project View: Create>Prefab
From the menu: GameObject>Create Other>Cube

Drag the cube from the Hierarchy View and drop it on the new prefab, turning the prefab icon blue. Rename the new prefab ‘Koob’. Spelling stuff like a plonker is /_337, remember. Our original cube is now surplus to requirements, eliminate it (select it in the Hierarchy View and press Apple+Backspace).

2: Clonely Existence
Drag the Koob prefab to the Hierarchy View thrice, giving us three Koob instances. Rename them ‘Koob_01′, ‘Koob_02′ and ‘Koob_03′. Use the Transform component (in the Inspector View) to position them at the following X,Y and Z co-ordinates:

Koob_01: 0,0,0
Koob_02: 2,0,0
Koob_03: -2,0,0

While we’re moving things about, place the camera at 0,0,-4. Let’s also light the scene so that, when we get the cubes spinning, the effect will be more pleasing.

From the menu: GameObject>Create Other>Directional Light.

The position of a directional light is inconsequential, only its rotation matters: Use the light’s Transform component to set its pitch, yaw and roll to 0, 30 and 0 respectively.

It really is time for a screenshot, isn’t it!

3: Playing Tag
Now highlight the Koob prefab once more. In the top component of the Inspector View (called ‘Koob’) you will see the Tag drop-down menu, currently set to ‘Untagged’. Click it and select “Add new tag…”

You’ll now see the Tag Manager. Click to the right of “Element 0″ and type in our new, highly imaginative tag name “Koob”.

Flit back to the Koob prefab’s list of components (by clicking on the prefab in the Project View). This time, when you click the Tag drop-down menu, you’ll notice that ‘Koob’ has been added to the list. Set the prefab’s Tag to ‘Koob’ — now check out the three clones to see that they have automagically been set likewise. Joy!


4: Are You Are Missing Spinner?

Coding time!

Project View: Create>JavaScript

Rename the new script ‘Koob_AI’. Select the Koob prefab then drag ‘n’ drop Koob_AI on to its list of components. Double-click Koob_AI to bring up Unitron and edit the script thus:

var rotationSpeed = 0.0;

function Update () {
  rotationSpeed=rotationSpeed*.99;
  if (rotationSpeed<.01){rotationSpeed=0.0;}

  transform.Rotate(rotationSpeed*1,rotationSpeed*2,rotationSpeed*3);
}

As you can see, we are decreasing the object in question’s rotation speed each update, halting it altogether once it gets small enough. But hang on, there is no code here to kick the rotationSpeed variable nice and high! How are we gonna set things in motion?! Is the fact that we have left rotationSpeed public a clue?! So glad you asked…

5: We’ve Scene Everything Now

From the menu: GameObject>Create Empty.
Project View: Create>JavaScript

Rename the new GameObject to ‘Scene_Manager’; Rename the new script to ‘Scene_AI’; Highlight Scene_Manager then drag ‘n’ drop Scene_AI onto its list of components. Double-click Scene_AI to edit it, like-a-this:

private var koobArray:GameObject[];

function Awake(){
  koobArray = GameObject.FindGameObjectsWithTag("Koob");
}

function Update () {
  var currentKoob:GameObject;
  for (currentKoob in koobArray){
    var currentScript : Koob_AI = currentKoob.GetComponent(Koob_AI);
    currentScript.rotationSpeed = 2.0;
  }
}

I told you things were gonna get interesting! What’s going on here is that we’re being half sneaky and setting up an array which references each Koob in our Awake function (which is a special function that executes before the scene proper). We don’t want to be generating this collection every Update, so what we’ve got is, in effect, a look-up table.

Within the Update function itself we declare a Koob reference (called ‘currentKoob’) with which to iterate through the koobArray. In each iteration we update a further reference (called ‘currentScript’) so that it points to the instance of the Koob_AI component attached to the Koob currently under inspection. After that we set that particular script’s rotationSpeed variable to 2.0 and there’s our kick!

Four things of note:

  1. Because a script’s variables and functions act like members and methods in relation to the object to which it is attached, it is easy to assume that they can be accessed/invoked using straightforward dot syntax: ie “currentKoob.rotationSpeed = 2.0″; They CAN’T! (At least not under iPhone Unity.) Instead you must first establish a reference to the instance of the script you want to manipulate with GetComponent().
  2. If we had been fully sneaky the script references themselves would’ve been stored in a look-up table. Anything that ‘finds’ or ‘gets’ is best kept out of your update functions if possible. For the sake of illustration, because even doing the above trips everyone up at first, things are as they are. Optimise away!
  3. If we had made the rotationSpeed variable in the Koob_AI script private then we’d have generated an error trying to access it from within the Scene_AI script.
  4. This code just gets the cubes (sorry, koobs) spinning… there’s no interactive element. Go on, Build/Remote the project and look at ‘em go!

Now you’ve seen how objects and scripts can be iterated through and accessed from a separate script, we can add the final bit of code to add the interactive element.

6: Been Waitin’ For Your Touch
Okay, leap deftly back into the Scene_AI script and edit it so the entire thing looks just so:

private var koobArray:GameObject[];

function Awake(){
  koobArray = GameObject.FindGameObjectsWithTag("Koob");
}

function Update () {
  if(iPhoneInput.touchCount == 1 && iPhoneInput.GetTouch(0).phase == iPhoneTouchPhase.Began){
    var ray = Camera.main.ScreenPointToRay(iPhoneInput.GetTouch(0).position);
    var pickedObject : RaycastHit;

    if (Physics.Raycast (ray, pickedObject, 10)){
      print ("Ray "+ray);

      var currentKoob:GameObject;
      for (currentKoob in koobArray){
        if (pickedObject.collider==currentKoob.collider){
          var currentScript : Koob_AI = currentKoob.GetComponent(Koob_AI);
          currentScript.rotationSpeed = 2.0;
        }
      }
    }else{
      print("Ray null.");
    }
  }
}

You will see some familiar elements here and some new stuff associated with mesh picking. As is regular for 3D engines, a ray is cast into the scene from the camera’s perspective and the assigned variable (‘pickedObject’ in our case) either comes back null or referencing the object that was hit. At this point it might be dawning on you why we handle this from a Scene_Manager script: Ray casting can be expensive so you don’t want each object in your scene asking “Have I been hit? Cast a ray and tell me!” individually, especially not each object per touch! Rather we want to do the minimal number of ray-casts possible and compare the result universally. (Seeing as we can’t specify that any one object’s script’s Update() executes before all others’, we can’t have an autonomous “picking object” that is just reliably sitting there with the pick result each frame. The script that orders the mesh picking has to be the master of the scene.)

With the updated script saved, pop back into Unity and Build/Remote. Tap each cube and be amazed!

Cheers to: Pending
This article is due to be updated with API info:
BroadcastMessage

Comments are closed.