In this lesson, you will implement a TurretAttack
MonoBehaviour that will spawn Projectiles that will target enemies within the Turret's Area of Engagement.
Your TurretAttack
will need to be able to check if there are any Targets in the AreaOfEngagement
. If there is at least one target, it will need to Instantiate
a Projectile
. Then, it should wait for some amount of time before firing again. Finally, to be able to check if it can fire again, it should track if it is currently cooling down.
TurretAttack
MonoBehaviour ScriptAreaOfEngagement AoE
PropertyProjectile ProjectilePrefab
Propertyfloat CooldownTime
Property. Set the default value to 3.bool IsCoolingDown
Property. Set the default value to false.AreaOfEngagement
in the InspectorProjectilePrefab
in the InspectorWhen you have finished, your Turret Prefab should be similar to the image below:
To help you test if your TurretAttack
implementation is working in isolation, create a test scene. In this test, you will test that your TurretAttack:
When you're done, your TurretAttack Test Scene should look similar to the image below:
Sometimes, it is easier to build a small part of a bigger challenge. Start by implementing a Fire()
method that will Instantiate
a clone of the ProjectilePrefab
, set its transform.position
to match the Turret's transform.position
, and set its Target
to be the first element of AoE
.
private void Fire()
methodInstantiate
a clone of the ProjectilePrefab
transform.position
of the new Projectile
to this.transform.position
(the position of the Turret)Projectile.Target
to the first target in AoE.Targets
Projectile.Target
access modifier to public
In your Start()
method, you can use the Invoke(string, float)
method to call the Fire()
method after a specified amount of time has passed. Invoke
is similar to InvokeRepeating
but only calls the method once.
Start()
method to your TurretAttack
scriptStart()
, use Invoke
to call Fire
after 1 second (use nameof(Fire)
rather than "Fire"
)Note: Waiting 1 second allows the AreaOfEngagement
time to add the Enemies to the Targets list.
If all went well, your Turret should fire exactly one projectile that deals damage to one of the enemies in the Area of Engagement.
Now that you have tested your Fire()
method and have confidence that it is working, it is time to implement the full TurretAttack
script
Start()
method, you don't need itUpdate()
method:TurretAttack
IsOnCooldown
, do nothing (return
)AoE.Targets
is empty, do nothing (AoE.Targets.Count == 0
)Fire()
IsOnCoolDown
to true
Invoke
a method that sets IsOnCoolDown
to false
after CooldownTime
secondsNote: Set the CooldownTime
to a smaller number to increase the speed of the test.
If all went well, when you enter Play Mode, you should see:
MissingReferenceException
. You will solve this bug in the next part of the lesson. A MissingReferenceException
occurs when you attempt to reference a Game Object or Component that has been destroyed and removed from the Scene. In this case, you have destroyed the Enemy but it has not been removed from the Area of Effect's Targets
list. If you look closely in the documentation for OnTriggerExit
, you will see that it states, "Deactivating or destroying a Collider while it is inside a trigger volume will not register an on exit event."
This means that you need to manually remove the target from the AreaOfEngagement
when it is destroyed.
There are several ways that you could implement a solution to this bug. One common way is to use a UnityEvent
to notify the AreaOfEngagement
when the Enemy has been destroyed.
A UnityEvent is a way to trigger actions when something happens, like clicking a button or reaching a goal, or in this case, when an enemy dies.
Create an OnDeath
event inside your Health
script that is invoked before the object is destroyed.
Health
MonoBehaviour ScriptUnityEvent<Health> OnDeath
PropertyApplyHit
method to invoke the OnDeath
event before destroying the gameObject
Now that you have implemented a Health
component, you can refactor your AreaOfEngagement
to track the Health
of the Enemy rather than just the Transform
.
AreaOfEngagement
ScriptTargets
list to be a List<Health>
. You will only fire at enemies that have a Health
component.OnTriggerEnter
and OnTriggerExit
to find the Health
component on colliderHealth
component, return earlyHealth
componentAreaOfEngagement.Targets
to use the transform
of the component. For example, the TurretAttack
script may need to be refactor to:Before continuing, be sure to test that your Test Scene continues to function the same way it did previously.
You can use the UnityEvent.AddListener
method to "listen" for the OnDeath
event to be invoked. In this case, you can use it to call a method that accepts a Health
parameter and use it to remove the associated target. To do this, you first need to write a method that will remove a Health
component from the Targets list.
void RemoveOnDeath(Health targetHealth)
to your AreaOfEngagement
scriptTarget.Remove(targetHealth)
OnTriggerEnter
method such that the RemoveOnDeath
method to the is added as a listener to the Health.OnDeath
event.Similar to the UnityEvent.AddListener
method, there is also a UnityEvent.RemoveListener
method which is used to stop listening to the event. You should do this when the enemy exits the AreaOfEngagement
. If you do not and the enemy is destroyed by another turret, it will notify all AreaOfEngagement
's it has ever entered.
OnTriggerExit
method such that the RemoveOnDeath
method stops listening to the Health.OnDeath
event using OnDeath.RemoveListener
If all went well, you should see the following:
With your TurretAttack Test Scene demonstrating the Turret working in isolation, you now have a little confidence that your turrets will work in your game.
In this lesson, you used a UnityEvent
to notify the AreaOfEffect
when your enemy died. In this challenge you will continue to develop your skills using UnityEvents
-- TO DO: Add challenge
With a simple turret, projectile, and enemy implemented, it is now time to add the ability for the player to be able to place turrets onto the map. In the next lesson, you will learn how to utilize Mouse Events to send messages between Game Objects