We want to be able to pick up our collectable GameObjects when our
Player GameObject collides with them.
To do this we need to detect our collisions between the Player
GameObject and the Pick Up GameObjects.
We will need to have these collisions trigger a new behavior
and we will need to test these collisions to make sure we are
picking up the correct objects.
The Pick Up objects, the Player Sphere,
the ground plane and the walls all have Colliders
that inform us about collisions.
If we didn't test our collisions to find out which objects we've
collided with, we could collect the wrong objects.
We could collect the floor, or the walls.
As a matter of face if we didn't test our collisions,
on the very first frame of the game we would come in contact with
the ground plane and we would collect the ground plane
and then we would fall in to the void of the scene space
and the game would essentially be over.
First, we don't need our player to remain inactive,
so let's tick the Active checkbox and bring back our player.
Next, let's select the PlayerController script
and open it for editing.
Just a note, we could edit this script regardless of whether
the GameObject is active or not.
Now that we have the script open, what code are we going to write?
We could write Collider and then search the documentation using
the hotkey combination.
But there is a different way that we could do this as well.
Let's return to Unity and look at the details of our player GameObject.
What we are interested in here is the Sphere Collider component.
In the header of each component on the left is the component's
turndown arrow, the icon, the Enable checkbox if it's available,
and the type of the component.
On the right is the context-sensitive gear gizmo and an icon of
a little book with a question mark.
Now this is what we need.
This is the quick link to the component reference.
If we select this icon, we are taken not to the scripting reference
but to the component reference.
We would read this document to find out more about how to use
this component in the context of the Editor.
We, however, want to find out how to script to this component's class.
To do this we simply switch to scripting,
and we are taken to the scripting reference for the Sphere Collider.
We want to detect and test our collisions.
For this project we are going to use OnTriggerEnter.
Just imagine if we were, say, a daring plumber and we jumped up
to collect a perfect arch of coins, and bounced off the very first one
as we collected it and fell back to the ground.
Not very elegant.
This code will give us the ability to detect the contact between our
Player GameObject and our Pick Up GameObjects without actually
creating a physical collision.
The code example however is not exactly what we're looking for.
But that's okay, we can change that.
First, let's copy the code,
and then let's return to our scripting application.
Now that we're back in scripting let's paste the code.
As we have copied this code from a web page let's correct the indents.
In this case I'm going to make sure the indents are tabs, and that all of
the tabs are correctly aligned.
Next, let's look at this code.
We are using the function OnTriggerEnter()
OnTriggerEnter() will be called by Unity when our Player GameObject
first touches a Trigger Collider.
We are given as an argument
a reference to the Trigger Collider that we have touched.
This is the Collider called "other".
This reference gives us a way to get a hold of the Colliders that we touch.
With this code, when we touch another Trigger Collider,
we will destroy the GameObject that the Trigger Collider is attached to
through the reference other.gameObject.
By destroying that GameObject, the GameObject
all of its components and all of its children and their components
are removed from the scene.
For the sake of example in this assignment, we won't destroy the
other GameObject; we will deactivate it.
Just like we deactivated the player object when we were
creating our Pick Up objects.
First, let's remove the Destroy(other.gameobject) code from the function.
But let's paste it down below our script as a palette to work with.
How will we deactivate our Pick Up objects?
Well, what clues do we have?
We can address the other Collider's GameObject through other.gameObject.
We can see this here in the sample code.
And we want to test the other GameObject and if it's a
Pick Up object we want to deactivate that GameObject.
So let's look up "GameObject" with our hotkey combination
and see what we can find.
Now we have the page on GameObject.
There are a few important items here that we want:
They are - "tag"
tag allows us to identify the GameObject
by comparing the tag value to a string.
And - "SetActive"
This is how we activate or deactivate a GameObject through code.
The last item we need to know about is:
"CompareTag"
CompareTag allows us to efficiently compare the tag of any
GameObject to a string value.
Let's open up these three items, each in their own tab.
Tag allows us to identify a GameObject by a tag value.
We must declare our tags in the Tags and Layers Panel before using them.
It is possible to test a tag against a string value directly,
with code like if (gameObject.tag == "Some String Value”)
But there is a more efficient built-in way to do this,
and that is CompareTag
With CompareTag we can efficiently compare the tag of any GameObject
with a string value.
Let's copy the sample code and paste it in to our working palette.
Now - GameObject.SetActive
This is how we activate or deactivate a GameObject.
This is the code equivalent of clicking the Active check box
next to the Name field in the Inspector.
In our case, just like the code snippet, we will call
GameObject.SetActive (false)
to deactivate our Pick Up GameObjects.
Let's copy this code,
and returning to our script editor, paste it in to our palette as well.
I feel we have enough pieces to write our code, so let's write:
if (other.gameObject.CompareTag
with the string value of "Pick Up”, and we will have
to declare this tag in Unity later.
other.gameObject.SetActive (false);
Now this code will be called every time we touch a Trigger Collider.
We are given a reference to the Collider we touch, we test it's tag,
and if the tag is the same as the string value "Pick Up”,
we will take the other GameObject and we will call SetActive (false),
which will deactivate that GameObject.
Now we don't need this code we've been saving anymore,
and keeping it will only confuse the compiler, so we can delete it.
Let's save this script, and return to Unity, and check for errors.
The first thing we need to do it set up the Tag value for the Pick Up objects.
Select the Prefab asset for the Pick Up object.
When we look at the tag list we don't see any tag called Pick Up.
So we need to add one.
Select Add Tag...
This brings up the Tags and Layers Panel.
Here we can customize tags and layers.
Note that this list is empty.
To create a new custom tag, select the + button
to add a new row to the tags list.
In the new empty element, in our case Tag 0, type "Pick Up";
and this is case-sensitive, and needs to be exactly the same string
that we have in our script.
If in doubt we can copy and paste this string to get the exact value.
When we look back at the Prefab asset, note that the asset is still untagged.
By selecting Add Tag we changed our focus from the Prefab asset
to the Tag Manager and in the Tag Manager we created our tag.
Now we need to apply that tag to the Prefab asset.
Select the Tag drop-down again and see how we now have Pick Up in the list.
Select this tag from the list and the asset is now tagged "Pick Up".
And with the power of Prefabs
all of the instances are now tagged Pick Up as well.
Now let's test our game.
Save the scene and enter Play mode.
Hm, okay,
Our tag is set to Pick Up, but we are still bouncing off
the Pick Up cubes just like we are bouncing off the walls.
So let's exit Play mode.
Before we discuss why we are bouncing off
the Pick Up cubes rather than picking them up,
we need to have a brief discussion about Unity's physics system.
I'm going to enter Play mode for this discussion.
Let's look at one of our cubes and our player.
As an aside, we can select two or more GameObjects at the same time
and inspect them all.
We do this by holding down the Command key on the Mac
or the CTRL key on the PC, and selecting the GameObjects.
When we select multiple GameObjects, note how the Inspector changes
to show the common components and property values
of the selected GameObjects.
The Inspector also allows for multi-object editing.
Using multi-object editing I'm going to disable the Mesh Renderer on both
the player's Sphere and the selected Cube with a single click.
This leaves us with the two green outlines of the Collider volumes
for these two objects.
How do collisions work in Unity's physics engine?
The physics engine does not allow two Collider volumes to overlap.
When the physics engine detects that any two or more Colliders will
overlap that frame, the physics engine will look at the objects and
analyze their speed and rotation and shape and calculate a collision.
One of the major factors in this calculation is whether the Colliders
are static or dynamic.
Static Colliders are usually non-moving parts of your scene,
like the walls, the floor, or the fountain in the courtyard.
Dynamic Colliders are things that move, like the player's Sphere or a car.
When calculating a collision the static geometry will not
be affected by the collision.
But the dynamic objects will be.
In our case the player's sphere is dynamic, or moving geometry,
and it is bouncing off the static geometry of the cubes.
Just as it bounces off the static geometry of the walls.
The physics engine can however allow the penetration
or overlap of Collider volumes.
When it does this the physics engine still calculates the Collider volumes
and keeps track of the Collider overlap.
But it doesn't physically act on the overlapping objects;
it doesn't cause a collision.
We do this by making our Colliders into triggers, or Trigger Colliders.
When we make our Colliders in to a trigger, or Trigger Collider, we can
detect the contact with that trigger through the OnTrigger event messages.
When a Collider is a trigger you can do clever things like place a trigger
in the middle of a doorway in, say, an adventure game,
And when the player enters the trigger the mini-map updates;
and a message plays 'you have discovered this room'.
Or every time your player walks around that corner spiders drop
from the ceiling because the player has walked through a trigger.
For more information on OnCollision and OnTrigger messages
see the lessons linked below.
We are using OnTriggerEnter in our code rather than OnCollisionEnter.
So we need to change our Collider volumes in to trigger volumes.
To do this we must be out of Play mode.
Let's select the Prefab asset and look at the Box Collider component.
Here we select Is Trigger and again, the power of Prefabs
all of our Pick Up objects are now using Trigger Colliders.
Let's save our scene, enter Play mode and test.
And as our player enters the trigger we pick up the objects.
Excellent.
Let's exit Play mode.
Everything looks great.
We only have one issue.
We have made one small mistake, and this is related to
how Unity optimizes it's physics.
As a performance optimization, Unity calculates all the volumes
of all the static Colliders in a scene and holds this information in a cache.
This makes sense as static Colliders shouldn't move,
and this saves recalculating this information every frame.
Where we have made our mistake is by rotating our cubes.
Any time we move, rotate, or scale a Static Collider, Unity will
recalculate all the static Colliders again and update the static Collider cache.
To recalculate the cache takes resources.
We can move, rotate or scale dynamic Colliders as often as we want,
and Unity won't recache any Collider volumes.
Unity expects us to move Colliders.
We simply need to indicate to Unity which Colliders are dynamic
before we move them.
We do this by using the Rigidbody component.
Any GameObject with a Collider and a Rigidbody is considered dynamic.
Any GameObject with a Collider attached, but no physics Rigidbody,
is expected to be static.
Currently our Pick Up GameObjects have a Box Collider, but no Rigidbody.
So Unity is recalculating our static Collider cache every frame.
The solution is to add a Rigidbody to the Pick Up objects.
This will move the cubes from being static Colliders
to being dynamic Colliders.
Let's save and play.
And our cubes fall through the floor.
Gravity pulls them down, and as they are triggers
they don't collide with the floor.
Let's exit Play mode.
If we look at the Rigidbody component, we could simply disable Use Gravity,
which would prevent the cubes from being pulled downwards.
This is only a partial solution, however.
If we did this, even though our cubes would not respond to gravity,
they would still respond to physics forces
There is a better solution.
And that is to select Is Kinematic.
When we do this we set this Rigidbody component to be a kinematic Rigidbody.
A kinematic Rigidbody will not react to physics forces
and it can be animated and moved by its Transform.
This is great for everything from objects with Colliders
like elevators and moving platforms, to objects with triggers,
like our collectables that need to be animated or moved by their Transform.
For more information on the Rigidbody and Is Kinematic,
see the lessons linked below.
Let's save our scene and enter Play mode to test.
Now our behavior is identical and performant.
So, static Colliders shouldn't move like walls and floors.
Dynamic Colliders can move, and have a Rigidbody attached.
Standard Rigidbodies are moved using physics forces.
Kinematic Rigidbodies are moved using their Transform.
In the next assignment we will count our collectable objects,
and make a simple UI to display our score and messages.