====== Interactive Clock ======
===== Introduction =====
In this lesson, we will be building on the interactive bitmap tutorial by creating a multitouch clock. We will learn how to make following items touchable in the scene:
* the bells will be touchable to create a ring sound;
* the minute hand may be adjusted with a two finger rotate;
* the entire clock will rotate, scale, and drag with the same gestures as the interactive bitmaps tutorial;
Here is a snapshot of the finished clock tutorial in game mode:
{{:clockimage.png?300|}}
===== Requirements =====
* Estimated time to complete: 30 minutes
* GestureWorks 2 SDK
* Microsoft Windows 7, 8, or 10
* Unity 5 or greater
* Multitouch display device
===== Process Overview =====
* Setup the Project
* Create Bell Script
* Create Clock Script
* Minute Hand Script
* Second Hand Script (Optional)
* Review
===== Setup the Project =====
Copy from your GestureWorks install location, usually C:\Program Files(x86)\Ideum\GestureWorks2, the Unity project UnityGestureWorksClock - Start. Open this folder with Unity and open the scene file Scenes/Clock.unity. Your screen should be similar to this:
{{:clockscene.png?800|}}
If you look at the scene you'll see the clock is setup with colliders for the clock base, minute hand, and bells. These are the areas that will respond to gestures.
{{:clock_colliders.png?600|}}
===== Create Bell Script =====
Our first task with the clock is to have the bells play a sound in resonse to tap. Create a new C# script called TouchBell. Be sure to inherit from TouchObject like the interactive bitmap example. The final script for TouchBell is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TouchBell : TouchObject
{
public void ntap (GestureWorks.GestureInfo gesture)
{
GetComponent().Play();
}
}
Click and drag TouchBell to the Bell_L and Bell_R object in the Hierarchy panel. Configure the attached script for each bell to support 1 gesture and type in ntap for Element 0.
{{::bellscript.png?600|}}
Test out what you have done so far to ensure that the bells ring and that there are no errors. If you don’t hear anything be sure your audio connection is working correctly and check the output log file to see if there were any errors loading files.
===== Create Clock Script =====
We will now add the clock script. Make a new C# script and name it TouchClock. Put the following code in the file:
using System.Collections.Generic;
using UnityEngine;
public class TouchClock : TouchObject
{
public void ndrag(GestureWorks.GestureInfo gesture)
{
Camera cam = Camera.main;
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(cam);
Plane plane = new Plane(planes[5].normal, gameObject.transform.position);
float dX = gesture.value("drag_dx") * Screen.width;
float dY = gesture.value("drag_dy") * Flipped * Screen.height;
if(dX == 0.0f && dY == 0.0f)
{
return;
}
if(gesture.x == 0.0f && gesture.y == 0.0f)
{
return;
}
float screenX = gesture.x;
float screenY = Screen.height - gesture.y;
Ray dragRay = cam.ScreenPointToRay(new Vector3(screenX, screenY, 0.0f));
float dragEnter;
if(!plane.Raycast(dragRay, out dragEnter))
{
return;
}
Vector3 newPosition = dragRay.origin + dragRay.direction * dragEnter;
Ray prevRay = cam.ScreenPointToRay(new Vector3(screenX - dX, screenY - dY, 0.0f));
float prevEnter;
if(!plane.Raycast(prevRay, out prevEnter))
{
return;
}
Vector3 prevPosition = prevRay.origin + prevRay.direction * prevEnter;
transform.position += (newPosition - prevPosition);
if (transform.position.y < 0.0f)
{
transform.position = new Vector3(transform.position.x, 0.0f, transform.position.z);
}
}
public void nrotate(GestureWorks.GestureInfo gesture)
{
float multiplier = 0.75f;
Camera cam = Camera.main;
float dTheta = gesture.value("rotate_dtheta") * multiplier;
transform.Rotate(0, 0, dTheta);
}
public void nscale(GestureWorks.GestureInfo gesture)
{
float scaleDX = gesture.value("scale_dsx") * Screen.width;
float scaleDY = gesture.value("scale_dsy") * Screen.height;
float scale = (scaleDX + scaleDY) * 0.5f;
float cx = Mathf.Clamp(transform.localScale.x + scale, 0.5f, 2f);
float cy = Mathf.Clamp(transform.localScale.y + scale, 0.5f, 2f);
float cz = Mathf.Clamp(transform.localScale.z + scale, 0.5f, 2f);
transform.localScale = new Vector3(cx, cy, cz);
}
}
Note we are doing two things different here, using MoveObjectInCameraPlane to handle dragging and basic transforms for the other gestures. MoveObjectInCameraPlane is a helper method inside of GestureWorks/Unity/TouchObject.cs that in response to touch drags the object parallel to the current camera plane. This has the effect of keeping the Clock dragging consistent in response to user touch regardless of the current camera position and orientation.
Boundary checking is included in this script and is used to limit to how far the clock may be scaled and dragged. We use the clamp method to achieve this functionality.
Be sure to click and drag this script to the Clock > Clock object with the the collider box. Increase the supported gestures to 3 and type in the supported elements as follows:
Element 0: ndrag
Element 1: nrotate
Element 2: nscale
If you build and run at this point, you should be able to manipulate the clock in all the ways defined.
===== Minute Hand Script =====
We will now add a script that will allow you to rotate the minute hand around the center point of the clock. This rotational gesture will require that two fingers are touching the minute hand at the same time (as defined in the gml). Create a new C# script called TouchMinuteHand.
NOTE: the clock.fbx file was designed to contain properly registered pivot points for the clock hands. If you would like to achieve the same effect with a different model, you will need to also set registration points properly in the 3D authoring program.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TouchMinuteHand : TouchObject
{
public Transform SecondsHand;
public Transform HourHand;
public void nrotate (GestureWorks.GestureInfo gesture)
{
float dTheta = gesture.value("rotate_dtheta");
transform.Rotate(0f, 0f, dTheta);
SecondsHand.Rotate(0f, 0f, dTheta * 60f);
HourHand.transform.Rotate(0f, 0f, dTheta / 12);
}
}
Attach this script to the MinuteHand object in the Hierarchy and increase supported gestures to 1 with nrotate as the supported gesture.
This particular script also has a public Transform property that is used in the nrotate method. The rotate method will also affect the HourHand object and rotate it based on the revolutions the minute hand makes. It is important that you click and drag the HourHand object onto the Hour Hand transform target in the script. The following is a screenshot of what it should look like when done properly:
{{:hourhandsetup.png?600|}}
===== Second Hand Script (Optional) =====
If you would like to make the clock look like it has a good wind up (time keeping power), add this script to the SecondHand object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SecondHand : MonoBehaviour
{
void Update ()
{
transform.Rotate(new Vector3(0.0f, 0.0f, Time.deltaTime * 6.0f));
}
}
===== Review =====
In this tutorial we expanded on the knowledge gained in Tutorial #3 by manipulating on-screen bitmaps using gesture data obtained from GestureWorks. This tutorial covers a number of concepts that are central to the usage of GestureWorks:
* Fine tuned control over touchable objects
* Adding unique gestures to the registered touch objects
* Manipulating object instances based on data obtained from GestureWorks