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





0/5 (0 vote)
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:
- Refactor our code to be cleaner
- 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:
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:
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.
- In the Event System in
Ball
, add a Pointer Down event and a Pointer Up. - In Pointer Down, select
GvrControllerPointer
as our game object and selectPlayerController
>HoldGameObject
HoldGameObject()
takes in aGameObject
. We’ll pass theball
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 theball
and use it, and it looks like each prefab instance is passed incorrectly.- For Pointer Up, select
GvrControllerPointer
as the game object to grab a script from and selectPlayerController
>RelaseGameObject
- 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:
And here’s what it’ll look like now in our game:
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!