Collecting coins
Previously, we developed a coin counting variable telling us how many coins are in the scene. However, regardless of the count, the player still can't collect the coins during gameplay. Let's fix this now. To start, we need to think about collisions. Thinking carefully, we know that a coin is considered collected whenever the player walks into it, that is, a coin is collected when the player and the coin intersect or collide.
To determine when a collision happens like that, we must approximate the volume of both the player and coin in order to determine when the two volumes overlap in space. This is achieved in Unity through colliders. Colliders are special physics objects attached to meshes. They tell us when two meshes intersect. The FPSController object (First-person controller) already has a collider on it, through its Character Controller component. This approximates the physical body of a generic person. This can be confirmed by selecting FPSController in the Scene and examining the green wireframe cage surrounding the main camera. It is capsule-shaped. See Figure 2.15.
The Character Controller features a Collider to approximate the player body:
FPSController features a Character Controller component attached, which is configured by default with Radius, Height, and Center settings, defining the physical extents of the character in the scene. See Figure 2.16, FPSController features Character Controller. These settings can be left unchanged for our game:
The Coin object, in contrast, features only a Capsule Collider component, which was added automatically when we created the Cylinder primitive earlier to resemble a coin. This approximates the coin's physical volume in the scene without adding any additional features specific to characters and motion as found in the Character Controller component. This is fine, because the coin is a Static object as opposed to a moving and dynamic object like the FPSController. See Figure 2.17, Cylinder primitives feature a Capsule Collider component:
For this project, I'll stick to using a Capsule Collider component for the Coin object. However, if you want to change the attached collider to a different shape instead, such as a box or sphere, you can do this by first removing any existing collider components on the coin—click on the cog icon of the component in the Object Inspector and then select Remove Component from the context menu.
See Figure 2.18:
You can then add a new collider component to the selected object by choosing Component | Physics from the application menu and then choosing a suitably shaped collider. See Figure 2.19:
Regardless of the collider type used, there's a minor problem. If you play the game now and try to run through the coin, it'll block your path. The coin acts as a solid, physical object through which FPSController cannot pass. However, for our purposes, this isn't how the coin should behave. It's supposed to be a collectible object. The idea is that when we walk through it, the coin is collected and disappears. We can fix this easily by selecting the Coin object and enabling the Is Trigger checkbox in the Capsule Collider component, in the Object Inspector. The Is Trigger setting appears for almost all collider types. It lets us detect collisions and intersections with other colliders while allowing them to pass through. See Figure 2.20:
If you play the game now, FPSController will easily walk through all coin objects in the scene. This is a good start. However, the coins don't actually disappear when touched; they still don't get collected. To achieve this, we'll need to add more script to the Coin.cs
file. Specifically, we'll add an OnTriggerEnter function. This function is called automatically when an object, like the player, enters a collider. For now, we'll add a Debug.Log statement to print a debug message when the player enters the collider, just for test purposes. See Code Sample 2.4:
//------------------------- using UnityEngine; using System.Collections; //------------------------- public class Coin : MonoBehaviour { //------------------------- public static int CoinCount = 0; //------------------------- // Use this for initialization void Start () { //Object created, increment coin count ++Coin.CoinCount; } //------------------------- void OnTriggerEnter(Collider Col) { Debug.Log ("Entered Collider"); } //------------------------- //Called when object is destroyed void OnDestroy() { //Decrement coin count --Coin.CoinCount; //Check remaining coins if(Coin.CoinCount <= 0) { //We have won } } //------------------------- } //-------------------------
Note
More information on the OnTriggerEnter function can be found at the online Unity documentation here:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerEnter.html
Test the Code Sample 2.4 by pressing play on the toolbar. When you run into a coin, the OnTriggerEnter function will be executed and the message displayed. However, the question remains as to what object initiated this function in the first place. It's true that something collided with the coin, but what exactly? Was it the player, an enemy, a falling brick, or something else? To check this, we'll use Tag. The Tag feature lets you mark specific objects in the scene with specific tags or labels, allowing these objects to be easily identified in code so that we can check quickly that the player, rather than other objects, are colliding with the coins. After all, it should only be the player that can collect coins. So, firstly, we'll tag the player object with a tag called Player. To do this, select the FPSController object in the scene and then click on the Tag drop-down box in the Object Inspector. From here, select the Player tag. This marks FPSController as the Player object. See Figure 2.21:
With FPSController now tagged as Player, we can refine the Coin.cs file, as shown in Code Sample 2.5. This handles coin collection, making the coin disappear on touch and decreasing the coin count:
//------------------------- using UnityEngine; using System.Collections; //------------------------- public class Coin : MonoBehaviour { //------------------------- public static int CoinCount = 0; //------------------------- // Use this for initialization void Start () { //Object created, increment coin count ++Coin.CoinCount; } //------------------------- void OnTriggerEnter(Collider Col) { //If player collected coin, then destroy object if(Col.CompareTag("Player")) Destroy(gameObject); } //------------------------- //Called when object is destroyed void OnDestroy() { //Decrement coin count --Coin.CoinCount; //Check remaining coins if(Coin.CoinCount <= 0) { //We have won } } //------------------------- } //-------------------------
Code Sample 2.5
The following points summarize the code sample:
- OnTriggerEnter is called once automatically by Unity each time FPSController intersects the Coin collider
- When OnTriggerEnter is called, the Col argument contains information about the object that entered the collider on this occasion
- The CompareTag function is used to determine if the colliding object is the Player as opposed to a different object
- The Destroy function is called to destroy the Coin object itself, represented internally by the inherited member variable, gameObject
When the Destroy function is called, the OnDestroy event is invoked automatically, which decrements the Coin count
Excellent work! You've just created your first working coin. The player can now run into the coin, collect it, and remove it from the scene. This is a great beginning, but the scene should contain more than one coin. We could solve this by duplicating the existing coin many times and repositioning each duplicate to a different place. However, there's a better way, as we'll see next.