Holistic Game Development 2nd Ed C# Listings

Major differences between UnityScript (aka JavaScript in Unity) and C#:

  • For all C# code ensure a new C# Script is created instead of JavaScript. The C# file will have a .cs on the end.
  • C# code will be contained within a class. It doesn’t matter what you call the class except that the name should be meaningful. The class code contains the code inside it whereas JavaScript doesn’t require the container.
  • The filename of the .cs file should be EXACTLY the same as the class name. E.g. if the class is:
  • 1
    2
    3
    4
    public class spin : MonoBehaviour
    {
        //your code
    }

    Then the filename should be spin.cs.

    Chapter 1

    Step 16 p. 25

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class spin : MonoBehaviour {

        // Use this for initialization
        void Start () {
           
        }
       
        // Update is called once per frame
        void Update () {
            transform.Rotate(Vector3.up * 10);
        }
    }

    Listing 1.7 A short program with comments

    1
    2
    3
    4
    5
    // Update is called once per frame
    void Update () {
        //rotate around the vertical axis with a speed of 10
        transform.Rotate(Vector3.up * 10);
    }

    Section 1.5.3 Functions

    1
    2
    document.write(“Hello Unity”); in JavaScript
    Debug.Log(“Hello Unity”); in C#

    Listing 1.8 Code syntax for an empty function.

    1
    2
    3
    void myFunctionName(input list)
    {
    }

    Listing 1.9 An example of declaring variables of differing types.
    (Note in C# a float with a decimal place requires a ‘f’ on the end of the value.

    1
    2
    3
    4
    5
    int x = 10; //an integer called x with the value 10
    float y = 5.6f; //a float called y with the value 5.6 v
    bool isSpinning = true; //a Boolean value set to true
    char ch = ‘a’; // a character called ch with a value ‘a’
    string myName = “Penny”; // a string called myName with the value Penny

    Immediately below Listing 1.9

    1
    var x: int; //in JavaScript

    is equivalent to using

    1
    int x; //in C#

    Listing 1.10 JavaScript to Change the x Axis Scale of a GameObject on Game Start

    1
    2
    3
    4
    5
    6
    private float objScaleX = 0.5f;

    void Start ()
    {
        transform.localScale = new Vector3 (objScaleX, transform.localScale.y, transform.localScale.z);
    }

    Listing 1.11 A Script to Modify X, Y, and Z Scales of a Game Object

    1
    2
    3
    4
    5
    6
    7
    8
    float objScaleX = 2.0f;
    float objScaleY = 0.2f;
    float objScaleZ = 0.5f;

    void Start ()
    {
        transform.localScale = new Vector3 (objScaleX, objScaleY, objScaleZ);
    }

    Listing 1.12 Script to Grow a Game Object by 0.05 in Scale in Each Game Update

    1
    2
    3
    4
    5
    float growthRate = 0.05f;
    void Update ()
    {
        transform.localScale = new Vector3 (objScaleX + growthRate, objScaleY + growthRate, objScaleZ + growthRate);
    }

    Listing 1.13 A Script to Move a Game Object in a Circle

    1
    2
    3
    4
    5
    float radius = 5;
    void Update ()
    {
        transform.position = new Vector3(radius * Mathf.Sin(Time.fixedTime), radius * Mathf.Cos(Time.fixedTime), 0);
    }

    Listing 1.14 An if-else Statement

    C# syntax is exactly the same as JavaScript

    Listing 1.15 An Example Script Using an if-else Statement

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int x = 5;
    int y = 10;
    if( x > y)
    {
        Debug.Log(“X is greater than Y”);
    }
    else
    {
        Debug.Log(“Y is greater than or equal to X”);
    }

    Listing 1.16 Making a Game Object Fall to a Certain Position and Then Stop

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    float speed = 0.1f;
    int groundLevel = -4;
    void Update ()
    {
        if(transform.position.y > groundLevel)
        {
            //keep moving down
            transform.Translate(0,0,-speed);
        }
    }

    Listing 1.17 A Script to Make a Game Object Fall Down to a Certain Height and Then Grow

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    float speed = 0.1f;
    float growthRate = 0.01f;
    float groundLevel = -4;
    void Update ()
    {
        if(transform.position.y > groundLevel)
        {
            //keep moving down
            transform.Translate(0,0, -speed);
        }
        else
        {
            transform.localScale = new Vector3(transform.localScale.x + growthRate, 1,1);
        }
    }

    Listing 1.18 JavaScript for Printing Numbers between 1 and 5

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int i = 1;
    Debug.Log(i);
    i = i + 1;
    Debug.Log(i);
    i = i + 1;
    Debug.Log(i);
    i = i + 1;
    Debug.Log(i);
    i = i + 1;
    Debug.Log(i);
    i = i + 1;

    Listing 1.19 A for Loop

    C# is exactly the same as JavaScript

    Listing 1.20 A for Loop to Print out Numbers between 1 and 5

    1
    2
    3
    4
    for( int i = 1; i < = 9; i++)
    {
        Debug.Log(i);
    }

    Listing 1.21 Creating a Stack of Cubes with a for Loop

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void Start ()
    {
        GameObject aP;
        for(int i = 1; i <= 9; i++)
        {
            aP = GameObject. CreatePrimitive(PrimitiveType. Cube);
            aP.transform.position = new Vector3(0, i, 0);
        }
    }

    Listing 1.22 A Script That Creates One Column and One Row of Cubes

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void Start ()
    {
        GameObject aP;
        for(int i = 1; i < = 9; i++)
        {
            aP = GameObject.CreatePrimitive(PrimitiveType.Cube);
            aP.transform.position = new Vector3(0, i * 2, 0);
        }
        for(int j = 1; j <= 9; j++)
        {
            aP = GameObject.CreatePrimitive(PrimitiveType.Cube);
            aP.transform.position.x = new Vector3(j*2, 0, 0);
        }
    }

    Listing 1.23 A Matrix of Cubes Created Entirely with Script

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void Start ()
    {
        GameObject aP;
        int numRows= 9;
        int numCols = 9;
        for(int row = 1; row < = numRows; row++)
        {
            for(int col = 1; col < = numCols; col++)
            {
                aP = GameObject.CreatePrimitive(PrimitiveType.Cube);
                aP.transform.position.x = new Vector3(col*2, row*2, 0);
            }
        }
    }

    Listing 1.24 Storing Game Objects in an Array

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void Start ()
    {
        GameObject[] aP = new GameObject[9];
        for(int i = 0 ; i < 9 ; i++)
        {
            aP[i] = GameObject.CreatePrimitive(PrimitiveType.Cube);
            aP[i].transform.position = new Vector3(0,i + 1,0);
        }
    }

    Listing 1.25 Setting an Array of Cubes to the Color Red

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class CubeArray : MonoBehaviour {

        GameObject[] aP = new GameObject[9];
        void Start ()
        {
            for(int i = 0; i < 9; i++)
            {
                aP[i] = GameObject.CreatePrimitive(PrimitiveType.Cube);
                aP[i].transform.position = new Vector3(0,i + 1,0);
            }
        }

        void Update ()
        {
            for(int i = 0; i < 9; i++)
            {
                aP[i].GetComponent<Renderer>().material.color = Color.red;
            }
        }
    }

    Listing 1.26 Script to Change Cubes to Random Colors in Each Main Loop

    1
    2
    3
    4
    5
    6
    7
    void Update ()
    {
        for(int i = 0; i < 9; i++)
        {
            aP[i].GetComponent<Renderer>().material.color = new Color(Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f), Random.Range(0.0f,1.0f));
        }
    }

    Listing 1.27 Changing the Location of a Game Object with Script

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ChangeLocation : MonoBehaviour {
        GameObject gameObj;
        void Start()
        {
            gameObj = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            gameObj.transform.position = new Vector3(1,5,2);
        }
    }

    Listing 1.28 Adding a Rigidbody to a Game Object Using Script

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ChangeLocation : MonoBehaviour {
        GameObject gameObj;
        void Start()
        {
            gameObj = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            gameObj.transform.position = new Vector3(1,5,2);
            gameObj.AddComponent<Rigidbody>();
        }
    }

    Listing 1.29 Modifying the Physics Material of a Game Object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ChangeLocation : MonoBehaviour {
        GameObject gameObj;
        void Start()
        {
            gameObj = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            gameObj.transform.position = new Vector3(1,5,2);
            gameObj.AddComponent<Rigidbody>();
            PhysicMaterial material = new PhysicMaterial();
            material.bounciness = 1;
            gameObj.GetComponent<Collider>().material = material;
        }
    }

    Listing 1.30 Script to modify Properties of a Game Object to Which it is attached

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ChangeLocation : MonoBehaviour {

        Color myColor = Color.red;
        void Start()
        {
            this.gameObject.AddComponent<Rigidbody>();
            PhysicMaterial material = new PhysicMaterial();
            material.bounciness = 0.5f;
            this.GetComponent<Collider>().material = material;
            this.collider.material = material;
            this.GetComponent<Renderer>().material.color = myColor;
            this.renderer.material.color = myColor;
        }
    }

    Listing 1.31 Exposing a Variable for Bounciness

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class bouncy : MonoBehaviour {

        Color myColor = Color.red;
        public float bouncyAmount = 0.5f;
        void Start()
        {
            this.gameObject.AddComponent<Rigidbody>();
            PhysicMaterial material = new PhysicMaterial();
            material.bounciness = bouncyAmount;
            this.GetComponent<Collider>().material = material;
            this.GetComponent<Collider>().material = material;
            this.GetComponent<Renderer>().material.color = myColor;
            this.GetComponent<Renderer>().material.color = myColor;
        }
    }

    Chapter 2

    Listing 2.1 Script to Make a Game Object Move in a Circular Path Around Its Starting Position

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class orbit : MonoBehaviour {

        float radius = 30;
        Vector3 startPosition;
        int speed = 3;

        void Start() {
            startPosition = transform.position;
        }

        void Update ()
        {
            transform.position = new Vector3(radius * Mathf.Sin(Time.fixedTime * speed) +
                startPosition.x,
                transform.position.y,
                radius * Mathf.Cos(Time.fixedTime * speed) +
                startPosition.z);
        }

    }

    Section 2.3.3 Translation, Rotation and Scaling

    The difference between JavaScript and C# in this section is that C# cannot set x, y and z values separately as JavaScript allows.

    1
    2
    3
    this.transform.position.x += 3;
    this.transform.position.y += 5;
    this.transform.position.z += 8;

    Must be written in one line as:

    1
    this.transform.Translate(3,5,8);

    The same applies for setting localScale and rotation.

    Section 2.3.4 Polygons and Normals

    Hands-On Meshes and Normals

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    //CrumpleMesh.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class CrumpleMesh : MonoBehaviour {

        public float scale = 1.0f;
        public float speed = 0.5f;
        public bool recalculateNormals = false;

        Vector3[] baseVertices;
        Perlin noise;

        void Start ()
        {
            noise = new Perlin ();
        }

        void Update () {
            Mesh mesh = GetComponent<MeshFilter>().mesh;
           
            if (baseVertices == null)
            baseVertices = mesh.vertices;

            Vector3[] vertices = new Vector3[baseVertices.Length];
           
            float timex = Time.time * speed + 0.1365143f;
            float timey = Time.time * speed + 1.21688f;
            float timez = Time.time * speed + 2.5564f;
            for (int i=0;i<vertices.Length;i++)
            {
                Vector3 vertex = baseVertices[i];

                vertex.x += noise.Noise(timex + vertex.x,
                    timex + vertex.y,
                    timex + vertex.z) * scale;
                vertex.y += noise.Noise(timey + vertex.x, timey +
                    vertex.y,
                    timey + vertex.z) * scale;
                vertex.z += noise.Noise(timez + vertex.x,
                    timez + vertex.y,
                    timez + vertex.z) * scale;
               
                vertices[i] = vertex;
            }
           
            mesh.vertices = vertices;
           
            if (recalculateNormals)
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();
        }
    }

    Section 2.4 Two-Dimensional Games in a 3D Game Engine

    Listing 2.2 Script to make one game object face another

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //Attack.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Attack : MonoBehaviour {

        public GameObject target;
        public float turnSpeed = 5.0f;

        void LookAt2D(Vector3 targetPos)
        {  
            Vector3 dir = targetPos - this.transform.position;  
            float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90;
            Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);  
            this.transform.rotation = Quaternion.Slerp(transform.rotation, q,
                Time.deltaTime * turnSpeed);
        }

        void Update ()
        {  
            LookAt2D(target.transform.position);
        }
    }

    Listing 2.3 Script to make an object move toward its target.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Attack : MonoBehaviour {

        public GameObject target;
        public float turnSpeed = 5.0f;
        public float flightSpeed = 0.3f;

        void LookAt2D(Vector3 targetPos)
        {  
            Vector3 dir = targetPos - this.transform.position;  
            float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90;
            Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);  
            this.transform.rotation = Quaternion.Slerp(transform.rotation, q,
                Time.deltaTime * turnSpeed);
        }

        void Update ()
        {  
            LookAt2D(target.transform.position);
            this.transform.Translate(Vector3.up * flightSpeed);
        }
    }

    Listing 2.4 Testing rocket’s distance to planet

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Attack : MonoBehaviour {

        public GameObject target;
        public float turnSpeed = 5.0f;
        public float flightSpeed = 0.3f;

        float distanceToTarget;
        string state = "ATTACK";

        void LookAt2D(Vector3 targetPos)
        {  
            Vector3 dir = targetPos - this.transform.position;  
            float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90;
            Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);  
            this.transform.rotation = Quaternion.Slerp(transform.rotation, q,
                Time.deltaTime * turnSpeed);
        }

        void Update ()
        {  
            distanceToTarget = (target.transform.position -
                this.transform.position).magnitude;
            if(distanceToTarget > 10)  
            {    
                state = "ATTACK";  
            }  
            else if (distanceToTarget < 2)  
            {    
                state = "RETREAT";  
            }  
            if(state == "ATTACK")  
            {    
                LookAt2D(target.transform.position);    
                this.transform.Translate(Vector3.up * flightSpeed);  
            }  
            else  
            {    
                this.transform.Translate(Vector3.up * flightSpeed);  
            }
        }
    }

    Listing 2.5 MoveBullet.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class MoveBullet : MonoBehaviour {

        float speed = 0.1f;

        void OnBecameInvisible()
        {
            Destroy(this.gameObject);
        }

        void Update ()
        {
            this.transform.Translate(Vector3.up * speed);
        }
    }

    Listing 2.6 Giving the script access to the bullet prefab

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Attack : MonoBehaviour {

    ...
    public float flightSpeed = 0.3f;

    public GameObject bullet;

    ...

    void Update ()
    {  
        Instantiate(bullet, this.transform.position, this.transform.rotation);
        distanceToTarget = (target.transform.position -
            this.transform.position).magnitude;
        if(distanceToTarget > 10)  
        ...

    Listing 2.7 Controlling the shooting based on the rocket’s facing direction.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
        ...
        void Update ()
        {  
            //MOVE INSTANTIATE LINE FROM HERE TO BELOW
            distanceToTarget = (target.transform.position -
                this.transform.position).magnitude;
            if(distanceToTarget > 10)  
            {    
                state = "ATTACK";  
            }  
            else if (distanceToTarget < 2)  
            {    
                state = "RETREAT";  
            }  
            if(state == "ATTACK")  
            {    
                LookAt2D(target.transform.position);    
                this.transform.Translate(Vector3.up * flightSpeed);
                    Vector3 vectorToTarget = target.transform.position -
                this.transform.position;    
                if(Vector3.Angle(vectorToTarget, this.transform.up) < 30)
                {
                    Instantiate(bullet, this.transform.position,
                        this.transform.rotation);
                }

            }  
            else
    ...

    Listing 2.8 Adding 2D collision detection function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class MoveBullet : MonoBehaviour {

        float speed = 0.1f;
        public GameObject explosion;

        void OnCollisionEnter2D(Collision2D collisionObj)
        {
            if (collisionObj.gameObject.name == "Earth")
            {
                Instantiate(explosion,this.transform.position, this.transform.rotation);
            }
        }

        void OnBecameInvisible()
        {
            Destroy(this.gameObject);
        }

        void Update ()
        {
            this.transform.Translate(Vector3.up * speed);
        }
    }

    Section 2.5.1 The Law of Gravity

    Equation 2.4

    1
    transform.Translate(0,0,-1);

    Listing 2.9 Script to create a sphere as a projectile on a left mouse click
    Fire.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Fire : MonoBehaviour {

        void Update ()
        {
            if(Input.GetButtonDown("Fire1"))
            {
                Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
                GameObject sphere =
                GameObject.
                CreatePrimitive(PrimitiveType.Sphere);
                sphere.transform.position = ray.origin;
                sphere.AddComponent<Rigidbody>();
                sphere.GetComponent<Rigidbody>().AddForce(ray.direction * 1000);
            }
        }
    }

    Listing 2.10 Script to change the mass of a Rigidbody
    Fire.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void Update ()
    {
        if(Input.GetButtonDown("Fire1"))
        {
            ...
            sphere.AddComponent<Rigidbody>();
            sphere.GetComponent<Rigidbody>().mass = 10;
            sphere.GetComponent<Rigidbody>().AddForce(ray.direction * 1000);
        }
    }

    Listing 2.11 Instantiating a game object and shooting it forward relative to the creator.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //Fire.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Fire : MonoBehaviour {

        public GameObject bulletObject;
        void Update ()
        {
            if(Input.GetButtonDown("Fire1"))
            {
                GameObject newBullet = Instantiate(bulletObject,
                    this.transform.position,
                    this.transform.rotation);
                newBullet.GetComponent<Rigidbody>().AddForce(
                    this.transform.forward * 500);
            }
        }
    }

    Listing 2.12 Turn off an object’s physics on a collision.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //Blob.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Blob : MonoBehaviour {

        void OnCollisionEnter(Collision collision)
        {
            if(collision.gameObject.name !=
                "First Person Controller")
            {
                GetComponent<Rigidbody>().isKinematic = true;
            }
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 2.13 Causing a squash effect by changing an object’s scale

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Blob : MonoBehaviour {

        void OnCollisionEnter(Collision collision)
        {
            if(collision.gameObject.name !=
                "First Person Controller")
            {
                GetComponent<Rigidbody>().isKinematic = true;
                Destroy (this.GetComponent<Collider>());
                ContactPoint contact = collision.contacts[0];
                Quaternion rot = Quaternion.FromToRotation(Vector3.up,
                    contact.normal);
                this.transform.position = contact.point;
                this.transform.rotation = rot;
                this.transform.localScale = new Vector3(
                    this.transform.localScale.x *
                    collision.relativeVelocity. magnitude/5.0f,
                    this.transform.localScale.y * 0.2f,
                    this.transform.localScale.z *
                    collision.relativeVelocity.magnitude/5.0f);
            }
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 2.14 Creating a timed explosion

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //Blob.cs
    public class Blob : MonoBehaviour {

        public GameObject exp;
        float timeToExplode = 5.0f;

        void Explosion()
        {
            Instantiate(exp, this.transform.position, this.transform.rotation);
            Destroy(this.gameObject,1);
        }

        void OnCollisionEnter(Collision collision)
        {
            if(collision.gameObject.name !=
                "First Person Controller")
            {
                ...
                this.transform.localScale = new Vector3(
                    this.transform.localScale.x *
                    collision.relativeVelocity. magnitude/5.0f,
                    this.transform.localScale.y * 0.2f,
                    this.transform.localScale.z *
                    collision.relativeVelocity.magnitude/5.0f);

                Invoke("Explode", timeToExplode);
            }
        }
        ...

    Listing 2.15 Destroying another object in an explosion.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //Blob.cs
    public class Blob : MonoBehaviour {

        public GameObject exp;
        float timeToExplode = 5.0f;
        GameObject stuckOn;

        void Explosion()
        {
            Instantiate(exp, this.transform.position, this.transform.rotation);
            if(stuckOn && stuckOn.name == "Door")
            {
                Destroy(stuckOn);
            }

            Destroy(this.gameObject,1);
        }

        void OnCollisionEnter(Collision collision)
        {
            if(collision.gameObject.name !=
                "First Person Controller")
            {
                stuckOn = collision.gameObject;
                GetComponent<Rigidbody>().isKinematic = true;
                Destroy (this.GetComponent<Collider>());
                ...

    Listing 2.17 LOD Code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    //LODManager.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class LODManager : MonoBehaviour {

        public Mesh lod1;
        public Mesh lod2;
        public float lodDist1 = 80;
        public float lodDist2 = 120;
        public float updateInterval = 1.0f;
        public int currentLOD = 1;
        MeshFilter meshFilter;
        Transform thisTransform;

        void Awake()
        {
            meshFilter = GetComponent<MeshFilter>() as MeshFilter;
            thisTransform = transform;
            float startIn = Random.Range(0.0f, updateInterval);
            InvokeRepeating("UpdateLOD",startIn,updateInterval);
        }

        void UpdateLOD()
        {
            float distanceFromObject = Vector3.Distance(this.transform.position,
                Camera.main.transform.position);
            if (distanceFromObject < lodDist1)
            {
                if(currentLOD != 1)
                {
                    currentLOD = 1;
                    meshFilter.mesh = lod1;
                }
            }
            else if (distanceFromObject < lodDist2)
            {
                if(currentLOD != 2)
                {
                    currentLOD = 2;
                    meshFilter.mesh = lod2;
                }
            }
            else
            {
                currentLOD = 0;
                meshFilter.mesh = null;
            }
        }
    }

    Listing 2.18 A quick and easy script to create a virtual city from one building object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //InstantCity.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class InstantCity : MonoBehaviour {

        public GameObject building;
        void Start()
        {
            for(int i = 0; i < 20; i++)
            {
                for(int j = 0; j < 20; j++)
                {
                    Vector3 newPos =
                    new Vector3(
                        i*Random.Range(100,200),0,
                        j*Random.Range(100,200));
                    Instantiate(building, newPos,Quaternion.identity);
                }
            }
        }
    }

    Listing 2.19 Scrolling a Texture over the Surface of an Object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //Scrolling.cs     
    using UnityEngine;
    using System.Collections;

    public class scrolling : MonoBehaviour {
        Vector2 uvSpeed = new Vector2( 0.0f, -1.0f );
        Vector2 uvOffset = Vector2.zero;
       
        void LateUpdate()
        {
            uvOffset += ( uvSpeed * Time.deltaTime );
            this.GetComponent<Renderer>().materials[0].
            SetTextureOffset("_MainTex", uvOffset);                
        }
    }

    Listing 2.20 Script to Create a Billboard from a Quad

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //FaceCamera.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class FaceCamera : MonoBehaviour {

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

        // Update is called once per frame
        void Update () {
            //note the minus in here as a Unity Quad has it’s Z axis facing in the opposite
            //direction to its textured side.
            this.transform.LookAt(-Camera.main.transform.position);
        }
    }

    Listing 2.21 Billboarding script that allows the x rotation to be turned off

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class FaceCamera : MonoBehaviour {

        public bool stayUpright = true;
        // Use this for initialization
        void Start () {
           
        }

        // Update is called once per frame
        void Update () {
            this.transform.LookAt(-Camera.main.transform.position);
            if(stayUpright)
            this.transform.eulerAngles = new Vector3(0,
                this.transform.eulerAngles.y,
                this.transform.eulerAngles.z);
        }
    }

    Chapter 3

    Listing 3.1 Code to scroll a texture over the surface of a mesh.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //Scrolling.cs     
    using UnityEngine;
    using System.Collections;

    public class scrolling : MonoBehaviour {
        Vector2 uvSpeed = new Vector2( 0.0f, -1.0f );
        Vector2 uvOffset = Vector2.zero;

        void LateUpdate()
        {
            uvOffset += ( uvSpeed * Time.deltaTime );
            this.GetComponent<Renderer>().materials[0].
                SetTextureOffset("_MainTex", uvOffset);                
        }
    }

    Listing 3.2 Code to scroll a texture over the surface of a mesh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //Walk.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Walk : MonoBehaviour {

        public float speed = 0.1f;
        // Use this for initialization
        void Start () {
           
        }

        // Update is called once per frame
        void Update () {
            if(Input.GetKey("right"))
            {
                this.transform.Translate(speed,0,0);
            }
           
            if(Input.GetKey("left"))
            {
                this.transform.Translate(-speed,0,0);
            }
        }
    }

    Listing 3.3 Script to Control a Model’s Movement and Animation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    //AnimControls.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class AnimControls : MonoBehaviour {

        Animator anim;
        float speed = 0.05f;
        float rotSpeed = 50.0f;

        // Use this for initialization
        void Start () {
            anim = gameObject.GetComponent<Animator>();
        }

        // Update is called once per frame
        void Update () {
            if(Input.GetKey("up"))
            {
                anim.Play("walking",0);
                this.transform.position += this.transform.forward * speed;
            }
            else if(Input.GetKey("down"))
            {
                anim.Play("walking",0);
                this.transform.position -= this.transform.forward * speed;
            }
            else if(Input.GetKeyUp("up") || Input.GetKeyUp("down"))
            {
                anim.Play("idle",0);
            }

        }
    }

    Listing 3.4 Updated AnimControls

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    //AnimControls.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class AnimControls : MonoBehaviour {

        Animator anim;
        float speed = 0.05f;
        float rotSpeed = 50.0f;

        // Use this for initialization
        void Start () {
            anim = gameObject.GetComponent<Animator>();
        }

        // Update is called once per frame
        void Update () {
            if(Input.GetKey("up"))
            {
                anim.SetBool("amWalking",true);
                this.transform.position += this.transform.forward * speed;
            }
            else if(Input.GetKey("down"))
            {
                anim.SetBool("amWalking",true);
                this.transform.position -= this.transform.forward * speed;
            }
            else if(Input.GetKeyUp("up") || Input.GetKeyUp("down"))
            {
                anim.SetBool("amWalking",false);
            }

        }                  
    }

    Listing 3.5 Updated AnimControls

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //AnimControls.cs
    ...
    float rotSpeed = 50.0f;

    ...

    // Update is called once per frame
    void Update () {
        ...

        if(Input.GetKey("left"))
        {
            transform.Rotate(-Vector3.up * Time.deltaTime * rotSpeed);
        }
        else if(Input.GetKey("right"))
        {
            transform.Rotate(Vector3.up * Time.deltaTime * rotSpeed);
        }
    }

    Listing 3.6 Controlling the movement of a sprite with the arrow keys

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    //SpriteManagement.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class SpriteManagement : MonoBehaviour {

        float speed = 0.01f;

        // Update is called once per frame
        void Update () {

            if(Input.GetKey (KeyCode.UpArrow))
            {  
                this.transform.Translate(0,speed,0);
            }
            else if(Input.GetKey (KeyCode.DownArrow))
            {
                this.transform.Translate(0,-speed,0);
            }
            else if(Input.GetKey (KeyCode.LeftArrow))
            {
                this.transform.Translate(-speed,0,0);
            }
            else if(Input.GetKey (KeyCode.RightArrow))
            {
                this.transform.Translate(speed,0,0);
            }
        }
    }

    Listing 3.7 Creating an animation array.

    1
    2
    3
    4
    5
    6
    7
    8
    public class SpriteManagement : MonoBehaviour {

    float speed = 0.01f;
    public GameObject[] anims;

    // Update is called once per frame
    void Update () {
    ...

    Listing 3.8 Creating class structures for strong values

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    // Updated SpriteManagement.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class SpriteManagement : MonoBehaviour {

        float speed = 0.01f;
        public GameObject[] anims;
        int currentAnim = -1;

        // Use this for initialization
        void Start () {
            TurnAllAnimationsOff();
        }

        void TurnAllAnimationsOff()
        {
            foreach(GameObject i in anims)
            {
                i.SetActive(false);
            }
        }

        // Update is called once per frame
        void Update () {
            if(currentAnim != -1)
            //if there is a current animation set its speed to 1
            anims[currentAnim].GetComponent<Animator>().speed = 1;

            if(Input.GetKey (KeyCode.UpArrow))
            {
                //before turning on this animation
                //turn off the previous if it is a different one
                if(currentAnim != 0 && currentAnim != -1)
                    anims[currentAnim].SetActive(false);
               
          //move the character
          this.transform.Translate(0,speed,0);

            //make the appropriate sprite active and start playing
            anims[0].SetActive(true);

                //update the number of the current animation
                currentAnim = 0;
            }
            else if(Input.GetKey (KeyCode.DownArrow))
            {
                if(currentAnim != 1 && currentAnim != -1)
                anims[currentAnim].SetActive(false);
                this.transform.Translate(0,-speed,0);
                anims[1].SetActive(true);
                currentAnim = 1;
            }
            else if(Input.GetKey (KeyCode.LeftArrow))
            {
                if(currentAnim != 2 && currentAnim != -1)
                anims[currentAnim].SetActive(false);
                this.transform.Translate(-speed,0,0);
                anims[2].SetActive(true);
                currentAnim = 2;
            }
            else if(Input.GetKey (KeyCode.RightArrow))
            {
                if(currentAnim != 3 && currentAnim != -1)
                anims[currentAnim].SetActive(false);
                this.transform.Translate(speed,0,0);
                anims[3].SetActive(true);
                currentAnim = 3;
            }
            else
            {
                //if there is no key being pressed, set the current animation speed to 0
                //in otherwords, pause it.
                if(currentAnim != -1)
                anims[currentAnim].GetComponent<Animator>().speed = 0;
            }
        }
    }

    Chapter 4

    Listing 4.1 Code to destroy an object when it moves out of the camera’s view.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class DestroyWhenGone : MonoBehaviour {

        void OnBecameInvisible()
        {
            DestroyImmediate(this.gameObject); 
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.2 Spawning Objects at Random Intervals

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //Spawn.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Spawn : MonoBehaviour {

        public GameObject ball;
        public Material[] materialArray;

        // Update is called once per frame
        void Update () {
            if(Random.Range(0,200) < 1)
            {
                GameObject sphere = Instantiate(ball, new Vector3(0,5,0),
                    Quaternion.identity);
            }
        }
    }

    Listing 4.3 Setting a random value for the position of game objects.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //Spawn.cs
    // Update is called once per frame
    void Update () {
        if(Random.Range(0,200) < 1)
        {
            Vector3 ballStartPosition = new Vector3(0,8,Random.Range(-4,4));
            GameObject sphere = Instantiate(ball,ballStartPosition,
                Quaternion.identity);
        }
    }

    Listing 4.4 Coloring a game object using an array of materials.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //Spawn.cs
    void Update () {
        if(Random.Range(0,200) < 1)
        {
            Vector3 ballStartPosition = new Vector3(0,8,Random.Range(-4,4));
            GameObject sphere = Instantiate(ball,ballStartPosition,
                Quaternion.identity);

            sphere.GetComponent<Renderer>().material =
            materialArray[Random.Range(0,materialArray.Length)];
            sphere.GetComponent<Renderer>().material.shader =
            Shader.Find("Diffuse");
        }
    }

    Listing 4.5 Code to convert the mouse position on the screen to viewport and world coordinates.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //DisplayMousePos.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class DisplayMousePos : MonoBehaviour {

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

        // Update is called once per frame
        void Update () {
            Debug.Log("Screen to Viewport " +
                Camera.main.ScreenToViewportPoint(Input.mousePosition));
            Debug.Log("Screen to World " +
                Camera.main.ScreenToWorldPoint(Input.mousePosition));
        }
    }

    Listing 4.6 Creating a draggable object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    //ClickNDrag.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ClickNDrag : MonoBehaviour {

        GameObject focusObj = null;
        Vector3 lastMousePosition;
        Vector3 deltaMousePosition;
        float mouseSensitivity = 1000.0f;

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

        // Update is called once per frame
        void Update () {
            if( Input.GetButtonDown("Fire1"))
            {
                focusObj = null;
                //cast a ray
                Ray ray = Camera.main.ScreenPointToRay
                (Input.mousePosition);
                RaycastHit hit;
               
                //if the ray hits an object and that object is a ball
                if(Physics.Raycast (ray, out hit, 100) &&
                    hit.transform.gameObject.tag == "ball")
                {
                    //take hold of it with the focusObj variable
                    focusObj = hit.transform.gameObject;
                   
                    //remember the location of the mouse
                    lastMousePosition = Input.mousePosition;
                }
            }

            //if the fire button is down
            if( focusObj && Input.GetButton("Fire1"))
            {
                //calculate how far the the mouse has moved across the screen
                deltaMousePosition = Input.mousePosition - lastMousePosition;

                //use this value to set the position of the object
                focusObj.transform.Translate(0,0,
                    deltaMousePosition.x/mouseSensitivity);
               
                //remember the location of the mouse
                lastMousePosition = Input.mousePosition;
            }
           
            //if the fire button is released
            if( focusObj && Input.GetButtonUp("Fire1"))
            {
                //forget about the object that was clicked on
                focusObj = null;
            }
        }
    }

    Listing 4.7 Script to register a collision on a triggering collider.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //StopBalls.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class StopBalls : MonoBehaviour {

        void OnTriggerEnter (Collider obj)
        {
            obj.gameObject.tag = "Untagged";   
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.8 Code to test the colour of a game object against a variable value.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //CountBalls.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class CountBalls : MonoBehaviour {

        public Material colorMatch;
        public int score = 0;
        void OnTriggerEnter (Collider obj)
        {
            if(obj.gameObject.GetComponent<Renderer>().material.color ==
                colorMatch.color)
            {
                score = score + 1; 
            }
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.9 Gathering scores from each box for a single score.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    //Score.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Score : MonoBehaviour {

        public int score = 0;
        public GUIStyle scoreStyle;
        public GameObject[] scoringObjects;
        int maxBalls = 10;

        void OnGUI ()
        {
            score = 0;
            for(var i = 0; i < scoringObjects.Length; i++)
            {
                //extract the score from the object and tally
                score += scoringObjects[i].GetComponent<CountBalls>().score;   
            }


            GUI.BeginGroup (new Rect (Screen.width - 85, 5, 80, 80));
            GUI.Box (new Rect (0,0,80,60), "Score");
            GUI.Label (new Rect (0, 20, 80, 30), string.Format("{0:0}", score), scoreStyle);
            GUI.EndGroup ();


            int totalBalls =
            scoringObjects[0].GetComponent<CountBalls>().score +
            scoringObjects[1].GetComponent<CountBalls>().score +
            scoringObjects[2].GetComponent<CountBalls>().score +           
            scoringObjects[3].GetComponent<CountBalls>().score;

            if(totalBalls >= maxBalls)
            {
                PlayerPrefs.SetInt("LastScore",score);
                Application.LoadLevel("gameover");
            }

        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.10 Setting player preferences in code.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //MainMenu.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class MainMenu : MonoBehaviour {

        void OnGUI ()
        {
            // Make a group on the center of the screen
            GUI.BeginGroup (new Rect (Screen.width / 2 - 50, Screen.height / 2 - 60,
                100, 120));
            // All rectangles are now adjusted to the group. (0,0)
            //is the topleft corner of the group.

            // We'll make a box so you can see where the group is on-screen.
            GUI.Box (new Rect (0,0,100,120), "Main Menu");
            if(GUI.Button (new Rect (10,40,85,30), "Play Level 1"))
            {
                PlayerPrefs.SetInt("Level", 1);
                Application.LoadLevel("maingame"); 
            }
           
            if(GUI.Button (new Rect (10,80,85,30), "Play Level 2"))
            {
                PlayerPrefs.SetInt("Level", 2);
                Application.LoadLevel("maingame"); 
            }


            // End the group we started above. This is very important to remember!
            GUI.EndGroup ();
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.11 A start function that loads the player preference and uses it to set the difficulty of the game.
    To Spawn.cs add the Start function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Use this for initialization
    void Start () {
        int playerlevel = PlayerPrefs.GetInt("Level");
        if(playerlevel == 1)
        {
            GameObject.Find("Cutoff").transform.position = new Vector3(0,1,0);
        }
        else if(playerlevel == 2)
        {
            GameObject.Find("Cutoff").transform.position = new Vector3(0,3,0);
        }
    }

    Listing 4.12 Displaying a game over screen with the score.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //ShowScore.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ShowScore : MonoBehaviour {
        public Texture2D goImage;

        void OnGUI ()
        {
            string scoreText = "You Scored " + PlayerPrefs.GetInt("LastScore");
            GUI.BeginGroup (new Rect (Screen.width / 2 - 100,
                Screen.height / 2 - 100, 200, 200));
            GUI.Box (new Rect (0,0,200,200), goImage);
            GUI.Label (new Rect (55, 15, 100, 30), scoreText);
            if(GUI.Button (new Rect (25,165,150,30), "Back to Menu"))
            {
                Application.LoadLevel("mainmenu"); 
            }
            GUI.EndGroup ();
        }

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

        // Update is called once per frame
        void Update () {
           
        }
    }

    Listing 4.13 Code to gather a total count of balls in each box.

    Already added in the code in Listing 4.9

    Listing 4.14 Rotating a game object around its axes with the arrow keys.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void Update () {
        if (Input.GetKey ("up")) //if up key is pressed
        {
            //rotate upward around the side axis
            this.transform.RotateAround (this.transform.position,this.transform.right,
                20 * Time.deltaTime);
        }
        if (Input.GetKey ("down"))
        {
            //rotate downward around the side axis
            this.transform.RotateAround (this.transform.position,this.transform.right,
                -20 * Time.deltaTime);
        }
        if (Input.GetKey ("right"))
        {
            this.transform.RotateAround (this.transform.position,this.transform.forward,
                -20 * Time.deltaTime);
        }
        if (Input.GetKey ("left"))
        {
            this.transform.RotateAround (this.transform.position,this.transform.forward,
                20 * Time.deltaTime);
        }
    }

    Listing 4.15 Code to instantiate an object and shoot it from another game object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public class aim : MonoBehaviour {

        public GameObject cannonball;

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

        // Update is called once per frame
        void Update () {
            if (Input.GetKey ("up")) //if up key is pressed
            {
                //rotate upward around the side axis
                this.transform.RotateAround (this.transform.position,this.transform.right,
                    20 * Time.deltaTime);
            }
            if (Input.GetKey ("down"))
            {
                //rotate downward around the side axis
                this.transform.RotateAround (this.transform.position,this.transform.right,
                    -20 * Time.deltaTime);
            }
            if (Input.GetKey ("right"))
            {
                this.transform.RotateAround(this.transform.position,
                    this.transform.forward,
                    -20 * Time.deltaTime);
            }
            if (Input.GetKey ("left"))
            {
                this.transform.RotateAround
                (this.transform.position,this.transform.forward,
                    20 * Time.deltaTime);
            }

            if(Input.GetKeyUp ("space"))
            {
                GameObject cb = Instantiate(cannonball,
                    this.transform.position, this.transform.rotation);
                cb.GetComponent<Rigidbody>().AddForce(this.transform.up * 10000);
            }
        }
    }

    Listing 4.16 Creating an explosion on impact.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class explode : MonoBehaviour {

        public GameObject explosion;
        public float explosionLife = 5.0f;
        public float detailLevel = 1.0f;
        public float countdown = 5.0f;
        Quaternion explosionDirection;
        Vector3 explosionLocation;

        void OnCollisionEnter(Collision collision)
        {
            GetComponent<Rigidbody>().isKinematic = true;
            Destroy (this.GetComponent<Collider>());
            ContactPoint contact = collision.contacts[0];
            Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal);
            explosionDirection = rot;
            float offsetSize = (explosion.GetComponent("Detonator") as Detonator).size /
            3.0f;
            Vector3 explosionLocation = contact.point + ((Vector3.Scale(contact.normal,
                new Vector3(offsetSize,offsetSize,offsetSize))));
            float timeSet = Time.fixedTime + countdown;
            bool explosionActivated = true;
            if(collision.gameObject.name != "Terrain")
            {
                Destroy(collision.gameObject,1);
            }
            Explode();
            Destroy(this.gameObject);
        }

        void Explode()
        {
            GameObject exp = Instantiate (explosion,explosionLocation,
                explosionDirection);
            (exp.GetComponent("Detonator") as Detonator).detail = detailLevel;
            Destroy(exp, explosionLife);
        }
    }

    Listing 4.17 Creating a visual meter for setting cannon strength.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class strengthMeter : MonoBehaviour {

        public static float shotStrength;
        void OnGUI ()
        {
            GUI.Box (new Rect (0,0,220,40), "Force");
            shotStrength = Slider (new Rect (10,20,200,30), shotStrength);
        }

        float Slider (Rect screenRect, float strength)
        {
            strength = GUI.HorizontalSlider (screenRect, strength, 0.0f, 1.0f);
            return strength;
        }
    }

    Listing 4.18 Script to build up firing strength when space is down and to fire when space is released.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    void Update ()
    {
        if (Input.GetKey ("up")) //if up key is pressed
        {
            //rotate upward around the side axis
            this.transform.RotateAround (this.transform.position,
                this.transform.right, 20 * Time.deltaTime);
        }
        ...
        if (Input.GetKey ("left"))
        {
            this.transform.RotateAround (this.transform.position,
                this.transform.forward,
                20 * Time.deltaTime);
        }

        if(Input.GetKey("space"))
        {
            //each loop the space is down increase the strength
            strengthMeter.shotStrength =
            strengthMeter.shotStrength + 0.01f;
        }

        if(Input.GetKeyUp ("space"))
        {
            GameObject cball = Instantiate(cannonball,
                this.transform.position, this.transform.rotation);
            //use built up strength to influence the shot
            float strength = strengthMeter.shotStrength * 10000;
            cball.GetComponent<Rigidbody>().AddForce(this.transform.up *
                strength);
            //set strength back to 0 after a shot
            strengthMeter.shotStrength = 0;
        }
    }

    Listing 4.19 Call a game over screen.
    a) C# version of the waypoints.js script

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class waypoints : MonoBehaviour {

        public int currentWP = 0;

        //the array of waypoints in order of visiting
        //can be made up of any game objects
        Public GameObject[] wps;

        int speed = 5;
        int rotationSpeed = 2;

        //how close to get to the waypoint to consider having reached it
        float accuracy = 5.0f;

        void Update ()
        {
            if(wps.Length == 0) return;
            if(Vector3.Distance(wps[currentWP].transform.position,
                transform.position) < accuracy)
            {
                currentWP++;
                if(currentWP >= wps.Length)
                {
                    currentWP = 0;
                }  
            }
           
            //rotate towards target
            Vector3 direction = wps[currentWP].transform.position -
            transform.position;
            transform.rotation = Quaternion.Slerp(transform.rotation,
                Quaternion.LookRotation(direction),
                rotationSpeed * Time.deltaTime);
            transform.Translate(0, 0, Time.deltaTime * speed);
        }
    }

    b) finishline.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class finishLine : MonoBehaviour {

        string winner = "";
        Vector3 playerLocation;
        Vector3 npcLocation;

        void Start()
        {
            //remember location and orientation of both cars at
            //the start
            playerLocation = GameObject.Find("Car").transform.position;
            npcLocation = GameObject.Find("Player2").transform.position;
        }

        void OnTriggerEnter(Collider collision)
        {
            //if there is no winner yet
            if(winner == "")
            {
                //make this one the winner
                winner = collision.gameObject.name;
            }
        }

        void OnGUI ()
        {
            if(winner != "")
            {
                GUI.BeginGroup(new Rect(Screen.width/2-100,
                    Screen.height/2-100,200,200));
                GUI.Box (new Rect (0,0,200,200), "Game Over");
                GUI.Label(new Rect (20,20,200,100), "Winner: " + winner);
                if(GUI.Button (new Rect (50,60,100,30), "Play Again"))
                {
                    //reset the game
                    winner = "";
                    GameObject.Find("Car").transform.position = playerLocation;
                    GameObject.Find("Player2").transform.position = npcLocation;
                    (GameObject.Find("Player2").GetComponent("waypoints")
                        as waypoints).currentWP = 0;
                }
                GUI.EndGroup ();
            }
        }
    }

    Listing 4.20 Code to decline the health bar.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //ginterface.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class ginterface : MonoBehaviour {

        static public float gHealth = 100.0f;
        public Slider health;

        void Update ()
        {
            gHealth -= 0.1f;
            if(gHealth > 100) gHealth = 100;
            health.value = gHealth;
        }
    }

    Listing 4.21 Triggering a death animation.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    //grannyWalker.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class grannyWalker : MonoBehaviour {

        Animator anim;
        float speed= 0.1f;

        void Start ()
        {
            anim = gameObject.GetComponent<Animator>();
        }

        void Update ()
        {

            if(ginterface.gHealth <= 0)
            {
                anim.SetTrigger("dead");
                return;
            }
           
            if(Input.GetKey("left"))
            {
                this.transform.Rotate(Vector3.up, -3);
            }
            else if(Input.GetKey("right"))
            {
                this.transform.Rotate(Vector3.up, 3);
            }
            else if(Input.GetKey("up"))
            {
                anim.SetBool("running",true);
                this.transform.position += this.transform.forward * speed;
            }
            else if(Input.GetKey("down"))
            {
                anim.SetBool("running",true);
                this.transform.position -= this.transform.forward * speed;
            }
            else if(Input.GetKeyUp("up") || Input.GetKeyUp("down"))
            {
                anim.SetBool("running",false);
            }
        }
    }

    Listing 4.22 Code to increase health bar.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //pickup.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class pickup : MonoBehaviour {

        void OnTriggerEnter(Collider other)
        {
            if(other.gameObject.name == "Sporty_Granny")
            {
                ginterface.gHealth += 50;
                this.GetComponent<AudioSource>().Play();
                Destroy(this.gameObject);
            }
        }
    }

    Listing 4.23 Code to increase health and play sound.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //pickup.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class pickup : MonoBehaviour {

        void OnTriggerEnter(Collider other)
        {
            if(other.gameObject.name == "Sporty_Granny")
            {
                ginterface.gHealth += 50;
                GameObject.Find("PickupSound").
                GetComponent<AudioSource>().Play();
                Destroy(this.gameObject);
            }
        }
    }

    Listing 4.24 Code to randomly drop chests on the ground plane.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //dropChests.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class dropChests : MonoBehaviour {

        public GameObject chestObj;
        public int numberOfChests = 50;

        void Start ()
        {
            for(int i = 0; i < numberOfChests; i++)
            {
                Vector3 pos = new Vector3(Random.Range(-100,100),
                    0.14f, Random.Range(-100,100));
                Instantiate(chestObj, pos, Quaternion.identity);
            }

        }
    }

    Listing 4.25 A script to decrease the player health while they remain inside a collider.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //firehit.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class firehit : MonoBehaviour {

        void OnTriggerStay(Collider other)
        {
            if(other.gameObject.name == "Sporty_Granny")
            {
                ginterface.gHealth -= 2;
            }
        }
    }

    Listing 4.26 Creating a HUD radar to track game objects with the tag orb.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    //radar.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class radar : MonoBehaviour {

        public Texture orbspot;
        public Transform playerPos;
        float mapScale = 0.5f;
        float radarSpotX;
        float radarSpotY;
        float radarWidth = 100f;
        float radarHeight = 100f;

        void OnGUI ()
        {
            GUI.BeginGroup (new Rect (10, Screen.height-radarHeight-10,
                radarWidth, radarHeight));
            GUI.Box (new Rect (0, 0, radarWidth, radarHeight), "Radar");
            DrawSpotsForOrbs();
            GUI.EndGroup();
        }

        void DrawRadarBlip(GameObject go, Texture spotTexture)
        {
            Vector3 gameObjPos = go.transform.position;
            //find distance between object and player
            float dist = Vector3.Distance(playerPos.position, gameObjPos);
            //find the horizontal distances along the
            //x and z between player and object
            float dx = playerPos.position.x - gameObjPos.x;
            float dz = playerPos.position.z - gameObjPos.z;
            //determine the angle of rotation between the
            //direction the player is facing and the location
            //of the object
            float deltay = Mathf.Atan2(dx, dz) *
            Mathf.Rad2Deg-270-playerPos.eulerAngles.y;

            //orient the object on the radar according to the
            //direction the player is facing
            radarSpotX = dist * Mathf.Cos(deltay * Mathf.Deg2Rad) * mapScale;
            radarSpotY = dist * Mathf.Sin(deltay * Mathf.Deg2Rad) * mapScale;
            //draw a spot on the radar
            GUI.DrawTexture( new Rect(radarWidth/2.0f + radarSpotX,
                radarHeight/2.0f + radarSpotY, 2, 2),
            spotTexture);
        }

        void DrawSpotsForOrbs()
        {
            GameObject[] gos;
            //look for all objects with a tag of orb
            gos = GameObject.FindGameObjectsWithTag("orb");
            double distance = Mathf.Infinity;
            Vector3 position = transform.position;
            foreach(GameObject go in gos)
            {
                DrawRadarBlip(go,orbspot);
            }
        }
    }

    Chapter 5

    Listing 5.1 & 5.2 Basic line of sight script.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    //lineOfSight.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class lineOfSight : MonoBehaviour {

        public Transform target; //the enemy
        float seeRange = 20.0f; //maximum attack distance –
        //will attack if closer than
        //this to the enemy
        float shootRange = 8.0f;
        float keepDistance = 2.0f; //closest distance to get to enemy
        float rotationSpeed = 2.0f;
        float sightAngle = 60f;
        float speed = 0.1f;
        string state = "PATROL";

        Animator anim;
        public GameObject magic;

        void Start()
        {
            anim = gameObject.GetComponent<Animator>();
            magic = GameObject.Find("Magic");
            magic.GetComponent<ParticleSystem>().Stop();
            Patrol();
        }

        void TurnOnMagic()
        {
            magic.GetComponent<ParticleSystem>().Play();
        }

        void TurnOffMagic()
        {
            magic.GetComponent<ParticleSystem>().Stop();
        }

        void Update ()
        {
            if(anim.GetCurrentAnimatorClipInfo(0)[0].clip.name ==
                "Standing_Walk_Forward")
            speed = 0.1f;
            else
            speed = 0.0f;
           
            if (CanSeeTarget ())
            {
                if(CanShoot())
                {
                    state = "SHOOTING";
                    anim.SetBool("isWalking",false);
                    anim.SetBool("isAttacking",true);
                    Shoot();
                }
                else
                {
                    state = "PURSUE";
                    anim.SetBool("isWalking",true);
                    Pursue();
                }
            }
            else
            {
                state = "PATROL";
                anim.SetBool("isWalking",false);
                anim.SetBool("isAttacking",false);
                Patrol();
            }
        }

        void Patrol ()
        {
        //stand around
        }

        bool CanSeeTarget ()
        {
        Vector3 directionToTarget = target.position - transform.Find("Eyes").position;
        float angle = Vector3.Angle(directionToTarget,
            this.transform.Find("Eyes").forward);
        if (Vector3.Distance(transform.Find("Eyes").position, target.position) >
            seeRange || angle > sightAngle)
        return false;
        return true;
        }

        bool CanShoot()
        {
        if (Vector3.Distance(transform.Find("Eyes").position, target.position) >
            shootRange)
        return false;
        return true;
        }

        void Pursue()
        {
        Vector3 position = target.position;
        Vector3 direction = position - transform.Find("Eyes").position;
        direction.y = 0;
            // Rotate towards the target
            transform.Find("Eyes").rotation = Quaternion.Slerp
            (transform.Find("Eyes").rotation, Quaternion.LookRotation(direction),
                rotationSpeed * Time.deltaTime);
            transform.eulerAngles = new Vector3(0,transform.eulerAngles.y, 0);
            // Move the character
            if(direction.magnitude > keepDistance)
            {
                direction = direction.normalized * speed;
                transform.position += direction;
            }
        }

        void Shoot()
        {
            Vector3 position = target.position;
            Vector3 direction = position - transform.Find("Eyes").position;
            direction.y = 0;
            // Rotate towards the target
            transform.rotation = Quaternion.Slerp(transform.rotation,
                Quaternion.LookRotation(direction),
                rotationSpeed * Time.deltaTime);
            transform.eulerAngles = new Vector3(0, transform.
                eulerAngles.y, 0);
        }
    }

    Listing 5.3 A breadth-first search algorithm.
    No change
    Listing 5.4 A depth-first search algorithm.
    No change

    Listing 5.5 Initialising waypoints in a graph object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class patrol : MonoBehaviour {

        public GameObject[] waypoints;
        Graph graph = new Graph();
        int currentWP = 0;
        GameObject currentNode;

        int speed = 8;
        int rotationSpeed = 5;
        float accuracy = 1.0f;


        // Use this for initialization
        void Start () {
            if(waypoints.Length > 0)
            {
                // add all the waypoints to the graph
                for(int i = 0; i < waypoints.Length; i++)
                {
                    graph.AddNode(waypoints[i], true, true);
                }
               
                //create edges between the waypoints
                graph.AddEdge(waypoints[0], waypoints[1]);
                graph.AddEdge(waypoints[1], waypoints[2]);
                graph.AddEdge(waypoints[2], waypoints[3]);
                graph.AddEdge(waypoints[3], waypoints[4]);
                graph.AddEdge(waypoints[4], waypoints[5]);
                graph.AddEdge(waypoints[5], waypoints[6]);
                graph.AddEdge(waypoints[6], waypoints[7]);
                graph.AddEdge(waypoints[7], waypoints[8]);
                graph.AddEdge(waypoints[8], waypoints[0]);
               
            }
            currentNode = waypoints[0];
        }

        // Update is called once per frame
        void Update () {
            graph.debugDraw();
        }
    }

    Listing 5.6 Ordering a guard to traverse waypoints.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public GameObject[] waypoints;
    ...

        void OnGUI ()
        {
            GUI.Box (new Rect (10,10,100,90), "Guard's Orders");

            if (GUI.Button (new Rect (20,65,80,20), "Patrol"))
            {
                graph.AStar(waypoints[0],waypoints[8]);

                this.GetComponent<Animation>().Play("run");
                this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
            }
        }

    ...    

        void Update () {
            graph.debugDraw();
            //if there is no path or at the end don't do anything
            if(graph.getPathLength() == 0 || currentWP == graph.getPathLength())
            {
                this.GetComponent<Animation>().Play("idle");
                return;
            }  

            //the node we are closest to at this moment
            currentNode = graph.getPathPoint(currentWP);

            //if we are close enough to the current waypoint move to next
            if(Vector3.Distance(
                graph.getPathPoint(currentWP).transform.position,
                transform.position) < accuracy)
            {
                currentWP++;
            }

            //if we are not at the end of the path
            if(currentWP < graph.getPathLength())
            {
              //keep on movin'
              Vector3 direction = graph.getPathPoint(currentWP).transform.position -
              transform.position;
              transform.rotation = Quaternion.Slerp(transform.rotation,
                Quaternion.LookRotation(direction),
                rotationSpeed * Time.deltaTime);
              transform.Translate(0, 0, Time.deltaTime * speed);
             }
        }

    Listing 5.7 Testing A* pathfinding by given movement comments to a character.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    ...
    void OnGUI ()
    {
        GUI.Box (new Rect (10,10,100,90), "Guard's Orders");

        if (GUI.Button (new Rect (20,65,80,20), "Front Door"))
        {
            graph.AStar(currentNode,waypoints[0]);
            currentWP = 0;
           
            this.GetComponent<Animation>().Play("run");
            this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
        }
        if (GUI.Button (new Rect (20,90,80,20), "Driveway"))
        {
            graph.AStar(currentNode,waypoints[9]);
            currentWP = 0;
           
            this.GetComponent<Animation>().Play("run");
            this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
        }
       
        if (GUI.Button (new Rect (20,115,80,20), "Front"))
        {
            graph.AStar(currentNode,waypoints[1]);
            currentWP = 0;
           
            this.GetComponent<Animation>().Play("run");
            this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
        }
    }

    // Use this for initialization
    void Start () {
        if(waypoints.Length > 0)
        {
            // add all the waypoints to the graph
            for(int i = 0; i < waypoints.Length; i++)
            {
                graph.AddNode(waypoints[i], true, true);
            }

            //create edges between the waypoints
            graph.AddEdge(waypoints[0], waypoints[1]);
            graph.AddEdge(waypoints[1], waypoints[2]);
            graph.AddEdge(waypoints[2], waypoints[3]);
            graph.AddEdge(waypoints[3], waypoints[4]);
            graph.AddEdge(waypoints[4], waypoints[5]);
            graph.AddEdge(waypoints[5], waypoints[6]);
            graph.AddEdge(waypoints[6], waypoints[7]);
            graph.AddEdge(waypoints[7], waypoints[8]);
            graph.AddEdge(waypoints[8], waypoints[0]);

            //and back the other way
            graph.AddEdge(waypoints[1], waypoints[0]);
            graph.AddEdge(waypoints[2], waypoints[1]);
            graph.AddEdge(waypoints[3], waypoints[2]);
            graph.AddEdge(waypoints[4], waypoints[3]);
            graph.AddEdge(waypoints[5], waypoints[4]);
            graph.AddEdge(waypoints[6], waypoints[5]);
            graph.AddEdge(waypoints[7], waypoints[6]);
            graph.AddEdge(waypoints[8], waypoints[7]);
            graph.AddEdge(waypoints[0], waypoints[8]);

            //create edges to extra to waypoints
            graph.AddEdge(waypoints[0], waypoints[8]);
            graph.AddEdge(waypoints[0], waypoints[9]);
            graph.AddEdge(waypoints[9], waypoints[9]);
            graph.AddEdge(waypoints[5], waypoints[8]);
            //and back again
            graph.AddEdge(waypoints[8], waypoints[0]);
            graph.AddEdge(waypoints[9], waypoints[0]);
            graph.AddEdge(waypoints[9], waypoints[9]);
            graph.AddEdge(waypoints[8], waypoints[5]);

        }
        currentNode = waypoints[0];
    }

    Listing 5.8 Code for simple finite state machine.
    First line changes from

    1
    function FSM()

    To

    1
    void FSM()

    Listing 5.9 Setting up a waypoint system with automatic waypoint collection.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class patrol : MonoBehaviour {

        Graph graph = new Graph();
        int currentWP = 0;
        GameObject currentNode;
        int speed = 8;
        int rotationSpeed = 5;
        float accuracy = 1;


        void CreateBiPath(string a, string b)
        {
            GameObject w1 = GameObject.Find(a);
            GameObject w2 = GameObject.Find(b);
           
            if(w1 && w2) //if both objects exist
            {
                //create edges between the waypoints
                //in both directions
                graph.AddEdge(w1, w2);
                graph.AddEdge(w2, w1);
            }

        }
        // Use this for initialization
        void Start () {
            GameObject[] gos;
            gos = GameObject.FindGameObjectsWithTag("waypoint");
           
            foreach (GameObject go in gos)
            {
                graph.AddNode(go, true, true);
            }
           
            CreateBiPath("Sphere3","Sphere2");
            CreateBiPath("Sphere2","Sphere1");
            CreateBiPath("Sphere1","Sphere4");
            CreateBiPath("Sphere3","Sphere4");
            CreateBiPath("Sphere4","Sphere5");
            CreateBiPath("Sphere5","Sphere6");
            CreateBiPath("Sphere6","Sphere7");
            CreateBiPath("Sphere7","Sphere8");
            CreateBiPath("Sphere8","Sphere9");
            CreateBiPath("Sphere9","Sphere10");
            CreateBiPath("Sphere10","Sphere11");
            CreateBiPath("Sphere11","Sphere12");
            CreateBiPath("Sphere12","Sphere13");
            CreateBiPath("Sphere13","Sphere14");
            CreateBiPath("Sphere14","Sphere15");
            CreateBiPath("Sphere15","Sphere17");
           
            currentNode = GameObject.Find("Sphere2");
        }

        // Update is called once per frame
        void Update () {
            graph.debugDraw();
        }
    }

    Listing 5.10 & 5.11 Adding a FSM function and Extending with more states

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class patrol : MonoBehaviour {

        Graph graph = new Graph();
        int currentWP = 0;
        GameObject currentNode;
        int speed = 8;
        int rotationSpeed = 5;
        float accuracy = 1;

        string prevState = "";
        string state = "idle";
        string eventname = "enter";
        GameObject goalLocation;

        public Transform target;
        float seeRange = 10;
        float shootRange = 5;
        float keepDistance = 1;
        float heightOffset = 0.3f;

        void CreateBiPath(string a, string b)
        {
            GameObject w1 = GameObject.Find(a);
            GameObject w2 = GameObject.Find(b);
           
            if(w1 && w2) //if both objects exist
            {
                //create edges between the waypoints
                //in both directions
                graph.AddEdge(w1, w2);
                graph.AddEdge(w2, w1);
            }

        }

        bool CanSeeTarget ()
        {
            if (Vector3.Distance(transform.position, target.position) > seeRange)
            return false;
            return true;
        }

        bool CanShootTarget()
        {
            if(!CanSeeTarget()) return false;
            if (Vector3.Distance(transform.position, target.position) > shootRange)
            return false;
            return true;
        }

        // Use this for initialization
        void Start () {
            GameObject[] gos;
            gos = GameObject.FindGameObjectsWithTag("waypoint");
           
            foreach (GameObject go in gos)
            {
                graph.AddNode(go, true, true);
            }
           
            CreateBiPath("Sphere3","Sphere2");
            CreateBiPath("Sphere2","Sphere1");
            CreateBiPath("Sphere1","Sphere4");
            CreateBiPath("Sphere3","Sphere4");
            CreateBiPath("Sphere4","Sphere5");
            CreateBiPath("Sphere5","Sphere6");
            CreateBiPath("Sphere6","Sphere7");
            CreateBiPath("Sphere7","Sphere8");
            CreateBiPath("Sphere8","Sphere9");
            CreateBiPath("Sphere9","Sphere10");
            CreateBiPath("Sphere10","Sphere11");
            CreateBiPath("Sphere11","Sphere12");
            CreateBiPath("Sphere12","Sphere13");
            CreateBiPath("Sphere13","Sphere14");
            CreateBiPath("Sphere14","Sphere15");
            CreateBiPath("Sphere15","Sphere17");
           
            currentNode = GameObject.Find("Sphere2");
        }

        void FSM()
        {
            Vector3 direction;
            Vector3 position;
           
            if(state == "patrol")
            {
                if(prevState != state)
                {
                    print("patrol " + currentNode.name);
                    graph.AStar(currentNode,goalLocation);
                    graph.printPath();
                    currentWP = 0;
                   
                    this.GetComponent<Animation>().Play("run");
                    this.GetComponent<Animation>()["run"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";
                    prevState = state;
                }
                else if (eventname == "update")
                {
                    if(graph.getPathLength() == 0 ||
                        currentWP == graph.getPathLength())
                    {
                        state = "idle";
                        eventname = "enter";
                        return;
                    }

                    //the node we are closest to at this moment
                    currentNode = graph.getPathPoint(currentWP);

                    if(Vector3.Distance(
                        graph.getPathPoint(currentWP).transform.position,
                        transform.position) < accuracy)
                    {
                        currentWP++;
                    }

                    //if we are not at the end of the path
                    if(currentWP < graph.getPathLength())
                    {
                        //keep on movin'
                        direction =
                        graph.getPathPoint(currentWP).transform.position -
                        transform.position;
                        transform.rotation = Quaternion.Slerp(transform.rotation,
                            Quaternion.LookRotation(direction),
                            rotationSpeed * Time.deltaTime);
                        transform.Translate(0, 0, Time.deltaTime * speed);
                    }

                }
                else if (eventname == "exit")
                {
                   
                }  
            }
            else if(state == "attack")
            {
                if(prevState != state)
                {
                    this.GetComponent<Animation>().CrossFade("shoot");
                    this.GetComponent<Animation>()["shoot"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";  
                }
                else if(eventname == "update")
                {
                    position = target.position;
                    direction = position - transform.position;
                    direction.y = 0;
                    // Rotate towards the target
                    transform.rotation = Quaternion.Slerp(transform.rotation,
                        Quaternion.LookRotation(direction), rotationSpeed *
                        Time.deltaTime);
                    transform.position = new Vector3(transform.position.x,
                        target.position.y + heightOffset,
                        transform.position.z);
                }
               
            }
            else if(state == "pursue")
            {
                if(prevState != state)
                {
                    this.GetComponent<Animation>().CrossFade("run");
                    this.GetComponent<Animation>()["run"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";  
                }
                else if(eventname == "update")
                {
                    direction = target.position - transform.position;
                    direction.y = 0;
                    // Rotate towards the target
                    transform.rotation = Quaternion.Slerp(transform.rotation,
                        Quaternion.LookRotation(direction),
                        rotationSpeed * Time.deltaTime);

                    // Move the character
                    if(direction.magnitude > keepDistance)
                    {
                        transform.Translate(0, 0, Time.deltaTime * speed);
                    }
                    transform.position = new Vector3(transform.position.x,
                        target.position.y + heightOffset,
                        transform.position.z);
                }
               
            }
            else if(state == "idle")
            {
                print("play idle");
                this.GetComponent<Animation>().Play("idle");
                eventname = "update";
                prevState = state; 

                if(eventname == "update")
                {
                   
                    //just remain idle 
                    if(Random.Range(0,10) < 5)
                    {
                        state = "patrol";
                        eventname = "enter";

                        if(currentNode == GameObject.Find("Sphere17"))
                        goalLocation = GameObject.Find("Sphere2");
                        else
                        goalLocation = GameObject.Find("Sphere17");
                    }
                }
               
            }
        }

        // Update is called once per frame
        void Update () {
            graph.debugDraw();
            if(CanShootTarget())
            {
                prevState = state;
                state = "attack";
            }
            else if(CanSeeTarget())
            {
                prevState = state;
                state = "pursue";
            }
            else if(state != "patrol")
            {
                prevState = state;
                state = "idle";
            }

            FSM();
        }
    }

    Listing 5.12 Recording player movement with breadcrumbs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class breadcrumbs : MonoBehaviour {

        public ArrayList crumbs = new ArrayList();
        Vector3 lastPos;

        public void RemoveBreadCrumb()
        {      
            Destroy(crumbs[0] as GameObject);
            crumbs.RemoveAt(0);
        }

        void Update ()
        {
            if(lastPos != this.transform.position)
            {
                GameObject bc = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                bc.transform.position = this.transform.position;
                Destroy(bc.GetComponent<Collider>());
                bc.transform.localScale = new Vector3(0.5f,0.5f,0.5f);
                bc.GetComponent<Renderer>().material.color = Color.green;
                crumbs.Add(bc);
            }
            lastPos = this.transform.position;
           
            if(crumbs.Count > 100)
            {
                RemoveBreadCrumb();
            }
        }
    }

    Listing 5.13 Adding breadcrumb following code to the NPC.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ...
    else if(state == "pursue")
    {
    if(prevState != state)
    {
        this.GetComponent<Animation>().CrossFade("run");
        this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
        eventname = "update";  
    }
    else if(eventname == "update")
    {
        position = (target.gameObject.GetComponent<breadcrumbs>().
            crumbs[0] as
            GameObject).transform.position;
        if(Vector3.Distance(position, this.transform.position) < 2)
        {
            target.gameObject.GetComponent<breadcrumbs>().
            RemoveBreadCrumb();
        }

        direction = target.position - transform.position;
        direction.y = 0;
        ...

    Listing 5.14 Finding the closest waypoint to set NPC back to patrol.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class patrol : MonoBehaviour {

        Graph graph = new Graph();
        int currentWP = 0;
        GameObject currentNode;
        int speed = 8;
        int rotationSpeed = 5;
        float accuracy = 1;

        string prevState = "";
        string state = "idle";
        string eventname = "enter";
        GameObject goalLocation;

        public Transform target;
        float seeRange = 10;
        float shootRange = 5;
        float keepDistance = 1;
        float heightOffset = 0.3f;

        void CreateBiPath(string a, string b)
        {
            GameObject w1 = GameObject.Find(a);
            GameObject w2 = GameObject.Find(b);

            if(w1 && w2) //if both objects exist
            {
                //create edges between the waypoints
                //in both directions
                graph.AddEdge(w1, w2);
                graph.AddEdge(w2, w1);
            }

        }

        bool CanSeeTarget ()
        {
            if (Vector3.Distance(transform.position, target.position) > seeRange)
            return false;
            return true;
        }

        bool CanShootTarget()
        {
            if(!CanSeeTarget()) return false;
            if (Vector3.Distance(transform.position, target.position) > shootRange)
            return false;
            return true;
        }

        // Use this for initialization
        void Start () {
            GameObject[] gos;
            gos = GameObject.FindGameObjectsWithTag("waypoint");
           
            foreach (GameObject go in gos)
            {
                graph.AddNode(go, true, true);
            }
           
            CreateBiPath("Sphere3","Sphere2");
            CreateBiPath("Sphere2","Sphere1");
            CreateBiPath("Sphere1","Sphere4");
            CreateBiPath("Sphere3","Sphere4");
            CreateBiPath("Sphere4","Sphere5");
            CreateBiPath("Sphere5","Sphere6");
            CreateBiPath("Sphere6","Sphere7");
            CreateBiPath("Sphere7","Sphere8");
            CreateBiPath("Sphere8","Sphere9");
            CreateBiPath("Sphere9","Sphere10");
            CreateBiPath("Sphere10","Sphere11");
            CreateBiPath("Sphere11","Sphere12");
            CreateBiPath("Sphere12","Sphere13");
            CreateBiPath("Sphere13","Sphere14");
            CreateBiPath("Sphere14","Sphere15");
            CreateBiPath("Sphere15","Sphere17");
           
            currentNode = GameObject.Find("Sphere2");
        }

        GameObject findClosestWP()
        {
            GameObject[] gos;
            gos = GameObject.FindGameObjectsWithTag("waypoint");
            GameObject closest = gos[0];
           
            foreach (GameObject go in gos)
            {
                if(Vector3.Distance(closest.transform.position, this.transform.position) >
                    Vector3.Distance(go.transform.position,this.transform.position))
                {
                    closest = go;
                }
            }
            return closest;
        }

        void FSM()
        {
            Vector3 direction;
            Vector3 position;
           
            if(state == "patrol")
            {
                if(prevState != state)
                {
                    print("patrol " + currentNode.name);
                    graph.AStar(currentNode,goalLocation);
                    graph.printPath();
                    currentWP = 0;
                   
                    this.GetComponent<Animation>().Play("run");
                    this.GetComponent<Animation>()["run"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";
                    prevState = state;
                }
                else if (eventname == "update")
                {
                    if(graph.getPathLength() == 0 || currentWP ==
                        graph.getPathLength())
                    {
                        state = "idle";
                        eventname = "enter";
                        return;
                    }

                    //the node we are closest to at this moment
                    currentNode = graph.getPathPoint(currentWP);

                    if(Vector3.Distance(graph.getPathPoint(currentWP).
                        transform.position, transform.position) < accuracy)
                    {
                        currentWP++;
                    }

                    //if we are not at the end of the path
                    if(currentWP < graph.getPathLength())
                    {
                        //keep on movin'
                        direction =
                        graph.getPathPoint(currentWP).transform.position -
                        transform.position;
                        transform.rotation = Quaternion.Slerp(transform.rotation,
                            Quaternion.LookRotation(direction),
                            rotationSpeed * Time.deltaTime);
                        transform.Translate(0, 0, Time.deltaTime * speed);
                    }

                }
                else if (eventname == "exit")
                {
                   
                }  
            }
            else if(state == "attack")
            {
                if(prevState != state)
                {
                    this.GetComponent<Animation>().CrossFade("shoot");
                    this.GetComponent<Animation>()["shoot"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";  
                }
                else if(eventname == "update")
                {
                    position = target.position;
                    direction = position - transform.position;
                    direction.y = 0;
                    // Rotate towards the target
                    transform.rotation = Quaternion.Slerp(transform.rotation,
                        Quaternion.LookRotation(direction), rotationSpeed *
                        Time.deltaTime);
                    transform.position = new Vector3(transform.position.x,
                        target.position.y + heightOffset,
                        transform.position.z);
                }
               
            }
            else if(state == "pursue")
            {
                if(prevState != state)
                {
                    this.GetComponent<Animation>().CrossFade("run");
                    this.GetComponent<Animation>()["run"].wrapMode =
                    WrapMode.Loop;
                    eventname = "update";  
                }
                else if(eventname == "update")
                {
                    position = (target.gameObject.GetComponent<breadcrumbs>().
                        crumbs[0] as GameObject).transform.position;
                    if(Vector3.Distance(position, this.transform.position) < 2)
                    {
                        target.gameObject.GetComponent<breadcrumbs>().
                        RemoveBreadCrumb();
                    }

                    direction = target.position - transform.position;
                    direction.y = 0;
                    // Rotate towards the target
                    transform.rotation = Quaternion.Slerp(transform.rotation,
                        Quaternion.LookRotation(direction),
                        rotationSpeed * Time.deltaTime);

                    // Move the character
                    if(direction.magnitude > keepDistance)
                    {
                        transform.Translate(0, 0, Time.deltaTime * speed);
                    }
                    transform.position = new Vector3(transform.position.x,
                        target.position.y + heightOffset,
                        transform.position.z);
                }
               
            }
            else if(state == "idle")
            {
                print("play idle");
                this.GetComponent<Animation>().Play("idle");
                eventname = "update";
                prevState = state; 

                if(eventname == "update")
                {
                   
                    //just remain idle 
                    if(Random.Range(0,10) < 5)
                    {
                        state = "patrol";
                        eventname = "enter";
                        currentNode = findClosestWP();
                        if(currentNode == GameObject.Find("Sphere17"))
                        goalLocation =  
                        GameObject.Find("Sphere2");
                        else
                        goalLocation = GameObject.Find("Sphere17");
                    }
                }
               
            }
        }

        // Update is called once per frame
        void Update () {
            graph.debugDraw();
            if(CanShootTarget())
            {
                prevState = state;
                state = "attack";
            }
            else if(CanSeeTarget())
            {
                prevState = state;
                state = "pursue";
            }
            else if(state != "patrol")
            {
                prevState = state;
                state = "idle";
            }

            FSM();
        }
    }


    <b>Listings 5.15 to 5.20</b>
    <code lang="csharp">
    //flock.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class flock : MonoBehaviour {

        float speed = 0.001f;
        float rotationSpeed = 5.0f;
        Vector3 averageHeading;
        Vector3 averagePosition;

        float neighbourDistance = 2.0f;

        void Start()
        {
            speed = Random.Range(0.1f,1f);
        }

        void Update ()
        {
            if(Random.Range(0,5) < 1)
            ApplyRules();
            transform.Translate(0, 0, Time.deltaTime * speed);
        }

        void ApplyRules()
        {
            GameObject[] gos;
            gos = GameObject.FindGameObjectsWithTag("Seagull");
           
            Vector3 vcentre = Vector3.zero;
            Vector3 vavoid = Vector3.zero;
            float gSpeed = 0;
           
            Vector3 wind = new Vector3(1,0,1);
            Vector3 goalPos = globalFlock.goalPos;
           
            float dist = 0;

            int groupSize = 0;
            foreach (GameObject go in gos)
            {
                if(go != this.gameObject)
                {
                    dist = Vector3.Distance(go.transform.position,
                        this.transform.position);
                    if(dist <= neighbourDistance)
                    {
                        vcentre += go.transform.position;  
                        groupSize++;   
                       
                        if(dist < 0.5f)    
                        {
                            vavoid = vavoid + (this.transform.position -
                                go.transform.position);
                        }
                       
                        gSpeed = gSpeed + go.GetComponent<flock>().speed;
                    }
                }
            }
           
            if(groupSize != 0)
            {
                vcentre = vcentre/groupSize + wind + (goalPos -
                    this.transform.position);
                speed = gSpeed/groupSize;
               
                var direction = (vcentre + vavoid) - transform.position;
                if(direction != Vector3.zero)
                transform.rotation =
                Quaternion.Slerp(transform.rotation,
                    Quaternion.LookRotation(direction),
                    rotationSpeed * Time.deltaTime);

            }
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    //globalFlock.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class globalFlock : MonoBehaviour {

        public GameObject gull;

        static public Vector3 goalPos;

        void Start()
        {
            //create seagulls
            for(int i = 0; i < 100; i++)
            {
                Vector3 pos = new
                Vector3(Random.Range(-10,10),0,Random.Range(-10,10));
                Instantiate(gull,pos, Quaternion.identity);
            }  
           
            goalPos = new Vector3(0,0,0);
        }

        void Update ()
        {
            if(Random.Range(0,10000) < 50)
            {
                goalPos = new
                Vector3(Random.Range(-1.0f,1.0f),0,
                    Random.Range(-1.0f,1.0f));
            }
           
        }
    }

    Listing 5.21 Implementing a fuzzy logic engine in Unity
    (Includes C# code for all scripts necessary to make it work)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    //AI.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class AI : MonoBehaviour {

        public Transform target;        //the enemy
        float seeRange = 100.0f; //maximum attack distance ?
                                        //will attack if closer than
                                        //this to the enemy
                                        float shootRange = 20.0f;
        float keepDistance = 20.0f; //closest distance to get to enemy
        float safeDistance = 200.0f;
        float rotationSpeed = 5.0f;
        float sightAngle = 30f;
        float speed = 0.01f;
        string state = "PATROL";

        void Start()
        {
            Patrol();
            this.GetComponent<Animation>()["shoot"].wrapMode = WrapMode.Loop;
            this.GetComponent<Animation>()["run"].wrapMode = WrapMode.Loop;
            this.GetComponent<Animation>()["idle"].wrapMode = WrapMode.Loop;
        }

        void Update ()
        {
            this.GetComponent<fuzzyBrain>().UpdateStatus();
           
            if(this.GetComponent<fuzzyBrain>().moodValue < 20)
            {
                state = "RUNAWAY";
                GetComponent<Animation>().CrossFade("run");
                speed = 0.20f;
                RunAway();
                return;
            }
           
            if (CanSeeTarget ())
            {
                if(this.GetComponent<fuzzyBrain>().moodValue < 50)
                {
                    state = "SHOOTING";
                    GetComponent<Animation>().CrossFade("shoot");
                    speed = 0.00f;
                    Shoot();
                }
                else if (this.GetComponent<fuzzyBrain>().moodValue < 80)
                {
                    state = "PURSUE";
                    GetComponent<Animation>().CrossFade("run");
                    speed = 0.08f;
                    Pursue();
                }
            }
            else
            {
                state = "PATROL";
                if (!GetComponent<Animation>().IsPlaying("idle"))
                {
                    GetComponent<Animation>().Play ("idle");
                    speed = 0.00f;
                }  
                Patrol();
            }
           
        }

        void Patrol ()
        {
            //stand around
        }


        bool CanSeeTarget ()
        {
            var directionToTarget = target.position - transform.position;
            if (Vector3.Distance(transform.position, target.position) > seeRange)
            return false;
           
            return true;
        }

        bool CanShoot()
        {
            if (Vector3.Distance(transform.position, target.position) > shootRange)
            return false;
           
            return true;
        }


        void Pursue()
        {
            var position = target.position;
            var direction = position - transform.position;
            direction.y = 0;
           
            // Rotate towards the target
            transform.rotation = Quaternion.Slerp (transform.rotation,
                Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
           
            transform.eulerAngles = new Vector3(0,transform.eulerAngles.y, 0);

            // Move the character
            if(direction.magnitude > keepDistance)
            {
                direction = direction.normalized * speed;              
                transform.position += direction;
            }
            else
            {
                GetComponent<Animation>().Play ("idle");
                speed = 0.00f;
            }
        }

        void RunAway()
        {
            var position = target.position;
            var direction = transform.position - position;
            direction.y = 0;
           
            // Rotate away from the target
            transform.rotation = Quaternion.Slerp (transform.rotation,
                Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
            transform.eulerAngles = new Vector3(0,transform.eulerAngles.y, 0);

            // Move away the character
            if(direction.magnitude < safeDistance)
            {
                direction = direction.normalized * speed;              
                transform.position += direction;
            }
            else
            {
                GetComponent<Animation>().Play ("idle");
                speed = 0.00f;
            }
        }

        void Shoot()
        {
            var position = target.position;
            var direction = position - transform.position;
            direction.y = 0;
           
            // Rotate towards the target
            transform.rotation = Quaternion.Slerp(transform.rotation,
                Quaternion.LookRotation(direction), rotationSpeed *
                Time.deltaTime);
            transform.eulerAngles = new Vector3(0,
                transform.eulerAngles.y, 0);
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    //fuzzyBrain.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using DotFuzzy;

    public class fuzzyBrain : MonoBehaviour {

        public float moodValue = 100;
        public GameObject explode;

        LinguisticVariable health;
        LinguisticVariable shield;
        LinguisticVariable mood;
        FuzzyEngine fuzzyEngine;

        public int botHealth = 100;
        public int botShield = 100;

        int updateTime = 2; //seconds
        int lastUpdate = 0;

        void OnCollisionEnter(Collision obj)
        {
            GameObject e = Instantiate(explode, this.transform.position,
                Quaternion.identity);
            Destroy(e.gameObject,2);
            Destroy(obj.collider.gameObject);
            botShield -= 10;
            botHealth -= (int) (10 + (100 - botShield)/5.0f);
           
            if(botHealth < 0) botHealth = 0;
            if(botShield < 0) botShield = 0;
           
            health.InputValue = botHealth;
            shield.InputValue = botShield;
           
            //set alpha color of shield relative to shield strength
            GameObject.Find("Shield").GetComponent<Renderer>().material.color =
            new Color(1,0,0,botShield/200.0f);

        }

        public void UpdateStatus()
        {
            if(botHealth == 100 && botShield == 100) return; //don't bother updating
            if(botHealth > 100) botHealth = 100;
            if(botShield > 100) botShield = 100;
           
            if(Time.fixedTime > lastUpdate + updateTime)
            {
                if(botHealth < 100)
                botHealth += 5;
                if(botShield < 100)
                botShield += 10;
               
                GameObject.Find("Shield").GetComponent<Renderer>().material.color
                = new Color(1,0,0,botShield/200.0f);
                health.InputValue = botHealth;
                shield.InputValue = botShield;
                moodValue = (float) fuzzyEngine.Defuzzify();
                lastUpdate = (int) Time.fixedTime;
            }
        }

        void Start()
        {
            health = new LinguisticVariable("Health");
            health.MembershipFunctionCollection.Add(
                new MembershipFunction("Bad", 0, 0, 20, 40));
            health.MembershipFunctionCollection.Add(
                new MembershipFunction("Moderate", 30, 50, 60, 80));
            health.MembershipFunctionCollection.Add(
                new MembershipFunction("Good", 70, 90, 100, 100));
            shield = new LinguisticVariable("Shield");
            shield.MembershipFunctionCollection.Add(
                new MembershipFunction("Low", 0, 0, 10, 50));
            shield.MembershipFunctionCollection.Add(
                new MembershipFunction("Medium", 20, 50, 60, 80));
            shield.MembershipFunctionCollection.Add(
                new MembershipFunction("High", 40, 80, 100, 100));
            mood = new LinguisticVariable("Mood");
            mood.MembershipFunctionCollection.Add(
                new MembershipFunction("Angry", 0, 0, 20, 40));
            mood.MembershipFunctionCollection.Add(
                new MembershipFunction("Indifferent", 30, 50,
                    50, 80));
            mood.MembershipFunctionCollection.Add(
                new MembershipFunction("Happy", 60, 90, 100, 100));
            fuzzyEngine = new FuzzyEngine();
            fuzzyEngine.LinguisticVariableCollection.Add(health);
            fuzzyEngine.LinguisticVariableCollection.Add(shield);
            fuzzyEngine.LinguisticVariableCollection.Add(mood);
            fuzzyEngine.Consequent = "Mood";
            fuzzyEngine.FuzzyRuleCollection.Add(new FuzzyRule(
                "IF (Health IS Good) AND (Shield IS High) THEN Mood IS Happy"));
            fuzzyEngine.FuzzyRuleCollection.Add(new FuzzyRule(
                "IF (Health IS Bad) OR (Shield IS Low) THEN Mood IS Angry"));
            fuzzyEngine.FuzzyRuleCollection.Add(new FuzzyRule(
                "IF (Health IS Moderate) AND (Shield IS High) THEN Mood IS Indifferent"));
            fuzzyEngine.FuzzyRuleCollection.Add(new FuzzyRule(
                "IF (Health IS Moderate) OR (Shield IS Medium) THEN Mood IS Indifferent"));
            fuzzyEngine.FuzzyRuleCollection.Add(new FuzzyRule(
                "IF (Health IS Bad) AND (Shield IS High) THEN Mood IS Indifferent"));
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //Ginterface.cs (renamed from interface.js)
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ginterface : MonoBehaviour {

        public GUIStyle myStyle;

        void OnGUI()
        {
            GUI.Label(new Rect(10,10,800,50),"Health " +
                this.GetComponent<fuzzyBrain>().botHealth
                + " Shield " + this.GetComponent<fuzzyBrain>().botShield,
                myStyle);
        }

        void Update () {
    }
    }

    Chapter 6

    Listing 6.1 Script to switch to another scene.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    public class menuController : MonoBehaviour {

        public void GoToGame()
        {
            SceneManager.LoadScene("game");
        }
    }

    Section 6.4

    Listing 6.11 Using a trigger event to teleport to another scene.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    public class teleport : MonoBehaviour {

        void OnTriggerEnter (Collider obj)
        {
            if(obj.gameObject.tag == "toSewer")
            {
                SceneManager.LoadScene("sewerScene");
            }
        }
    }

    Listing 6.12 Script to reposition a colliding game object at the position of another game object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class teleportTo : MonoBehaviour {

        public GameObject to;

        void OnTriggerEnter(Collider obj)
        {
            obj.gameObject.transform.position = to.transform.position +
            obj.gameObject.transform.forward*3;

        }
    }

    Chapter 7

    Listing 7.1 Script to create random height values on a mesh.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class makeTerrain : MonoBehaviour {

        void Start()
        {
            Mesh mesh = this.GetComponent<MeshFilter>().mesh;
            Vector3[] vertices = mesh.vertices;
            for (int v = 0; v < vertices.Length; v++)
            {
                vertices[v].y = Random.Range(0f,10f);
            }
            mesh.vertices = vertices;
            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
            this.gameObject.AddComponent<MeshCollider>();
        }
    }

    Listing 7.2 Using a sine function to set the terrain height.
    Changes to code are the same.

    Listing 7.3 Using Perlin Noise to generate terrain heights.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class makeTerrain : MonoBehaviour {

        void Start()
        {
            Perlin surface = new Perlin();
            Mesh mesh = this.GetComponent<MeshFilter>().mesh;
            Vector3[] vertices = mesh.vertices;
            for (int v = 0; v < vertices.Length; v++)
            {
                vertices[v].y = surface.Noise(
                    vertices[v].x * 2 + 0.1365143f,
                    vertices[v].z * 2 + 1.21688f) * 10;
            }
            mesh.vertices = vertices;
            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
            this.gameObject.AddComponent<MeshCollider>();
        }
    }

    Listing 7.4 Using building prefabs to create a city based on Perlin Noise height values.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class makeCity : MonoBehaviour {

        public GameObject[] buildings;

        void Start()
        {
            Perlin surface = new Perlin();
           
            Mesh mesh = this.GetComponent<MeshFilter>().mesh;
            Vector3[] vertices = mesh.vertices;
            float scalex = this.transform.localScale.x;
            float scalez = this.transform.localScale.z;
           
            for (int v = 0; v < vertices.Length; v++)
            {
                float perlinValue = surface.Noise(vertices[v].x * 2 + 0.1365143f,
                    vertices[v].z * 2 + 1.21688f) * 10;
               
                perlinValue =
                Mathf.Round((Mathf.Clamp(perlinValue,0,buildings.Length)));
                Instantiate(buildings[(int)perlinValue],
                    new Vector3(vertices[v].x * scalex,
                        vertices[v].y, vertices[v].z * scalez),
                    buildings[(int)perlinValue].transform.rotation);   

            }
           
            mesh.vertices = vertices;
            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
           
            this.gameObject.AddComponent<MeshCollider>();
        }
    }

    Listing 7.5 Set the Sun’s light type to directional.

    1
    2
    3
    4
    5
    6
    void Start ()
    {
        sunLight = new GameObject("Sun");
        sunLight.AddComponent<Light>();
        sunLight.GetComponent<Light>().type = LightType.Directional;
    }

    Chapter 8

    Listing 8.1 Unity C# to report on mobile device acceleration.

    1
    2
    3
    Input.acceleration.x
    Input.acceleration.y
    Input.acceleration.z

    Listing 8.2 Unity C# to report on device orientation.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class orient : MonoBehaviour {

        string orientString = "Unknown";
        Vector2 pivotPoint = new Vector2(200,200);
        float rotAngle = 0;
        void OnGUI()
        {
            if (Input.deviceOrientation == DeviceOrientation.FaceDown)
            {
                orientString = "Face Down";
                rotAngle = 0;
            }
            if (Input.deviceOrientation == DeviceOrientation.FaceUp)
            {
                orientString = "Face Up";
                rotAngle = 0;
            }
            if (Input.deviceOrientation == DeviceOrientation.Portrait)
            {
                orientString = "Portrait";
                rotAngle = 0;
            }
            if (Input.deviceOrientation == DeviceOrientation.PortraitUpsideDown)
            {
                orientString = "Portrait Upside Down";
                rotAngle = 180;
            }
            if (Input.deviceOrientation == DeviceOrientation.LandscapeLeft)
            {
                orientString = "Landscape Left";
                rotAngle = 90;
            }
            if (Input.deviceOrientation == DeviceOrientation.LandscapeRight)
            {
                orientString = "Landscape Right";
                rotAngle = -90;
            }
            GUI.BeginGroup (new Rect (Screen.width / 2 - 200,
                Screen.height / 2 - 200, 400, 400));
            GUIUtility.RotateAroundPivot (rotAngle, pivotPoint);
            GUI.Label (new Rect (0, 0, 400, 400), orientString);
            GUI.EndGroup ();
        }
    }

    Listing 8.3 Sending a simple email message from Unity application.
    Code is the same but change “function” for “void”

    Listing 8.4 Prepopulation email data in a Unity application before sending.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class sendEmail : MonoBehaviour {

        string toEmail = "test@email.com";
        string nameField = "My Name";
        string messageField = "Message";
        void OnGUI()
        {
            GUI.Label(new Rect(10,10,50,50),"To");
            toEmail = GUI.TextField (new Rect (100, 10, 200, 50), toEmail);
            GUI.Label(new Rect(10,60,50,50),"Name");
            nameField = GUI.TextField (new Rect (100, 60, 200, 50),
                nameField);
            GUI.Label(new Rect(10,120,120,50),"Message");
            messageField = GUI.TextArea(new Rect(100, 120, 200, 200),
                messageField);
            if(GUI.Button(new Rect(50,350,180,50),"Send"))
            {
                Application.OpenURL("mailto:" + toEmail + "?" +
                    "subject=Test Email&" +
                    "body=From: " +
                    nameField + "\n" + messageField);
            }
        }
    }

    Listing 8.5 Sending data from a Unity app to a webserver for the purpose of emailing.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class emailer : MonoBehaviour {

        string toEmail = "test@email.com";
        string nameField = "My Name";
        string messageField = "Message";
        bool showWebMessage = false;
        int emailSuccessful = 1;

        void webMessageScreen()
        {
            GUI.BeginGroup (new Rect (Screen.width / 2 - 50,
                Screen.height / 2 - 50, 100, 100));
            GUI.Box (new Rect (0,0,100,100), "Email Message");
            if(emailSuccessful == 1)
            GUI.Label(new Rect(10,20,100,50),"Sending...");
            if(emailSuccessful == 2)
            GUI.Label(new Rect(10,20,100,50),"Email Sent");
            else if(emailSuccessful == 3)
            GUI.Label(new Rect(10,20,100,50),"Email Failed");
            if (GUI.Button (new Rect (10,50,80,30), "Ok"))
            {
                showWebMessage = false;
            }
            GUI.EndGroup ();
        }

        IEnumerator sendEmail()
        {
            emailSuccessful = 1;
            showWebMessage = true;
            string msgBody = "From: " + nameField + "\n" + messageField;
            WWWForm form = new WWWForm();
            form.AddField("to", toEmail);
            form.AddField("subject", "Email Subject");
            form.AddField("body", msgBody);
            WWW w = new WWW("URL OF YOUR PHP SCRIPT", form);
            yield return w;
            if (w.error != null)
            {
                emailSuccessful = 3;
            }
            else
            {
                emailSuccessful = 2;
            }
        }

        void OnGUI()
        {
            GUI.Label(new Rect(10,10,50,50),"To");
            toEmail = GUI.TextField (new Rect (100, 10, 200, 50), toEmail);
            GUI.Label(new Rect(10,60,50,50),"Name");
            nameField = GUI.TextField (new Rect (100, 60, 200, 50), nameField);
            GUI.Label(new Rect(10,120,120,50),"Message");

            messageField = GUI.TextArea (new Rect (100, 120, 200, 200),
                messageField);
            if(GUI.Button(new Rect(50,350,180,50),"Send"))
            {
                StartCoroutine(sendEmail());
            }
            if(showWebMessage)
            {
                webMessageScreen();
            }
        }
    }

    Listing 8.6 PHP web emailer.
    Code unchanged

    Listing 8.7 Code to Obtain GPS Information

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class gps : MonoBehaviour {

        bool locationFound = false;

        IEnumerator GetLocation()
        {
            Input.location.Start();
            int maxWait = 1;
            while (Input.location.status == LocationServiceStatus.Initializing &&
                maxWait > 0)
            {
                yield return new WaitForSeconds(1);
                maxWait--;
            }
            if (maxWait < 1)
            {
                yield return null;
            }
            if (Input.location.status == LocationServiceStatus.Failed)
            {
                yield return null;
            }
            else
            {
                locationFound = true;
            }
            Input.location.Stop();
        }

        void Start ()
        {
            StartCoroutine(GetLocation());
        }

        void OnGUI()
        {
            if(locationFound)
            {
                GUI.Label(new Rect(10,10,200,30),"Latitude: " +
                    Input.location.lastData.latitude);
                GUI.Label(new Rect(10,30,200,30),"Longitude: " +
                    Input.location.lastData.longitude);
                GUI.Label(new Rect(10,50,200,30),"Altitude: " +
                    Input.location.lastData.altitude);
                GUI.Label(new Rect(10,70,200,30),"Accuracy: " +
                    Input.location.lastData.horizontalAccuracy);
                GUI.Label(new Rect(10,90,200,30),"Time: " +
                    Input.location.lastData.timestamp);
            }
            else
            {
                GUI.Label(new Rect(10,10,200,30),
                    "Could not initialise location services.");
            }
            if(GUI.Button(new Rect(10,110,80,50),"Quit"))
            {
                Application.Quit();
            }
        }
    }

    Listing 8.8 Detect.
    Includes all C# files for exercise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //swipeTrail.cs
    using UnityEngine;
    using System.Collections;

    public class swipeTrail : MonoBehaviour {

        void Update () {
            if(((Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
                || Input.GetMouseButton(0)))
            {
                Vector3 touchPos = Camera.main.ScreenToWorldPoint(new
                    Vector3(Input.mousePosition.x,
                        Input.mousePosition.y, Camera.main.farClipPlane));
                this.transform.position = new Vector3(touchPos.x,touchPos.y,0);
            }
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //detectMotion.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class detectMotion : MonoBehaviour {

        bool isDown = false;
        bool startPointRecording = false;
        Vector2 lastMousePos = new Vector2(-1,-1);
        ArrayList points = new ArrayList();
        string patternDetected = "";
        Text patternDisplay;

        void OnGUI()
        {
            if(isDown && Input.mousePosition.x != lastMousePos.x &&
                Input.mousePosition.y != lastMousePos.x)
            {
                points.Add(new Vector2(Input.mousePosition.x, -Input.mousePosition.y));
                lastMousePos = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
            }
        }


        void Update ()
        {
            if(Input.GetMouseButtonDown(0))
            {
                isDown = true;
                startPointRecording = true;
                points = new ArrayList();
            }
            else if(Input.GetMouseButtonUp(0))
            {
                isDown = false;
                startPointRecording = false;
                Result result = this.GetComponent<gestureRecon>().Detect(points);
                patternDetected = result.Rname + " " + result.Rscore;
                patternDisplay.text = patternDetected;
            }
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    //gestureRecon.cs
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class Point
    {
        public float X = 0;
        public float Y = 0;
        public Point(float x, float y)
        {
            this.X = x;
            this.Y = y;
        }
    }

    class Rectangle
    {
        public float X = 0;
        public float Y = 0;
        public float Width = 0;
        public float Height = 0;
        public Rectangle(float x, float y, float width, float height)
        {
            this.X = x;
            this.Y = y;
            this.Width = width;
            this.Height = height;
        }
    }

    public class Result
    {
        public string Rname;
        public float Rscore;
        public Result(string n, float s)
        {
            Rname = n;
            Rscore = s;
        }
    }


    public class gestureRecon {

        string Rname;
        float Rscore;
        ArrayList Templates;



        class Template
        {
            public string Tname;
            public static float TScore;
            public int NumTemplates = 16;
            public static int NumPoints = 64;
            public static float SquareSize = 250.0f;
            public static Point Origin = new Point(0,0);
            public static float Diagonal = Mathf.Sqrt(SquareSize * SquareSize + SquareSize * SquareSize);
            public static float HalfDiagonal = 0.5f * Diagonal;
            public static float AngleRange = Deg2Rad(45.0f);
            public static float AnglePrecision = Deg2Rad(2.0f);
            public static float Phi = 0.5f * (-1.0f + Mathf.Sqrt(5.0f));
            public ArrayList points = new ArrayList();

            public Template(string name, ArrayList points)
            {
                this.Tname = name;
                this.points = Resample(points, NumPoints);
                var radians = IndicativeAngle(this.points);
                this.points = RotateBy(this.points, -radians);
                this.points = ScaleTo(this.points, SquareSize);
                this.points = TranslateTo(this.points, Origin);

            }

            static public ArrayList Resample(ArrayList points, int n)
            {
                float I = PathCount(points) / (n - 1);
                float D = 0.0f;
                ArrayList newpoints = new ArrayList();
                for (int i = 1; i < points.Count; i++)
                {
                    Point point1 = points[i-1] as Point;
                    Point point2 = points[i] as Point;
                    float d = Distance(point1, point2);
                    if ((D + d) >= I)
                    {
                        float qx = point1.X + ((I - D) / d) * (point2.X - point1.X);
                        float qy = point1.Y + ((I - D) / d) * (point2.Y - point1.Y);
                        Point q = new Point(qx, qy);
                        newpoints[newpoints.Count] = q;
                        points.Insert(i,q);
                        D = 0.0f;
                    }
                    else D += d;
                }

                if (newpoints.Count == n - 1)
                {
                    Point p = points[points.Count - 1] as Point;
                    newpoints[newpoints.Count] = new Point(p.X, p.Y);
                }
                return newpoints;
            }

            static public ArrayList ScaleTo(ArrayList points, float size)
            {
                Rectangle B = BoundingBox(points);
                ArrayList newpoints = new ArrayList();
                Point p;
                for (int i = 0; i < points.Count; i++)
                {
                    p = points[i] as Point;
                    float qx = p.X * (size / B.Width);
                    float qy = p.Y * (size / B.Height);
                    newpoints[newpoints.Count] = new Point(qx, qy);
                }
                return newpoints;
            }

            static public ArrayList TranslateTo(ArrayList points, Point pt)
            {
                Point c = Centroid(points);
                ArrayList newpoints = new ArrayList();
                Point p;
                for (int i = 0; i < points.Count; i++)
                {
                    p = points[i] as Point;
                    float qx = p.X + pt.X - c.X;
                    float qy = p.Y + pt.Y - c.Y;
                    newpoints[newpoints.Count] = new Point(qx, qy);
                }
                return newpoints;
            }  

            static public Rectangle BoundingBox(ArrayList points)
            {
                float minX = 20000;
                float maxX = -1;
                float minY = 20000;
                float maxY = -1;
                Point p;
                for (int i = 0; i < points.Count; i++)
                {
                    p = points[i] as Point;
                    if (p.X < minX)
                    minX = p.X;
                    if (p.X > maxX)
                    maxX = p.X;
                    if (p.Y < minY)
                    minY = p.Y;
                    if (p.Y > maxY)
                    maxY = p.Y;
                }
                return new Rectangle(minX, minY, maxX - minX, maxY - minY);
            }

            static public ArrayList RotateBy(ArrayList points, float radians)
            {
                Point c = Centroid(points);
                float cos = (float) Mathf.Cos(radians);
                float sin = (float) Mathf.Sin(radians);

                ArrayList newpoints = new ArrayList();
                Point p;
                for (int i = 0; i < points.Count; i++)
                {
                    p = points[i] as Point;
                    float qx = (p.X - c.X) * cos - (p.Y - c.Y) * sin + c.X;
                    float qy = (p.X - c.X) * sin + (p.Y - c.Y) * cos + c.Y;
                    newpoints[newpoints.Count] = new Point(qx, qy);
                }
                return newpoints;
            }


            static public Point Centroid(ArrayList points)
            {
                float x = 0.0f;
                float y = 0.0f;
                Point p;
                for (int i = 0; i < points.Count; i++)
                {
                    p = points[i] as Point;
                    x += p.X;
                    y += p.Y;
                }
                x /= points.Count;
                y /= points.Count;
                return new Point(x, y);
            }

            static public float IndicativeAngle(ArrayList points)
            {
                Point c = Centroid(points);
                Point p = points[0] as Point;
                return (float) Mathf.Atan2(c.Y - p.Y, c.X - p.X);
            }  

            static public float PathCount(ArrayList points)
            {
                float d = 0.0f;
                for (int i = 1; i < points.Count; i++)
                d += Distance(points[i - 1] as Point, points[i] as Point);
                return d;
            }  

            static public float Distance(Point p1, Point p2)
            {
                var dx = p2.X - p1.X;
                var dy = p2.Y - p1.Y;
                return (float) Mathf.Sqrt(dx * dx + dy * dy);
            }

            static public float Deg2Rad(float d) { return (float)(d * Mathf.PI / 180.0); }
            static public float Rad2Deg(float r) { return (float)(r * 180.0 / Mathf.PI); }

        }


        float PathDistance(ArrayList pts1, ArrayList pts2)
        {
            float d = 0.0f;
           
            for (int i = 0; i < pts1.Count; i++)
            {
                if(i < pts2.Count) //ensure pattern has enough points to compare
                {
                    d += Template.Distance(pts1[i] as Point, pts2[i] as Point);
                }
            }
            return d / pts1.Count;
        }

        float DistanceAtAngle(ArrayList points, Template T, float radians)
        {
            var newpoints = Template.RotateBy(points, radians);
            return PathDistance(newpoints, T.points);
        }

        float DistanceAtBestAngle(ArrayList points, Template T, float a, float b, float threshold)
        {      
            float x1 = (float)(Template.Phi * a + (1.0 - Template.Phi) * b);       
            float f1 = (float)(DistanceAtAngle(points, T, x1));
            float x2 = (float)((1.0 - Template.Phi) * a + Template.Phi * b);
            float f2 = (float)(DistanceAtAngle(points, T, x2));
            while (Mathf.Abs(b - a) > threshold)
            {
                if (f1 < f2)
                {
                    b = x2;
                    x2 = x1;
                    f2 = f1;
                    x1 = (float)(Template.Phi * a + (1.0 - Template.Phi) * b);
                    f1 = (float)(DistanceAtAngle(points, T, x1));
                }
                else
                {
                    a = x1;
                    x1 = x2;
                    f1 = f2;
                    x2 = (float)((1.0 - Template.Phi) * a + Template.Phi * b);
                    f2 = (float)(DistanceAtAngle(points, T, x2));
                }
            }
            return (float) Mathf.Min(f1, f2);
        }

        public Result Detect(ArrayList points)
        {
            points = Template.Resample(points, Template.NumPoints);
            float radians = Template.IndicativeAngle(points);
            points = Template.RotateBy(points, -radians);
            points = Template.ScaleTo(points, Template.SquareSize);
            points = Template.TranslateTo(points, Template.Origin);

            float b = 20000;
            int t = 0;
            for (int i = 0; i < this.Templates.Count; i++)
            {
                float d = DistanceAtBestAngle(points, this.Templates[i] as Template, (float)-Template.AngleRange, (float)Template.AngleRange, (float)Template.AnglePrecision);
                if (d < b)
                {
                    b = d;
                    t = i;
                   
                    Template tpl = this.Templates[t] as Template;
                    Debug.Log("Results " + tpl.Tname + " " + (b / Template.HalfDiagonal));
                }
            }
            float score = (float)(1.0 - (b / Template.HalfDiagonal));
            Template tp = this.Templates[t] as Template;
            return new Result(tp.Tname, score);
        }

        void Start()
        {
            Debug.Log("Loading Patterns");
            this.Templates = new ArrayList();

            this.Templates[0] = new Template("triangle", new ArrayList(new Point[]{new Point(137,139), new Point(135,141), new Point(133,144), new Point(132,146), new Point(130,149), new Point(128,151), new Point(126,155), new Point(123,160), new Point(120,166), new Point(116,171), new Point(112,177), new Point(107,183), new Point(102,188), new Point(100,191), new Point(95,195), new Point(90,199), new Point(86,203), new Point(82,206), new Point(80,209), new Point(75,213), new Point(73,213), new Point(70,216), new Point(67,219), new Point(64,221), new Point(61,223), new Point(60,225), new Point(62,226), new Point(65,225), new Point(67,226), new Point(74,226), new Point(77,227), new Point(85,229), new Point(91,230), new Point(99,231), new Point(108,232), new Point(116,233), new Point(125,233), new Point(134,234), new Point(145,233), new Point(153,232), new Point(160,233), new Point(170,234), new Point(177,235), new Point(179,236), new Point(186,237), new Point(193,238), new Point(198,239), new Point(200,237), new Point(202,239), new Point(204,238), new Point(206,234), new Point(205,230), new Point(202,222), new Point(197,216), new Point(192,207), new Point(186,198), new Point(179,189), new Point(174,183), new Point(170,178), new Point(164,171), new Point(161,168), new Point(154,160), new Point(148,155), new Point(143,150), new Point(138,148), new Point(136,148)}));
            Debug.Log("Loading 0 " + (this.Templates[0] as Template).points.Count.ToString());

            this.Templates[1] = new Template("x", new ArrayList(new Point[]{new Point(87,142), new Point(89,145), new Point(91,148), new Point(93,151), new Point(96,155), new Point(98,157), new Point(100,160), new Point(102,162), new Point(106,167), new Point(108,169), new Point(110,171), new Point(115,177), new Point(119,183), new Point(123,189), new Point(127,193), new Point(129,196), new Point(133,200), new Point(137,206), new Point(140,209), new Point(143,212), new Point(146,215), new Point(151,220), new Point(153,222), new Point(155,223), new Point(157,225), new Point(158,223), new Point(157,218), new Point(155,211), new Point(154,208), new Point(152,200), new Point(150,189), new Point(148,179), new Point(147,170), new Point(147,158), new Point(147,148), new Point(147,141), new Point(147,136), new Point(144,135), new Point(142,137), new Point(140,139), new Point(135,145), new Point(131,152), new Point(124,163), new Point(116,177), new Point(108,191), new Point(100,206), new Point(94,217), new Point(91,222), new Point(89,225), new Point(87,226), new Point(87,224)}));
            Debug.Log("Loading 1 " + (this.Templates[1] as Template).points.Count.ToString()); 

            this.Templates[2] = new Template("rectangle", new ArrayList(new Point[]{new Point(78,149), new Point(78,153), new Point(78,157), new Point(78,160), new Point(79,162), new Point(79,164), new Point(79,167), new Point(79,169), new Point(79,173), new Point(79,178), new Point(79,183), new Point(80,189), new Point(80,193), new Point(80,198), new Point(80,202), new Point(81,208), new Point(81,210), new Point(81,216), new Point(82,222), new Point(82,224), new Point(82,227), new Point(83,229), new Point(83,231), new Point(85,230), new Point(88,232), new Point(90,233), new Point(92,232), new Point(94,233), new Point(99,232), new Point(102,233), new Point(106,233), new Point(109,234), new Point(117,235), new Point(123,236), new Point(126,236), new Point(135,237), new Point(142,238), new Point(145,238), new Point(152,238), new Point(154,239), new Point(165,238), new Point(174,237), new Point(179,236), new Point(186,235), new Point(191,235), new Point(195,233), new Point(197,233), new Point(200,233), new Point(201,235), new Point(201,233), new Point(199,231), new Point(198,226), new Point(198,220), new Point(196,207), new Point(195,195), new Point(195,181), new Point(195,173), new Point(195,163), new Point(194,155), new Point(192,145), new Point(192,143), new Point(192,138), new Point(191,135), new Point(191,133), new Point(191,130), new Point(190,128), new Point(188,129), new Point(186,129), new Point(181,132), new Point(173,131), new Point(162,131), new Point(151,132), new Point(149,132), new Point(138,132), new Point(136,132), new Point(122,131), new Point(120,131), new Point(109,130), new Point(107,130), new Point(90,132), new Point(81,133), new Point(76,133)}));
            Debug.Log("Loading 2 " + (this.Templates[2] as Template).points.Count.ToString());

            this.Templates[3] = new Template("circle", new ArrayList(new Point[]{new Point(127,141), new Point(124,140), new Point(120,139), new Point(118,139), new Point(116,139), new Point(111,140), new Point(109,141), new Point(104,144), new Point(100,147), new Point(96,152), new Point(93,157), new Point(90,163), new Point(87,169), new Point(85,175), new Point(83,181), new Point(82,190), new Point(82,195), new Point(83,200), new Point(84,205), new Point(88,213), new Point(91,216), new Point(96,219), new Point(103,222), new Point(108,224), new Point(111,224), new Point(120,224), new Point(133,223), new Point(142,222), new Point(152,218), new Point(160,214), new Point(167,210), new Point(173,204), new Point(178,198), new Point(179,196), new Point(182,188), new Point(182,177), new Point(178,167), new Point(170,150), new Point(163,138), new Point(152,130), new Point(143,129), new Point(140,131), new Point(129,136), new Point(126,139)}));
            Debug.Log("Loading 3 " + (this.Templates[3] as Template).points.Count.ToString());

            this.Templates[4] = new Template("check", new ArrayList(new Point[]{new Point(91,185), new Point(93,185), new Point(95,185), new Point(97,185), new Point(100,188), new Point(102,189), new Point(104,190), new Point(106,193), new Point(108,195), new Point(110,198), new Point(112,201), new Point(114,204), new Point(115,207), new Point(117,210), new Point(118,212), new Point(120,214), new Point(121,217), new Point(122,219), new Point(123,222), new Point(124,224), new Point(126,226), new Point(127,229), new Point(129,231), new Point(130,233), new Point(129,231), new Point(129,228), new Point(129,226), new Point(129,224), new Point(129,221), new Point(129,218), new Point(129,212), new Point(129,208), new Point(130,198), new Point(132,189), new Point(134,182), new Point(137,173), new Point(143,164), new Point(147,157), new Point(151,151), new Point(155,144), new Point(161,137), new Point(165,131), new Point(171,122), new Point(174,118), new Point(176,114), new Point(177,112), new Point(177,114), new Point(175,116), new Point(173,118)}));
            Debug.Log("Loading 4 " + (this.Templates[4] as Template).points.Count.ToString());

            this.Templates[5] = new Template("caret", new ArrayList(new Point[]{new Point(79,245), new Point(79,242), new Point(79,239), new Point(80,237), new Point(80,234), new Point(81,232), new Point(82,230), new Point(84,224), new Point(86,220), new Point(86,218), new Point(87,216), new Point(88,213), new Point(90,207), new Point(91,202), new Point(92,200), new Point(93,194), new Point(94,192), new Point(96,189), new Point(97,186), new Point(100,179), new Point(102,173), new Point(105,165), new Point(107,160), new Point(109,158), new Point(112,151), new Point(115,144), new Point(117,139), new Point(119,136), new Point(119,134), new Point(120,132), new Point(121,129), new Point(122,127), new Point(124,125), new Point(126,124), new Point(129,125), new Point(131,127), new Point(132,130), new Point(136,139), new Point(141,154), new Point(145,166), new Point(151,182), new Point(156,193), new Point(157,196), new Point(161,209), new Point(162,211), new Point(167,223), new Point(169,229), new Point(170,231), new Point(173,237), new Point(176,242), new Point(177,244), new Point(179,250), new Point(181,255), new Point(182,257)}));
            Debug.Log("Loading 5 " + (this.Templates[5] as Template).points.Count.ToString());

            this.Templates[6] = new Template("zig-zag", new ArrayList(new Point[]{new Point(307,216), new Point(333,186), new Point(356,215), new Point(375,186), new Point(399,216), new Point(418,186)}));
            Debug.Log("Loading 6 " + (this.Templates[6] as Template).points.Count.ToString());

            this.Templates[7] = new Template("arrow", new ArrayList(new Point[]{new Point(68,222), new Point(70,220), new Point(73,218), new Point(75,217), new Point(77,215), new Point(80,213), new Point(82,212), new Point(84,210), new Point(87,209), new Point(89,208), new Point(92,206), new Point(95,204), new Point(101,201), new Point(106,198), new Point(112,194), new Point(118,191), new Point(124,187), new Point(127,186), new Point(132,183), new Point(138,181), new Point(141,180), new Point(146,178), new Point(154,173), new Point(159,171), new Point(161,170), new Point(166,167), new Point(168,167), new Point(171,166), new Point(174,164), new Point(177,162), new Point(180,160), new Point(182,158), new Point(183,156), new Point(181,154), new Point(178,153), new Point(171,153), new Point(164,153), new Point(160,153), new Point(150,154), new Point(147,155), new Point(141,157), new Point(137,158), new Point(135,158), new Point(137,158), new Point(140,157), new Point(143,156), new Point(151,154), new Point(160,152), new Point(170,149), new Point(179,147), new Point(185,145), new Point(192,144), new Point(196,144), new Point(198,144), new Point(200,144), new Point(201,147), new Point(199,149), new Point(194,157), new Point(191,160), new Point(186,167), new Point(180,176), new Point(177,179), new Point(171,187), new Point(169,189), new Point(165,194), new Point(164,196)}));
            Debug.Log("Loading 7 " + (this.Templates[7] as Template).points.Count.ToString());

            this.Templates[8] = new Template("left square bracket", new ArrayList(new Point[]{new Point(140,124), new Point(138,123), new Point(135,122), new Point(133,123), new Point(130,123), new Point(128,124), new Point(125,125), new Point(122,124), new Point(120,124), new Point(118,124), new Point(116,125), new Point(113,125), new Point(111,125), new Point(108,124), new Point(106,125), new Point(104,125), new Point(102,124), new Point(100,123), new Point(98,123), new Point(95,124), new Point(93,123), new Point(90,124), new Point(88,124), new Point(85,125), new Point(83,126), new Point(81,127), new Point(81,129), new Point(82,131), new Point(82,134), new Point(83,138), new Point(84,141), new Point(84,144), new Point(85,148), new Point(85,151), new Point(86,156), new Point(86,160), new Point(86,164), new Point(86,168), new Point(87,171), new Point(87,175), new Point(87,179), new Point(87,182), new Point(87,186), new Point(88,188), new Point(88,195), new Point(88,198), new Point(88,201), new Point(88,207), new Point(89,211), new Point(89,213), new Point(89,217), new Point(89,222), new Point(88,225), new Point(88,229), new Point(88,231), new Point(88,233), new Point(88,235), new Point(89,237), new Point(89,240), new Point(89,242), new Point(91,241), new Point(94,241), new Point(96,240), new Point(98,239), new Point(105,240), new Point(109,240), new Point(113,239), new Point(116,240), new Point(121,239), new Point(130,240), new Point(136,237), new Point(139,237), new Point(144,238), new Point(151,237), new Point(157,236), new Point(159,237)}));
            Debug.Log("Loading 8 " + (this.Templates[8] as Template).points.Count.ToString());

            this.Templates[9] = new Template("right square bracket", new ArrayList(new Point[]{new Point(112,138), new Point(112,136), new Point(115,136), new Point(118,137), new Point(120,136), new Point(123,136), new Point(125,136), new Point(128,136), new Point(131,136), new Point(134,135), new Point(137,135), new Point(140,134), new Point(143,133), new Point(145,132), new Point(147,132), new Point(149,132), new Point(152,132), new Point(153,134), new Point(154,137), new Point(155,141), new Point(156,144), new Point(157,152), new Point(158,161), new Point(160,170), new Point(162,182), new Point(164,192), new Point(166,200), new Point(167,209), new Point(168,214), new Point(168,216), new Point(169,221), new Point(169,223), new Point(169,228), new Point(169,231), new Point(166,233), new Point(164,234), new Point(161,235), new Point(155,236), new Point(147,235), new Point(140,233), new Point(131,233), new Point(124,233), new Point(117,235), new Point(114,238), new Point(112,238)}));
            Debug.Log("Loading 9 " + (this.Templates[9] as Template).points.Count.ToString());     

            this.Templates[10] = new Template("v", new ArrayList(new Point[]{new Point(89,164), new Point(90,162), new Point(92,162), new Point(94,164), new Point(95,166), new Point(96,169), new Point(97,171), new Point(99,175), new Point(101,178), new Point(103,182), new Point(106,189), new Point(108,194), new Point(111,199), new Point(114,204), new Point(117,209), new Point(119,214), new Point(122,218), new Point(124,222), new Point(126,225), new Point(128,228), new Point(130,229), new Point(133,233), new Point(134,236), new Point(136,239), new Point(138,240), new Point(139,242), new Point(140,244), new Point(142,242), new Point(142,240), new Point(142,237), new Point(143,235), new Point(143,233), new Point(145,229), new Point(146,226), new Point(148,217), new Point(149,208), new Point(149,205), new Point(151,196), new Point(151,193), new Point(153,182), new Point(155,172), new Point(157,165), new Point(159,160), new Point(162,155), new Point(164,150), new Point(165,148), new Point(166,146)}));
            Debug.Log("Loading 10 " + (this.Templates[10] as Template).points.Count.ToString());       

            this.Templates[11] = new Template("delete", new ArrayList(new Point[]{new Point(123,129), new Point(123,131), new Point(124,133), new Point(125,136), new Point(127,140), new Point(129,142), new Point(133,148), new Point(137,154), new Point(143,158), new Point(145,161), new Point(148,164), new Point(153,170), new Point(158,176), new Point(160,178), new Point(164,183), new Point(168,188), new Point(171,191), new Point(175,196), new Point(178,200), new Point(180,202), new Point(181,205), new Point(184,208), new Point(186,210), new Point(187,213), new Point(188,215), new Point(186,212), new Point(183,211), new Point(177,208), new Point(169,206), new Point(162,205), new Point(154,207), new Point(145,209), new Point(137,210), new Point(129,214), new Point(122,217), new Point(118,218), new Point(111,221), new Point(109,222), new Point(110,219), new Point(112,217), new Point(118,209), new Point(120,207), new Point(128,196), new Point(135,187), new Point(138,183), new Point(148,167), new Point(157,153), new Point(163,145), new Point(165,142), new Point(172,133), new Point(177,127), new Point(179,127), new Point(180,125)}));
            Debug.Log("Loading 11 " + (this.Templates[11] as Template).points.Count.ToString());       

            this.Templates[12] = new Template("left curly brace", new ArrayList(new Point[]{new Point(150,116), new Point(147,117), new Point(145,116), new Point(142,116), new Point(139,117), new Point(136,117), new Point(133,118), new Point(129,121), new Point(126,122), new Point(123,123), new Point(120,125), new Point(118,127), new Point(115,128), new Point(113,129), new Point(112,131), new Point(113,134), new Point(115,134), new Point(117,135), new Point(120,135), new Point(123,137), new Point(126,138), new Point(129,140), new Point(135,143), new Point(137,144), new Point(139,147), new Point(141,149), new Point(140,152), new Point(139,155), new Point(134,159), new Point(131,161), new Point(124,166), new Point(121,166), new Point(117,166), new Point(114,167), new Point(112,166), new Point(114,164), new Point(116,163), new Point(118,163), new Point(120,162), new Point(122,163), new Point(125,164), new Point(127,165), new Point(129,166), new Point(130,168), new Point(129,171), new Point(127,175), new Point(125,179), new Point(123,184), new Point(121,190), new Point(120,194), new Point(119,199), new Point(120,202), new Point(123,207), new Point(127,211), new Point(133,215), new Point(142,219), new Point(148,220), new Point(151,221)}));
            Debug.Log("Loading 12 " + (this.Templates[12] as Template).points.Count.ToString());       

            this.Templates[13] = new Template("right curly brace", new ArrayList(new Point[]{new Point(117,132), new Point(115,132), new Point(115,129), new Point(117,129), new Point(119,128), new Point(122,127), new Point(125,127), new Point(127,127), new Point(130,127), new Point(133,129), new Point(136,129), new Point(138,130), new Point(140,131), new Point(143,134), new Point(144,136), new Point(145,139), new Point(145,142), new Point(145,145), new Point(145,147), new Point(145,149), new Point(144,152), new Point(142,157), new Point(141,160), new Point(139,163), new Point(137,166), new Point(135,167), new Point(133,169), new Point(131,172), new Point(128,173), new Point(126,176), new Point(125,178), new Point(125,180), new Point(125,182), new Point(126,184), new Point(128,187), new Point(130,187), new Point(132,188), new Point(135,189), new Point(140,189), new Point(145,189), new Point(150,187), new Point(155,186), new Point(157,185), new Point(159,184), new Point(156,185), new Point(154,185), new Point(149,185), new Point(145,187), new Point(141,188), new Point(136,191), new Point(134,191), new Point(131,192), new Point(129,193), new Point(129,195), new Point(129,197), new Point(131,200), new Point(133,202), new Point(136,206), new Point(139,211), new Point(142,215), new Point(145,220), new Point(147,225), new Point(148,231), new Point(147,239), new Point(144,244), new Point(139,248), new Point(134,250), new Point(126,253), new Point(119,253), new Point(115,253)}));
            Debug.Log("Loading 13 " + (this.Templates[13] as Template).points.Count.ToString());       

            this.Templates[14] = new Template("star", new ArrayList(new Point[]{new Point(75,250), new Point(75,247), new Point(77,244), new Point(78,242), new Point(79,239), new Point(80,237), new Point(82,234), new Point(82,232), new Point(84,229), new Point(85,225), new Point(87,222), new Point(88,219), new Point(89,216), new Point(91,212), new Point(92,208), new Point(94,204), new Point(95,201), new Point(96,196), new Point(97,194), new Point(98,191), new Point(100,185), new Point(102,178), new Point(104,173), new Point(104,171), new Point(105,164), new Point(106,158), new Point(107,156), new Point(107,152), new Point(108,145), new Point(109,141), new Point(110,139), new Point(112,133), new Point(113,131), new Point(116,127), new Point(117,125), new Point(119,122), new Point(121,121), new Point(123,120), new Point(125,122), new Point(125,125), new Point(127,130), new Point(128,133), new Point(131,143), new Point(136,153), new Point(140,163), new Point(144,172), new Point(145,175), new Point(151,189), new Point(156,201), new Point(161,213), new Point(166,225), new Point(169,233), new Point(171,236), new Point(174,243), new Point(177,247), new Point(178,249), new Point(179,251), new Point(180,253), new Point(180,255), new Point(179,257), new Point(177,257), new Point(174,255), new Point(169,250), new Point(164,247), new Point(160,245), new Point(149,238), new Point(138,230), new Point(127,221), new Point(124,220), new Point(112,212), new Point(110,210), new Point(96,201), new Point(84,195), new Point(74,190), new Point(64,182), new Point(55,175), new Point(51,172), new Point(49,170), new Point(51,169), new Point(56,169), new Point(66,169), new Point(78,168), new Point(92,166), new Point(107,164), new Point(123,161), new Point(140,162), new Point(156,162), new Point(171,160), new Point(173,160), new Point(186,160), new Point(195,160), new Point(198,161), new Point(203,163), new Point(208,163), new Point(206,164), new Point(200,167), new Point(187,172), new Point(174,179), new Point(172,181), new Point(153,192), new Point(137,201), new Point(123,211), new Point(112,220), new Point(99,229), new Point(90,237), new Point(80,244), new Point(73,250), new Point(69,254), new Point(69,252)}));
            Debug.Log("Loading 14 " + (this.Templates[14] as Template).points.Count.ToString());       

            this.Templates[15] = new Template("cake", new ArrayList(new Point[]{new Point(81,219), new Point(84,218), new Point(86,220), new Point(88,220), new Point(90,220), new Point(92,219), new Point(95,220), new Point(97,219), new Point(99,220), new Point(102,218), new Point(105,217), new Point(107,216), new Point(110,216), new Point(113,214), new Point(116,212), new Point(118,210), new Point(121,208), new Point(124,205), new Point(126,202), new Point(129,199), new Point(132,196), new Point(136,191), new Point(139,187), new Point(142,182), new Point(144,179), new Point(146,174), new Point(148,170), new Point(149,168), new Point(151,162), new Point(152,160), new Point(152,157), new Point(152,155), new Point(152,151), new Point(152,149), new Point(152,146), new Point(149,142), new Point(148,139), new Point(145,137), new Point(141,135), new Point(139,135), new Point(134,136), new Point(130,140), new Point(128,142), new Point(126,145), new Point(122,150), new Point(119,158), new Point(117,163), new Point(115,170), new Point(114,175), new Point(117,184), new Point(120,190), new Point(125,199), new Point(129,203), new Point(133,208), new Point(138,213), new Point(145,215), new Point(155,218), new Point(164,219), new Point(166,219), new Point(177,219), new Point(182,218), new Point(192,216), new Point(196,213), new Point(199,212), new Point(201,211)}));
            Debug.Log("Loading 15 " + (this.Templates[15] as Template).points.Count.ToString());

        }

    }