Breaking glass effect PART III


In this third part of the project we will see how the game mechanics are implemented.


Letting a ball drop down when the player clicks the mouse button, under the mouse pointer

At first, the camera was stationary looking down the glass surface (named after GlassTerrain). The user clicked and released the left mouse button, and let a ball drop vertically on the glass. The script interpreted the mouse position on the screen, translated to world coordinates, and after procedurally generating a ball (created out of a primitive sphere), gave it an initial default speed (or rather applied an initial force).

This is the code excerpt from the Update function of the Monobehaviour class:


 if (Input.GetMouseButtonUp(0)) {
            float x = Input.mousePosition.x;
            float y = Input.mousePosition.y;
            float z = Input.mousePosition.z;
            // Spawn a shpere at the same x and y position but in front of the camera (add a constant to the camera z):
            GameObject newSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            newSphere.name = "Sphere_" + ++sphereNumber;
            newSphere.tag = "Respawn";    
            newSphere.AddComponent<rigidbody>();
            newSphere.GetComponent<transform>().position = camera.ScreenToWorldPoint (new Vector3(x, y, 1));
            newSphere.GetComponent<transform>().localScale = new Vector3(0.2f, 0.2f, 0.2f);
            newSphere.GetComponent<renderer>().material = sphereMaterial;
            newSphere.GetComponent<rigidbody>().mass = 0.1f;
            newSphere.GetComponent<rigidbody>().drag = 0.06f;
            float appliedYForce = 100.0f;
            // now calculate the direction of the force related to the camera direction
            Vector3 appliedForce = transform.rotation*new Vector3(0, 0, 1);
            // finally, apply the calculated force on the shpere
            newSphere.rigidbody.AddForce(appliedYForce*appliedForce, ForceMode.Force);
        }

With input.mousePosition we get the mouse position at the current moment. Then as we saw in Part II of this series of posts for this project, we procedurally generate another Unity primitive mesh, this time a sphere to represent the droped ball.

The new instance of the sphere is uniquely named with the help of the sphereNumber variable, which at the same moment is increased by one (++sphereNumber).

The following line positions the ball exactly under the mouse pointer when its left button is released:
newSphere.GetComponent<Transform>().position = camera.ScreenToWorldPoint (new Vector3(x, y, 1));
Basically what it does is that it translates from the 2-dimensional screen coordinates (that are returned by the Input.mousePosition calls) to the three-dimensional world coordinates, which is the principal coordination system of each Unity scene. The z coordinate is set to 1, you may try another value and see the ball positioned further down or up the camera axis.

The new instance is also specially tagged as "Respawn", this tag will be used to destroy the balls that collide with the hollow surrounding sphere (VisibleHorizon), as will be seen later on. Finally after creating the new ball instance, we should apply an initial force to get it moving, otherwise it will be positioned under the mouse pointer and stay there indefinetly! rigidbody.AddForce does exactly that.


Implementing parametric ball releasing force

Then, I modified the script to counter for varying ball velocities. I simulated this by measuring the time between the left mouse button click and its release. So the longer the player is delaying to release the left mouse button after clicking it, the bigger the force that is exerted at the ball when leaving its initial position (heading towards the glass). This is what I came up with:


void Update () {
    // Sense the event of a mouse click (left mouse button has been clicked and released):
    if (Input.GetMouseButtonDown(0))
        ClickPeriod = Time.time;
    if (Input.GetMouseButtonUp(0)) {
        // Compute the time period between mouse down and the button's release. This lapse defines the force imposed on the spheres
        ClickPeriod = Time.time - ClickPeriod;
...

    float appliedYForce = ForceMultiplierConst*ClickPeriod*ForceMultiplierConst*ClickPeriod;
    // Set an upwards bound to the applied force, so that the collider catches up with the big velocities resulted from the forces applied
    // otherwise the collider cannot calc in time the collision of the sphere with the GlassTerrain
    if (appliedYForce > 100) appliedYForce = 100;
    // now calculate the direction of the force related to the camera direction
    Vector3 appliedForce = transform.rotation*new Vector3(0, 0, 1);
    // finally, apply the calculated force on the shpere
    newSphere.rigidbody.AddForce(appliedYForce*appliedForce, ForceMode.Force);



Destroying balls occluded from camera view

After these modifications, I had balls falling with different forces and resulting in colliding with the glass with different velocities. But as I was keeping on clicking the mouse button, I had an increasing number of balls falling down. The majority of these balls, as they are all rigid bodies and their physical behaviour is determined by their physical interactions in the scene by the Unity physics engine, were falling down in the VisibleHorison surrounding sphere. And at that point they are not seen by the player any more. So, I had to get rid of them somehow. So I created another script and attached it to the VisibleHorizon:

using UnityEngine;
using System.Collections;

public class SphereColliderBehaviour : MonoBehaviour {

    // Use this for initialization
    void Start () {
    
    }
    
    // Update is called once per frame
    void Update () {
    
    }
    
    void OnTriggerExit(Collider other) {
        if (other.gameObject.tag == "Respawn")
            Destroy(other.gameObject);
    }
}

What it does, is to destroy all game objects colliding with it, provided that they are tagged as "Respawn". Remember that during the procedural generation of the balls, they are tagged "Respawn", and they are the only type of objects tagged as such.

You may ask, why did I choose to implement this behaviour inside OnTriggerExit function and not inside OnCollisionEnter or inside another related collision detection function. The reason is that the varying velocities with which the balls are collided with VisibleHorizon sometimes cannot be properly detected by the physics engine. OnTriggerExit seems to be working since it seems to have much lower run-time requirements, and is successfully called back after the collision event has occured. A critical detail that will make this work, is that the IsTrigger property of the VisibleHorizon's sphere collider should be true (checked in the object's Unity Editor Inspector).

In the near future this article will be appended with steps to rotate the camera with the arrow keyboard keys, and how to get the glass re-appear (respawn) after it has been broken to pieces. 
Stay tuned :)

Comments