Hello Everybody,

This article is pulled from my website: www.IndieDevSpot.com and can be viewed here as well: http://indiedevspot.azurewebsites.net/2014/06/19/touch-input-moving-a-character-in-3d-environments-using-rays/

There are literally hundreds of millions of windows touch tablets out there running windows 8 these days, so it is almost mandatory these days to support touch input in some manner.  I am going to just provide some sample code below that has been commented.  Please note you can use these same techniques to select or unselect objects.  The sample below is a mouse and touch input that allows you to point and click, or touch a point in the 3D environment and the character will move to that point.

touchInput3D

 

If you ran through my Top Down tutorial series, some of this should look familiar, as we are still controlling in a 2D space really, just with 3D objects and letting gravity handle the y axis for us.

First, get the variables that you need.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//adjust the facing angle of this character to match world co-ordinates
    public float facingAngleAdjustment = 90f;
 
    //default maxSpeed
    public float maxSpeed = 10.0f;
 
    //our rigid body to cache
    private Rigidbody cachedRigidBody = null;
 
    //our animator
    private Animator cachedAnim = null;
 
    //destination to reach
    private Vector3 touchPoint;
 
    //which direction should we go?
    private Vector3 heading;
 
    //should we move somewhere?
    private bool dontMove = true;

Then, in update, check for touches or Mouse input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/// <summary>
    /// used for checking for information as fast as possible that
    /// does not necessarily make impact on game state.  Just check
    /// to see if we should, and then make those changes in fixed update.
    /// </summary>
    private void Update ()
    {
        //We are touch first, so check for that.
        if (Input.touchCount > 0)
        {
            //Handle Point Movement, send the touch position on the screen
            HandlePointMovement(Input.GetTouch(0).position);
        }
        else if(Input.GetButtonDown("Fire1"))//if no touch, fall back to see if a mouse click happened.
        {
            //Handle Point Movement, send the mouse position associated with this click.
            HandlePointMovement(Input.mousePosition);
        }
 
    }

Ok, but how are we actually handling the point movement?  Glad you asked, here it is…

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
/// <summary>
    /// Checks to see if there is a hit on a collider
    /// If there is, will set the new heading and save
    /// the destination point.  This will then allow
    /// movement to happen in fixed update.
    /// </summary>
    /// <param name="position"></param>
    private void HandlePointMovement(Vector3 position)
    {
        //the hit object, contains information about a hit.
        RaycastHit hit;
        //ray from where you touched or clicked projected in the orientation of the camera into the world
        Ray collisionRay = Camera.main.ScreenPointToRay(position);
        //returns a bool for if a hit happened or not, and resets hit (out) with correct information
        //100 is the layer to check against.  In this sample we check against all layers,
        //to optimize this check, we may want to check only for ground touches.
        if (Physics.Raycast(collisionRay, out hit, 100))
        {
            //touch point is used to determine if the character has reached the destination
            //therefor we set it to the destination
            this.touchPoint = new Vector3(hit.point.x, hit.point.y, hit.point.z);
            //vector mathematics, determine the direction the character needs to move.
            this.heading = this.touchPoint - this.transform.position;
            //don't move is the check for if the character should move or not.
            //since we want the character to move now, we set this to false.
            this.dontMove = false;
        }
    }

OK, so we have our variables, we have checked for input, we have done something with that input.
Now we actually make changes to our visual and physical properties.  We always do this in FixedUpdate()

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
/// <summary>
    /// update used for ensuring networked machines are kept in sync as well as
    /// for visual state updates.
    /// </summary>
    private void FixedUpdate()
    {
        //Distance formula for the x,z plane
        //sqrt (a^2 + b^2) = c (Pythagorean Theorum)
        //calc a^2
        float aSquared = this.transform.position.x - this.touchPoint.x;
        aSquared *= aSquared;
 
        //calc b^2
        float zSquared = this.transform.position.z - this.touchPoint.z;
        zSquared *= zSquared;
 
        //take sqrt for distance
        float distance = Mathf.Sqrt(aSquared + zSquared);
 
        //have we reached our threshold of our destination?
        if(distance < .5f)
        {
            //stop moving.
            this.dontMove = true;
        }
 
        //should we move?
        if (!dontMove)
        {
            //Ok, move in the heading set by our handlePointMovement function.
            this.Move(this.heading.x, this.heading.z);
        }
        else
        {
            //set heading to zero just in case
            this.heading = Vector3.zero;
            //remove all velocity from our character
            //except in the y direction as we don't control that
            //gravity does.
            this.cachedRigidBody.velocity = new Vector3(0.0f, this.cachedRigidBody.velocity.y, 0.0f);
            //zero out the animator, as we are no longer entering the move function
            this.cachedAnim.SetFloat("Speed", 0.0f);
        }
    }

OK, so what does the move function look like?  This is almost exactly like our 2D top down game.  In fact, I’m not really sure what the difference is off hand at the moment, except maybe name changes.

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
/// <summary>
    /// Move the character on the x,z plane as we use y for vertical in this game.
    /// Colliders will take care of y and our character doesn't fly.
    /// </summary>
    /// <param name="xMove"></param>
    /// <param name="zMove"></param>
    public void Move(float xMove, float zMove)
    {
        //Where should we move?
        Vector3 move = new Vector3(xMove, this.cachedRigidBody.velocity.y, zMove);
         
        //normalize this, so it has a magnitude of 1 such
        //that we are accurately portraying or max speed
        move.Normalize();
 
        //multiply by max speed
        //such that we don't move super slow.
        this.cachedRigidBody.velocity = move * this.maxSpeed;
 
        //calculate our total speed for the animator
        float speed = Mathf.Abs(this.cachedRigidBody.velocity.x) + Mathf.Abs(this.cachedRigidBody.velocity.z);
 
        //set that speed so the animation changes.
        this.cachedAnim.SetFloat("Speed", speed);
 
        //convert the vector into a radian angle,
        //convert to degrees and then adjust for the
        //object's starting orientation
        float angle = Mathf.Atan2(this.cachedRigidBody.velocity.z * -1, this.cachedRigidBody.velocity.x) * Mathf.Rad2Deg + facingAngleAdjustment;
 
        //don't rotate if we don't need to.
        if (speed > 0.0f)
        {
            //rotate by angle around the z axis.
            this.transform.rotation = Quaternion.AngleAxis(angle, new Vector3(0, 1, 0));
        }
    }

And just for reference, I’m pasting the whole class below for your reference and use :)
Please note that you will need a character with an animator that has a speed parameter and also a rigidbody attached to it for this to 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using UnityEngine;
using System.Collections;
 
public class CharacterController : MonoBehaviour {
 
    //adjust the facing angle of this character to match world co-ordinates
    public float facingAngleAdjustment = 90f;
 
    //default maxSpeed
    public float maxSpeed = 10.0f;
 
    //our rigid body to cache
    private Rigidbody cachedRigidBody = null;
 
    //our animator
    private Animator cachedAnim = null;
 
    //destination to reach
    private Vector3 touchPoint;
 
    //which direction should we go?
    private Vector3 heading;
 
    //should we move somewhere?
    private bool dontMove = true;
 
    /// <summary>
    /// second initialization phase
    /// </summary>
    void Start () {
 
    }
 
    /// <summary>
    /// first initialization phase
    /// </summary>
    private void Awake()
    {
        this.cachedRigidBody = this.GetComponent<Rigidbody>();
        this.cachedAnim = this.GetComponent<Animator>();
    }
 
    /// <summary>
    /// update used for ensuring networked machines are kept in sync as well as
    /// for visual state updates.
    /// </summary>
    private void FixedUpdate()
    {
        //Distance formula for the x,z plane
        //sqrt (a^2 + b^2) = c (Pythagorean Theorum)
        //calc a^2
        float aSquared = this.transform.position.x - this.touchPoint.x;
        aSquared *= aSquared;
 
        //calc b^2
        float zSquared = this.transform.position.z - this.touchPoint.z;
        zSquared *= zSquared;
 
        //take sqrt for distance
        float distance = Mathf.Sqrt(aSquared + zSquared);
 
        //have we reached our threshold of our destination?
        if(distance < .5f)
        {
            //stop moving.
            this.dontMove = true;
        }
 
        //should we move?
        if (!dontMove)
        {
            //Ok, move in the heading set by our handlePointMovement function.
            this.Move(this.heading.x, this.heading.z);
        }
        else
        {
            //set heading to zero just in case
            this.heading = Vector3.zero;
            //remove all velocity from our character
            //except in the y direction as we don't control that
            //gravity does.
            this.cachedRigidBody.velocity = new Vector3(0.0f, this.cachedRigidBody.velocity.y, 0.0f);
            //zero out the animator, as we are no longer entering the move function
            this.cachedAnim.SetFloat("Speed", 0.0f);
        }
    }
 
    /// <summary>
    /// Move the character on the x,z plane as we use y for vertical in this game.
    /// Colliders will take care of y and our character doesn't fly.
    /// </summary>
    /// <param name="xMove"></param>
    /// <param name="zMove"></param>
    public void Move(float xMove, float zMove)
    {
        //Where should we move?
        Vector3 move = new Vector3(xMove, this.cachedRigidBody.velocity.y, zMove);
         
        //normalize this, so it has a magnitude of 1 such
        //that we are accurately portraying or max speed
        move.Normalize();
 
        //multiply by max speed
        //such that we don't move super slow.
        this.cachedRigidBody.velocity = move * this.maxSpeed;
 
        //calculate our total speed for the animator
        float speed = Mathf.Abs(this.cachedRigidBody.velocity.x) + Mathf.Abs(this.cachedRigidBody.velocity.z);
 
        //set that speed so the animation changes.
        this.cachedAnim.SetFloat("Speed", speed);
 
        //convert the vector into a radian angle,
        //convert to degrees and then adjust for the
        //object's starting orientation
        float angle = Mathf.Atan2(this.cachedRigidBody.velocity.z * -1, this.cachedRigidBody.velocity.x) * Mathf.Rad2Deg + facingAngleAdjustment;
 
        //don't rotate if we don't need to.
        if (speed > 0.0f)
        {
            //rotate by angle around the z axis.
            this.transform.rotation = Quaternion.AngleAxis(angle, new Vector3(0, 1, 0));
        }
    }
     
    /// <summary>
    /// used for checking for information as fast as possible that
    /// does not necessarily make impact on game state.  Just check
    /// to see if we should, and then make those changes in fixed update.
    /// </summary>
    private void Update ()
    {
 
 
        //We are touch first, so check for that.
        if (Input.touchCount > 0)
        {
            //Handle Point Movement, send the touch position on the screen
            HandlePointMovement(Input.GetTouch(0).position);
        }
        else if(Input.GetButtonDown("Fire1"))//if no touch, fall back to see if a mouse click happened.
        {
            //Handle Point Movement, send the mouse position associated with this click.
            HandlePointMovement(Input.mousePosition);
        }
 
    }
 
    /// <summary>
    /// Checks to see if there is a hit on a collider
    /// If there is, will set the new heading and save
    /// the destination point.  This will then allow
    /// movement to happen in fixed update.
    /// </summary>
    /// <param name="position"></param>
    private void HandlePointMovement(Vector3 position)
    {
        //the hit object, contains information about a hit.
        RaycastHit hit;
        //ray from where you touched or clicked projected in the orientation of the camera into the world
        Ray collisionRay = Camera.main.ScreenPointToRay(position);
        //returns a bool for if a hit happened or not, and resets hit (out) with correct information
        //100 is the layer to check against.  In this sample we check against all layers,
        //to optimize this check, we may want to check only for ground touches.
        if (Physics.Raycast(collisionRay, out hit, 100))
        {
            //touch point is used to determine if the character has reached the destination
            //therefor we set it to the destination
            this.touchPoint = new Vector3(hit.point.x, hit.point.y, hit.point.z);
            //vector mathematics, determine the direction the character needs to move.
            this.heading = this.touchPoint - this.transform.position;
            //don't move is the check for if the character should move or not.
            //since we want the character to move now, we set this to false.
            this.dontMove = false;
        }
    }
 
}