Attacking The Player

General AI Behaviours

  1. Moving the Agent
  2. Attacking the Player
  3. Stopping Actions Upon Death
  4. Stopping the Agent on Death

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.

adding-a-health-variable.gif

Similarly, we need an Observer which senses whether the player is alive. We have two options here:

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.

hooking-up-the-attacks

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.

getting-hit