Attacking The Player
General AI Behaviours
2. Attacking the Player
Our agent moves and follows the player, but in the previous gif, our agents do not attack the player when close.
Let’s starting implementing attacking!
Create another Variable, PlayerHealthVariable.cs
to store a reference to the PlayerHealth
Component.
// PlayerHealthVariable.cs
using UnityEngine;
using InitialPrefabs.DaniAI;
// Stores a reference to the PlayerHealth component inside the Template
public class PlayerHealthVariable : GenericVariable<PlayerHealth> { }
Once created, add it to our Variables inspector.
Similarly, we need an Observer which senses whether the player is alive. We have two options here:
- Deriving from a
BoolObserver
- Deriving from a
FloatObserver
If we derive from a FloatObserver
, our output is simply return healthVariable.currentHealth
. However,
our Comparison
and Compare Value
must be Greater than 0 for our Condition. Below, we implement using
the BoolObserver
.
// IsPlayerAlive.cs
using UnityEngine;
using InitialPrefabs.DaniAI;
public class IsPlayerAlive : BoolObserver {
[Header ("Variables")]
[Tooltip ("What is the name of the PlayerHealthVariable?")]
public string healthVariableName = "Health";
private PlayerHealthVariable healthVariable;
public override void OnStart () {
// Get the player gameObject and the health attached to the player
GameObject player = GameObject.FindGameObjectWithTag ("Player");
PlayerHealth health = player.GetComponent<PlayerHealth> ();
// Get the HealthVariable within DaniAI
healthVariable = Template.GetVariable<PlayerHealthVariable> (healthVariableName);
healthVariable.Value = health;
}
public override bool OnObserverUpdate () {
return healthVariable.Value.currentHealth > 0f;
}
}
Finally, we create an Action script, Attack.cs
. The logic for attacking follows the same logic as
EnemyAttack.cs
with the exception that we removed the use of colliders and triggers to determine
whether the player is within range.
// Attack.cs
using UnityEngine;
using InitialPrefabs.DaniAI;
public class Attack : Action {
[Tooltip ("How far can the AI attack?")]
public float attackRadius = 1.5f;
[Tooltip ("How long should the AI wait before it should attack again?")]
public float timeBetweenAttacks = 0.5f;
[Tooltip ("How much damage should the AI do?")]
public int damage = 10;
[Header ("Variables")]
[Tooltip ("What is the name of the Vector3Variable?")]
public string vector3VariableName = "Position";
[Tooltip ("What is the name of the PlayerHealthVariable?")]
public string playerHealthVariableName = "Health";
private Vector3Variable playerPosition;
private PlayerHealthVariable playerHealthVariable;
private float timer;
public override void OnStart () {
// Get the variables
playerPosition = Template.GetVariable<Vector3Variable> (vector3VariableName);
playerHealthVariable = Template.GetVariable<PlayerHealthVariable> (playerHealthVariableName);
// Reset the timer
timer = 0f;
}
public override ActionState OnActionUpdate () {
timer += Time.deltaTime;
// Attack every 0.5 seconds and if the player is within range
if (timer >= timeBetweenAttacks && IsWithinAttackRadius (Transform.position, playerPosition.Value)) {
// Then succeed the task!
AttackTarget ();
return ActionState.Success;
} else {
// Otherwise, we'll fail it...
return ActionState.Fail;
}
}
public override void OnActionEnd (ActionState state) {
switch (state) {
// Reset the timer when the task is successful
case ActionState.Success:
timer = 0f;
return;
}
}
// Check if the player is within the attack radius,
// we replaced the use of colliders with a distance check
private bool IsWithinAttackRadius (Vector3 start, Vector3 destination) {
float distance = Vector3.Distance (start, destination);
return distance <= attackRadius;
}
// Attack the player and damage the health
private void AttackTarget () {
playerHealthVariable.Value.TakeDamage (damage);
}
}
Once all the components we need are created, let’s hook them up in our Editor. Do note that, since most of the
outputs are true
for the Observers, our Conditions' Compare Value is defaulted to True
so we don’t need to
change the Compare Value.
Now, our agents' behaviours should look like the following. Note that our agent attacks every 0.5 seconds, so
there might be a delay when the agent is initially close up to you. You can modify the rate of attack in the
timeBetweenAttacks
field in the Inspector.