Click here to Skip to main content
14,932,691 members
Articles / Game Development / Unity
Technical Blog
Posted 27 May 2018

Stats

974 views

Day 67 of 100 Days of VR: Picking up and Throwing Objects In Unity - Part 2

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
27 May 2018CPOL4 min read
Picking up and throwing objects in Unity

Now we’re on part 2 of throwing a game object in VR. In the previous post, we almost finished being able to grab and throw our Ball game object in a script.

All that was left was to connect our Public variables. However, before we set that up, I wanted to refactor the code a bit.

Like I have mentioned at the end of the previous post, our code is getting messy where we’re calling scripts from other scripts. The code still works, but if we ever decided to change the way that script works, we could potentially break or cause unintended side effects.

So today, the game plan is to:

  1. Refactor our code to be cleaner
  2. Finish setting up our script

Let’s get started!

Refactoring Our Code to be Cleaner

To make our code cleaner, we’re going to apply the concept of Separation of Concerns, where each script should only know information about itself and rely on the functions of other scripts to do what they’re supposed to do.

Specifically, let’s move some of the code in our PlayerController to Throwable.

For the most part, we’re going to move the code we wrote in PlayerController that directly depends on the components in Throwable into Throwable.

PlayerController doesn’t need to know the details of how throwing a game object works. It just needs to know when it selects it and when it’s released. We can move everything else into the throwable script.

Here’s what the Throwable script should look like with the other changes:

C#
using UnityEngine;

public class Throwable : MonoBehaviour
{
    private Material _outlineMaterial;
    private Rigidbody _rigidbody;
    private Vector3 _currentGrabbedLocation; // The tracked location of our object 
                                             // for us to throw
    private bool _isGrabbed;

    private const string OutlineWidthKey = "_Outline";
    private const float OutlineWidthValue = 0.03f;

    void Start ()
    {
        _outlineMaterial = GetComponent<Renderer>().materials[1];
        _outlineMaterial.SetFloat(OutlineWidthKey, 0);

        _rigidbody = GetComponent<Rigidbody>();
        _currentGrabbedLocation = new Vector3();
        _isGrabbed = false;
    }

    void Update()
    {
        if (_isGrabbed)
        {
            _currentGrabbedLocation = transform.position;
        }
    }

    // Shows the outline by setting the width to be a fixed value when we are 
    // pointing at it.
    public void ShowOutlineMaterial()
    {
        _outlineMaterial.SetFloat(OutlineWidthKey, OutlineWidthValue);
    }

    // Hides the outline by making the width 0 when we are no longer 
    // pointing at it.
    public void HideOutlineMaterial()
    {
        _outlineMaterial.SetFloat(OutlineWidthKey, 0);
    }

    // Setup our throwable game object for when it is grabbed. Set the object that
    // grabbed it as its parent and disables kinematics
    public void GetGrabbed(GameObject controllerObject)
    {
        transform.parent = controllerObject.transform; // Set object as a child 
                                                       // so it'll follow our controller
        _rigidbody.isKinematic = true; // Stops physics from affecting the grabbed object
        _isGrabbed = true;
    }

    // Releases the throwable game object from being grabbed. It is sent flying 
    // by the force given by the controller game object.
    public void GetReleased()
    {
        if (_isGrabbed)
        {
            transform.parent = null; // Un-parent throwable object so it doesn't 
                                     // follow the controller
            _rigidbody.isKinematic = false; // Re-enables the physics engine.

            Vector3 throwVector = transform.position - _currentGrabbedLocation;
            _rigidbody.AddForce(throwVector * 10, ForceMode.Impulse); // Throws the ball 
                                                             // by applying the given force
            _isGrabbed = false;
        }
    }
}

I won’t go through the new Throwable code, but as you can see, we created 2 new functions: GetGrabbed() and GetReleased() which does pretty much exactly the same thing as HoldGameObject() and ReleaseGameObject() from PlayerController.

Here’s what PlayerController looks like now:

C#
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private Throwable _grabbedThrowable; // The object we're grabbing

    void Start ()
    {
        _grabbedThrowable = null;
    }

    // Called by the Event System when we click on an object, receives a game object to hold.
    // The object given must have a throwable object, otherwise we don't do anything
    public void HoldGameObject(GameObject throwableObject)
    {
        Throwable throwable = throwableObject.GetComponent<Throwable>();
        if (throwable != null)
        {
            _grabbedThrowable = throwable;
            _grabbedThrowable.GetGrabbed(gameObject);
        }
    }

    // Called by the Event System when we release our click on a game object.
    // Release our held object and throw it based on our controller motion
    public void ReleaseGameObject()
    {
        // Only throw an object if we're holding onto something
        if (_grabbedThrowable != null)
        {
            _grabbedThrowable.GetReleased();
            _grabbedThrowable = null;
        }
    }
}

As you can see, here we’ve cleaned up our PlayerController script quite a bit now. Now the only thing we do with the throwable script is that we would tell it that we pick it up and when we release our grip on it. Throwable would take care of the rest of the work involved.

Setting Up the Ball Game Object to Use the PlayerController

Now that we’ve cleaned up our script a bit, let’s add these function to our Ball’s event system.

  1. In the Event System in Ball, add a Pointer Down event and a Pointer Up.
  2. In Pointer Down, select GvrControllerPointer as our game object and select PlayerController > HoldGameObject
  3. HoldGameObject() takes in a GameObject. We’ll pass the ball itself as a variable to pick up. Select it in the game hierarchy. I tested what happens if we were to make a prefab of the ball and use it, and it looks like each prefab instance is passed incorrectly.
  4. For Pointer Up, select GvrControllerPointer as the game object to grab a script from and select PlayerController > RelaseGameObject
  5. Extra Note: If you haven’t already, don’t forget to remove the Box Collider and Mesh Renderer on the Player game object, otherwise it might interfere with the remote controller.

Now with the event system in place, we can grab and throw our ball game object. Here’s what our Event System looks like:

Image 1

And here’s what it’ll look like now in our game:

Image 2

We might want to tweak the force we apply to the ball, but at the most basic level, this is the code needed to grab and throw a game object.

Conclusion

Another day and another feature completed! We now have a fully functioning grab and throw script that allows us to throw our ball!

The cool thing about what we did with our script is that we’re not only limited to a sphere game object. Any game object we want to be able to throw, we can add the Throwable script (along with the Outline material and Rigidbody) and we can throw it! That’s nice and re-usable!

Now there are things that can be improved on like, what if we don’t want to rely on the laser to highlight a game object, but instead rely on proximity to the remote controller?

For that, we can’t depend on Unity’s event system anymore and we must create our own detection system which can be achieved with colliders and triggers, however, I’ll save that for a different day.

So, what’s next? Before we finish the Google Daydream series, there’s one more set of features that I want to experiment with. Menu creation! Stay tuned for more!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

IAmJoshChang
United States United States
Joshua is a passionate software developer working in the Seattle area. He also has experience with developing web and mobile applications, having spent years working with them.

Joshua now finds his spare coding time spent deep in the trenches of VR, working with the newest hardware and technologies. He posts about what he learns on his personal site, where he talks mostly about Unity Development, though he also talks about other programming topic that he finds interesting.

When not working with technology, Joshua also enjoys learning about real estate investment, doing physical activities like running, tennis, and kendo, and having a blast with his buddies playing video games.

Comments and Discussions

 
-- There are no messages in this forum --