When developing games and simulations, you may want your users to be able to influence assets that are active in a physics simulation. In this article, you learn how to develop a Unity* 3D scene that has touch gestures configured to alter the physics characteristics of assets in a scene, including position, scale, mass, and velocity. You can use these alterations to improve a simulation's user interface (UI) or to make the scene more aesthetically interesting. This example shows Unity 3D running on Windows* 8 as a viable platform for manipulating physics characteristics via touch gestures in games and simulations.
Configure the Unity 3D Scene
I configured the geometry for this scene in Autodesk 3ds Max*. I applied a wave modifier and a bend modifier to a surface, with a green turret at one end of the surface. I exported this scene from Autodesk 3ds Max in FBX format, and then imported it into Unity 3D (see Figure 1). The surface altered with the bend and wave modifiers instigate motion for other assets added to the scene at runtime.
Note: Be sure that the geometry you import from third-party applications does not contain such sharp and numerous surface features that the Unity 3D physics engine is overly burdened or simply does not function. Certain development environments may dictate a maximum number of vertices and triangles allowed per imported asset. The assets in this example scene do not use any materials, shaders, or textures, but when such assets are used, you must consider their impact on your targeted deployment platform resources.
Figure 1. A turret at one end of a surface, with bend and wave modifiers applied
Also imported with the "visible at runtime" geometry were spheres that are "invisible at runtime." I placed these spheres above the valleys of the wave (see Figure 2).
Figure 2. Wavy surface, with spheres that are not rendered at runtime
I use the location of these spheres as a spawning point for spheres entering the scene. I added these spheres programmatically, so I must add their physics properties programmatically as well. I do this with the following code (note that the 17 spawn sphere names are TargetSpawn[000 – 016]):
01 | int spawnPoint = 1; |
02 | string spawnPointString = ""; |
03 | Vector3 spawnPosition = new Vector3(0.0f,0.0f,0.0f); |
04 | Quaternion spawnRotation = new Quaternion(0.0f,0.0f,0.0f,0.0f); |
05 | float spawnTime = 0.0f; |
06 | |
07 | |
08 | void Update () |
09 | { |
10 | |
11 | |
12 | |
13 | spawnTime += Time.deltaTime; |
14 | if(spawnTime > 1.0f) |
15 | { |
16 | spawnTime = 0.0f; |
17 | |
18 | if(spawnPoint == 17){spawnPoint = 1;} |
19 | |
20 | if(spawnPoint < 10){spawnPointString = "TargetSpawn00" + spawnPoint;} |
21 | else{spawnPointString = "TargetSpawn0" + spawnPoint;} |
22 | |
23 | GameObject spawnPointGO = GameObject.Find(spawnPointString); |
24 | spawnPosition = spawnPointGO.transform.position; |
25 | |
26 | GameObject targetSphere = (GameObject)Instantiate |
27 | (GameObject.CreatePrimitive (PrimitiveType.Sphere), |
28 | spawnPosition,spawnRotation); |
29 | |
30 | Color randomColor = new |
31 | Color(Random.value,Random.value,Random.value,1.0f); |
32 | targetSphere.renderer.material.color = randomColor; |
33 | |
34 | |
35 | targetSphere.collider.tag = "targetSphere"; |
36 | |
37 | targetSphere.transform.localScale = new Vector3(10.0f,10.0f,10.0f); |
38 | targetSphere.AddComponent ("Rigidbody"); |
39 | targetSphere.AddComponent ("GenericPan"); |
40 | targetSphere.AddComponent ("PanGesture"); |
41 | Destroy (targetSphere,60.0f); |
42 | |
43 | spawnPoint += 1; |
44 | |
45 | } |
46 | } |
With the above script implemented, the wavy base geometry fills with rigid-body physics spheres that disappear after 60 seconds (note the Destroy function call in the penultimate line of the script). You can alter and balance the 60 seconds to spawned sphere destruction feature based on the capabilities of your target platform and the scene aesthetics you desire. The turret at the end of the geometry is configured to shoot capsules at the sphere if they get too close. This turret is not configured to lead the spheres and is usually off target (see Figure 3).
Figure 3. Turret firing capsules at spawned rigid-body spheres
The spawned spheres roll to the valley of the platform, and the capsules fired from the green turret push them to the back wall. The most noticeable impact to performance in this Unity 3D scene comes from having a large number of turret capsules and spawned spheres in active view at the same time.
In the previous section, I applied a GenericPan script programmatically to the spawned spheres. This GenericPan script allows users to move the sphere with a Panning Touch Gesture. Although the sphere is being panned, its local position transform is modified in the Update function of the script. The object is still part of the physics simulation during the Panning Gesture and can interact with other objects in the scene. I also applied the GenericPan gesture to the rectangular back wall in the scene to allow the back wall to be used to sweep the spawned spheres through the valley of the platform. The back can also be panned so that it's in front of the turret and used to shield the spawned sphere from impact with the turret capsules. Here's the script in its entirety:
01 | using TouchScript.Events; |
02 | using TouchScript.Gestures; |
03 | using UnityEngine; |
04 | using System; |
05 | using System.Collections; |
06 | |
07 | |
08 | public class GenericPan : MonoBehaviour |
09 | { |
10 | private Vector3 targetPan; |
11 | private Vector3 startPos; |
12 | private float panSpeed; |
13 | private float panFrac; |
14 | private bool panning; |
15 | |
16 | |
17 | void Start () |
18 | { |
19 | startPos = targetPan = transform.localPosition; |
20 | panSpeed = 10.0f; |
21 | panFrac = 10.0f; |
22 | panning = false; |
23 | GetComponent<PanGesture>().StateChanged += onPanStateChanged; |
24 | |
25 | } |
26 | |
27 | void Update () |
28 | { |
29 | |
30 | if(panning) |
31 | { |
32 | //this.rigidbody.velocity = transform.forward * Vector3.Magnitude (transform.localPosition – targetPan); |
33 | |
34 | |
35 | panFrac = panSpeed * Time.deltaTime; |
36 | transform.localPosition = Vector3.Lerp(transform.localPosition, targetPan, panFrac); |
37 | panning = false; |
38 | } |
39 | } |
40 | |
41 | private void onPanStateChanged(object sender, GestureStateChangeEventArgs e) |
42 | { |
43 | switch (e.State) |
44 | { |
45 | case Gesture.GestureState.Began: |
46 | case Gesture.GestureState.Changed: |
47 | var target = sender as PanGesture; |
48 | Debug.DrawRay(transform.position, target.WorldTransformPlane.normal); |
49 | Debug.DrawRay(transform.position, target.WorldDeltaPosition.normalized); |
50 | |
51 | var local = new Vector3(transform.InverseTransformDirection(target.WorldDeltaPosition).x, transform.InverseTransformDirection(target.WorldDeltaPosition).y, transform.InverseTransformDirection(target.WorldDeltaPosition).z); |
52 | targetPan += transform.InverseTransformDirection(transform.TransformDirection(local)); |
53 | panning = true; |
54 | // if (transform.InverseTransformDirection (transform.parent.TransformDirection (targetPan – startPos)).y < 0) targetPan = startPos; |
55 | break; |
56 | } |
57 | |
58 | } |
59 | |
60 | } |
Note that in the onPanStateChanged function, the Vector3 variable local uses all three axes, where in other graphical UI-focused simulations, I used only the x- and y-axes. You can adjust this functionality and other attributes, including panning speed, according to your preferences. When panning the back wall to protect the spawned spheres from turret capsules, it's somewhat difficult to place the back wall in an ideal position. You would need to modify the Vector3 variable local in a dedicated script for the back wall asset to enable a turret protection feature.
Tap
The next gesture I configured to alter physics characteristics is the Tap Gesture. I added this gesture programmatically to the spheres at spawn time with the following lines:
01 | targetSphere.AddComponent ("GenericTap"); |
02 | targetSphere.AddComponent ("TapGesture"); |
The GenericTap script performs all rigid-body modification in the onTap function and does not use the Update function. The onTap function increases the scale of the sphere tapped by a factor of 2 each time a user taps the sphere. I added this functionality to keep track of the sphere in the scene and more easily monitor its behavior. The rigid-body mass of the sphere is increased by a factor of 10 each time a user taps the sphere. You can see the result in Figure 4, where the turret's capsule fire doesn't move a large sphere that has been tapped two or three times, although the other spheres are easily moved.
Figure 4. Capsule fire doesn't move a sphere that has increased scale and mass.
The code for the GenericTap script is as follows:
01 | using UnityEngine; |
02 | using System.Collections; |
03 | using TouchScript.Events; |
04 | using TouchScript.Gestures; |
05 | |
06 | |
07 | public class GenericTap: MonoBehaviour { |
08 | |
09 | private Vector3 startScale; |
10 | |
11 | void Start () |
12 | { |
13 | |
14 | startScale = transform.localScale; |
15 | if (GetComponent<TapGesture>() != null) GetComponent<TapGesture>().StateChanged += onTap; |
16 | |
17 | |
18 | } |
19 | |
20 | void Update () |
21 | { |
22 | |
23 | } |
24 | |
25 | private void onTap(object sender, GestureStateChangeEventArgs gestureStateChangeEventArgs) |
26 | { |
27 | if (gestureStateChangeEventArgs.State == Gesture.GestureState.Recognized) |
28 | { |
29 | transform.rigidbody.mass *= 10; |
30 | transform.localScale *= 2.0f; |
31 | } |
32 | } |
33 | |
34 | } |
This script is simple: the sphere's mass and scale change discretely as the result of a received Tap Gesture.. In a more complex gaming environment, a Tap Gesture may initiate a sequence of events where mass and scale increase over time and in response to other elements in the simulation. Another possible enhancement is to use the Time.deltaTime method to configure a gesture sequence to reduce mass and scale by applying double taps.
Press and Release
I next configure a Press and Release Gesture to increase the forward velocity of the spawned sphere in proportion to the time between the gesture's press and release. Again, I add this functionality programmatically at spawn time:
01 | targetSphere.AddComponent ("PressGesture"); |
02 | targetSphere.AddComponent ("ReleaseGesture"); |
03 | targetSphere.AddComponent ("GenericPressRelease"); |
I configure the GenericPressRelease script to toggle two Boolean variables in the onPress and onRelease functions. The first Boolean variable keeps track of whether forward velocity increased or decreased in the last Press-Release gesture cycle so that the opposite may be done with the current cycle. The second Boolean variable keeps track of whether the script is currently engaged in a Press-Release gesture cycle; if so, the magnitude of forward velocity increases or decreases accordingly. I accomplish this by using the spawned sphere's rigidbody.velocity parameter. I also use a timing variable to throttle the rate at which the magnitude of forward velocity changes. The result is a sphere that increases its forward velocity every odd time it receives a Press or Release Gesture and decreases its forward velocity every even time it receives a Press or Release Gesture. The code for the Press and Release Gesture is as follows:
01 | using UnityEngine; |
02 | using System.Collections; |
03 | using TouchScript.Events; |
04 | using TouchScript.Gestures; |
05 | |
06 | |
07 | public class GenericPressRelease : MonoBehaviour { |
08 | |
09 | private Vector3 startScale; |
10 | private float velocityMult; |
11 | private bool pressed; |
12 | private bool currentlyIncreasing; |
13 | private float timeSinceChanged; |
14 | |
15 | void Start () |
16 | { |
17 | |
18 | startScale = transform.localScale; |
19 | velocityMult = 10.0f; |
20 | pressed = false; |
21 | currentlyIncreasing = true; |
22 | timeSinceChanged = 0.0f; |
23 | if (GetComponent<PressGesture>() != null) GetComponent<PressGesture>().StateChanged += onPress; |
24 | if (GetComponent<ReleaseGesture>() != null) GetComponent<ReleaseGesture>().StateChanged += onRelease; |
25 | |
26 | } |
27 | |
28 | void Update () |
29 | { |
30 | timeSinceChanged += Time.deltaTime; |
31 | if(timeSinceChanged >= 0.25 && currentlyIncreasing && velocityMult <= 100 && pressed) |
32 | { |
33 | velocityMult += 10.0f; |
34 | rigidbody.velocity = transform.forward * velocityMult; |
35 | timeSinceChanged = 0.0f; |
36 | } |
37 | if(timeSinceChanged >= 0.25 && !currentlyIncreasing && velocityMult >= 0 && pressed) |
38 | { |
39 | velocityMult += -10.0f; |
40 | rigidbody.velocity = transform.forward * velocityMult; |
41 | timeSinceChanged = 0.0f; |
42 | } |
43 | |
44 | } |
45 | |
46 | private void onPress(object sender, GestureStateChangeEventArgs gestureStateChangeEventArgs) |
47 | { |
48 | if (gestureStateChangeEventArgs.State == Gesture.GestureState.Recognized) |
49 | { |
50 | pressed = true; |
51 | } |
52 | } |
53 | private void onRelease(object sender, GestureStateChangeEventArgs gestureStateChangeEventArgs) |
54 | { |
55 | if (gestureStateChangeEventArgs.State == Gesture.GestureState.Recognized) |
56 | { |
57 | pressed = false; |
58 | currentlyIncreasing = !currentlyIncreasing; |
59 | } |
60 | } |
61 | |
62 | } |
This functionality may be useful in a shooting-type game, where projectiles' velocities can increase, resulting in a varying impact effect. Also in a shooting-type game, you could configure this functionality as a defense mechanism whereby a user press or release would reduce velocity and lessen an impact effect. You could also use it in a Tetris*-style game to increase point rewards as the velocity of the puzzle pieces increases.
Other Physics Properties
I used the gestures demonstrated in this article to manipulate a Unity 3D asset's position, scale, mass, and forward velocity. Other rigid-body parameters that are good candidates for touch gesture manipulation are angular velocity, drag, and center of mass. You can tie these rigid-body parameters, which are altered by touch gestures, to existing parameters, such as color and texture. There is also the potential to create custom variables for a rigid-body physics simulation. One example is a hitDamage variable that increases every time the user taps it, with a certain amount of health subtracted from the object it hits when a collision is detected.
Conclusion
I built and executed the rigid-body physics example developed in this article with Unity 3D running Windows 8 on an Ultrabook™ device. I tuned the maximum lifetime of turret projectiles and spawned spheres for that platform and provided a simulation that responded as expected to the programmed touch gestures. Asset lifetime is just one of the parameters you must keep in mind when altering asset physics attributes. Take care to prevent a user from being able to put the physics simulation in a state where the entire application's performance and response are degraded. A basic preventative measure is to cap the minimums and maximums of the physics attributes being altered. By keeping performance issues in mind, Windows 8 running on Ultrabook devices provides a viable platform for developing Unity 3D simulations that alter attributes of scene assets in physics simulations.
About the Author
Lynn Thompson is an IT professional with more than 20 years of experience in business and industrial computing environments. His earliest experience is using CAD to modify and create control system drawings during a control system upgrade at a power utility. During this time, Lynn received his B.S. degree in Electrical Engineering from the University of Nebraska, Lincoln. He went on to work as a systems administrator at an IT integrator during the dot com boom. This work focused primarily on operating system, database, and application administration on a wide variety of platforms. After the dot com bust, he worked on a range of projects as an IT consultant for companies in the garment, oil and gas, and defense industries. Now, Lynn has come full circle and works as an engineer at a power utility. Lynn has since earned a Masters of Engineering degree with a concentration in Engineering Management, also from the University of Nebraska, Lincoln.
For more such windows resources and tools from Intel, please visit the Intel® Developer Zone
Source: https://software.intel.com/en-us/articles/using-touch-gestures