Stealth Game: Tossing Coin for Distraction!
Darren can now throw a coin to distract the guards!
Objective
We want the player to toss a coin with the right mouse button to distract all guards.
Setting up
If we right-click, then we want to instantiate a coin at the position of the mouse cursor.
if (Input.GetMouseButtonDown(1))
{
var mousePos = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit _hit;
if (Physics.Raycast(mousePos, out _hit))
{
Instantiate(_coin, _hit.point, Quaternion.identity);
}
}
Since we want to store the location of where we click on the floor, we want to shoot a ray cast from the camera’s position and store it in a variable of type RaycastHit. It’s another form of a Vector3.
We then want to check if there was a hit when right-clicking on the ground. If there was, we want to instantiate the coin prefab to the hit point of the click.
Prevent Extra Coin Instantiation
We want the player to be able to toss the coin once. To prevent the player from instantiating many, we need to add a bool logic to the condition. I created a global variable of type bool called _hasTossedCoin
.
if (Input.GetMouseButtonDown(1) && !_hasTossedCoin)
{
_hasTossedCoin = true; var mousePos = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit _hit;
if (Physics.Raycast(mousePos, out _hit))
{
Instantiate(_coin, _hit.point, Quaternion.identity);
}
}
In the condition, we added logic to check if the _hasTossedCoin is false. If we right-clicked and _hasTossedCoin is false, then execute the code in the body. The first step will turn the _hasTossedCoin to true, which enables the body of code to run only once. Thus, only able to right-click once.
Send Guards to the Coin Position
We now want to send all guards to the coin position, the position where we right-clicked.
In the guard script, I added an else if condition checking to see if a coin was dropped. If so, I set the guards to move to the coin position through the nav mesh agent component.
Back in the player script, I created a function called SendAIToCoinPosition(Vector3 coinPos)
.
private void SendAIToCoinPosition(Vector3 coinPos)
{
foreach (var agent in _ai)
{
var guardScript = agent.GetComponent<GuardAI>();
var gaurdAnim = agent.GetComponent<Animator>();
guardScript.CoinDropped();
gaurdAnim.SetBool("isWalking", true);
guardScript.CoinPos = coinPos;
}
}
I want all the guards to head towards the hit point location by calling the CoinDropped function from the guard script. It will turn a bool to true that will enable the guards to move.
public void CoinDropped()
{
_coinDropped = true;
}
To move the guard, I have an else if statement that checks if the coin has been dropped. If so, set the destination to the coin position.
else if (_coinDropped)
{
var distance = Vector3.Distance(transform.position, _coinPos);
_agent.SetDestination(_coinPos);
if (distance < 4f)
{
_agent.stoppingDistance = 4f;
_animator.SetBool("isWalking", false);
}
}
Conclusion
After setting the logic, I did quite a bit of refactoring. I added a coin drop sound and a coin throw animation.
In Update, the right-click function looks like this:
if (Input.GetMouseButtonDown(1) && !_hasTossedCoin)
{
_hasTossedCoin = true;
_anim.SetTrigger("throw");
StartCoroutine(ThrowRoutine());
}
It now calls a coroutine that has all the logic. The reason for this is because I want there to be a delay after the throw animation ends.
private IEnumerator ThrowRoutine()
{
yield return new WaitForSeconds(0.8f);
var mousePos = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit _hit;
if (Physics.Raycast(mousePos, out _hit))
{
var coinInstance = Instantiate(_coin, _hit.point, Quaternion.identity);
var coinAudioSource = coinInstance.GetComponentInChildren<AudioSource>();
coinAudioSource.clip = _coinDrop;
coinAudioSource.Play();
SendAIToCoinPosition(_hit.point);
}
}
After 0.8 seconds, I want the logic to instantiate the coin to execute after the animation.
That is all for today. Thank you for your time! I feel like this one is all over the place.
Gabriel