Harming Enemies

Checked with version: 4.6

-

Difficulty: Beginner

This is part 7 of 10 of the Survival Shooter tutorial, in which you will give create the player shooting mechanic using Raycasting, Line Renderers and more.

Harming Enemies

Beginner Survival Shooter tutorial

Transcripts

EnemyHealth

Code snippet

using UnityEngine;

public class EnemyHealth : MonoBehaviour
{
    public int startingHealth = 100;            // The amount of health the enemy starts the game with.
    public int currentHealth;                   // The current health the enemy has.
    public float sinkSpeed = 2.5f;              // The speed at which the enemy sinks through the floor when dead.
    public int scoreValue = 10;                 // The amount added to the player's score when the enemy dies.
    public AudioClip deathClip;                 // The sound to play when the enemy dies.


    Animator anim;                              // Reference to the animator.
    AudioSource enemyAudio;                     // Reference to the audio source.
    ParticleSystem hitParticles;                // Reference to the particle system that plays when the enemy is damaged.
    CapsuleCollider capsuleCollider;            // Reference to the capsule collider.
    bool isDead;                                // Whether the enemy is dead.
    bool isSinking;                             // Whether the enemy has started sinking through the floor.


    void Awake ()
    {
        // Setting up the references.
        anim = GetComponent <Animator> ();
        enemyAudio = GetComponent <AudioSource> ();
        hitParticles = GetComponentInChildren <ParticleSystem> ();
        capsuleCollider = GetComponent <CapsuleCollider> ();

        // Setting the current health when the enemy first spawns.
        currentHealth = startingHealth;
    }

    void Update ()
    {
        // If the enemy should be sinking...
        if(isSinking)
        {
            // ... move the enemy down by the sinkSpeed per second.
            transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
        }
    }


    public void TakeDamage (int amount, Vector3 hitPoint)
    {
        // If the enemy is dead...
        if(isDead)
            // ... no need to take damage so exit the function.
            return;

        // Play the hurt sound effect.
        enemyAudio.Play ();

        // Reduce the current health by the amount of damage sustained.
        currentHealth -= amount;
            
        // Set the position of the particle system to where the hit was sustained.
        hitParticles.transform.position = hitPoint;

        // And play the particles.
        hitParticles.Play();

        // If the current health is less than or equal to zero...
        if(currentHealth <= 0)
        {
            // ... the enemy is dead.
            Death ();
        }
    }


    void Death ()
    {
        // The enemy is dead.
        isDead = true;

        // Turn the collider into a trigger so shots can pass through it.
        capsuleCollider.isTrigger = true;

        // Tell the animator that the enemy is dead.
        anim.SetTrigger ("Dead");

        // Change the audio clip of the audio source to the death clip and play it (this will stop the hurt clip playing).
        enemyAudio.clip = deathClip;
        enemyAudio.Play ();
    }


    public void StartSinking ()
    {
        // Find and disable the Nav Mesh Agent.
        GetComponent <NavMeshAgent> ().enabled = false;

        // Find the rigidbody component and make it kinematic (since we use Translate to sink the enemy).
        GetComponent <Rigidbody> ().isKinematic = true;

        // The enemy should no sink.
        isSinking = true;

        // Increase the score by the enemy's score value.
        ScoreManager.score += scoreValue;

        // After 2 seconds destory the enemy.
        Destroy (gameObject, 2f);
    }
}
var startingHealth : int = 100;            // The amount of health the enemy starts the game with.
var currentHealth : int;                   // The current health the enemy has.
var sinkSpeed : float = 2.5f;              // The speed at which the enemy sinks through the floor when dead.
var scoreValue : int = 10;                 // The amount added to the player's score when the enemy dies.
var deathClip : AudioClip;                 // The sound to play when the enemy dies.

private var anim : Animator;                              // Reference to the animator.
private var enemyAudio : AudioSource;                     // Reference to the audio source.
private var hitParticles : ParticleSystem;                // Reference to the particle system that plays when the enemy is damaged.
private var capsuleCollider : CapsuleCollider;            // Reference to the capsule collider.
private var isDead : boolean;                                // Whether the enemy is dead.
private var isSinking : boolean;                             // Whether the enemy has started sinking through the floor.

function Awake ()
{
    // Setting up the references.
    anim = GetComponent (Animator);
    enemyAudio = GetComponent (AudioSource);
    hitParticles = GetComponentInChildren (ParticleSystem);
    capsuleCollider = GetComponent (CapsuleCollider);

    // Setting the current health when the enemy first spawns.
    currentHealth = startingHealth;
}

function Update ()
{
    // If the enemy should be sinking...
    if(isSinking)
    {
        // ... move the enemy down by the sinkSpeed per second.
        transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
    }
}


public function TakeDamage (amount : int, hitPoint : Vector3)
{
    // If the enemy is dead...
    if(isDead)
        // ... no need to take damage so exit the function.
        return;

    // Play the hurt sound effect.
    enemyAudio.Play ();

    // Reduce the current health by the amount of damage sustained.
    currentHealth -= amount;
    
    // Set the position of the particle system to where the hit was sustained.
    hitParticles.transform.position = hitPoint;

    // And play the particles.
    hitParticles.Play();

    // If the current health is less than or equal to zero...
    if(currentHealth <= 0)
    {
        // ... the enemy is dead.
        Death ();
    }
}

function Death ()
{
    // The enemy is dead.
    isDead = true;

    // Turn the collider into a trigger so shots can pass through it.
    capsuleCollider.isTrigger = true;

    // Tell the animator that the enemy is dead.
    anim.SetTrigger ("Dead");

    // Change the audio clip of the audio source to the death clip and play it (this will stop the hurt clip playing).
    enemyAudio.clip = deathClip;
    enemyAudio.Play ();
}

public function StartSinking ()
{
    // Find and disable the Nav Mesh Agent.
    GetComponent (NavMeshAgent).enabled = false;

    // Find the rigidbody component and make it kinematic (since we use Translate to sink the enemy).
    GetComponent (Rigidbody).isKinematic = true;

    // The enemy should no sink.
    isSinking = true;

    // Increase the score by the enemy's score value.
    ScoreManager.score += scoreValue;

    // After 2 seconds destory the enemy.
    Destroy (gameObject, 2f);
}

PlayerShooting

Code snippet

using UnityEngine;

public class PlayerShooting : MonoBehaviour
{
    public int damagePerShot = 20;                  // The damage inflicted by each bullet.
    public float timeBetweenBullets = 0.15f;        // The time between each shot.
    public float range = 100f;                      // The distance the gun can fire.

    float timer;                                    // A timer to determine when to fire.
    Ray shootRay;                                   // A ray from the gun end forwards.
    RaycastHit shootHit;                            // A raycast hit to get information about what was hit.
    int shootableMask;                              // A layer mask so the raycast only hits things on the shootable layer.
    ParticleSystem gunParticles;                    // Reference to the particle system.
    LineRenderer gunLine;                           // Reference to the line renderer.
    AudioSource gunAudio;                           // Reference to the audio source.
    Light gunLight;                                 // Reference to the light component.
    float effectsDisplayTime = 0.2f;                // The proportion of the timeBetweenBullets that the effects will display for.

    void Awake ()
    {
        // Create a layer mask for the Shootable layer.
        shootableMask = LayerMask.GetMask ("Shootable");

        // Set up the references.
        gunParticles = GetComponent<ParticleSystem> ();
        gunLine = GetComponent <LineRenderer> ();
        gunAudio = GetComponent<AudioSource> ();
        gunLight = GetComponent<Light> ();
    }

    void Update ()
    {
        // Add the time since Update was last called to the timer.
        timer += Time.deltaTime;

        // If the Fire1 button is being press and it's time to fire...
        if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets)
        {
            // ... shoot the gun.
            Shoot ();
        }

        // If the timer has exceeded the proportion of timeBetweenBullets that the effects should be displayed for...
        if(timer >= timeBetweenBullets * effectsDisplayTime)
        {
            // ... disable the effects.
            DisableEffects ();
        }
    }

    public void DisableEffects ()
    {
        // Disable the line renderer and the light.
        gunLine.enabled = false;
        gunLight.enabled = false;
    }

    void Shoot ()
    {
        // Reset the timer.
        timer = 0f;

        // Play the gun shot audioclip.
        gunAudio.Play ();

        // Enable the light.
        gunLight.enabled = true;

        // Stop the particles from playing if they were, then start the particles.
        gunParticles.Stop ();
        gunParticles.Play ();

        // Enable the line renderer and set it's first position to be the end of the gun.
        gunLine.enabled = true;
        gunLine.SetPosition (0, transform.position);

        // Set the shootRay so that it starts at the end of the gun and points forward from the barrel.
        shootRay.origin = transform.position;
        shootRay.direction = transform.forward;

        // Perform the raycast against gameobjects on the shootable layer and if it hits something...
        if(Physics.Raycast (shootRay, out shootHit, range, shootableMask))
        {
            // Try and find an EnemyHealth script on the gameobject hit.
            EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();

            // If the EnemyHealth component exist...
            if(enemyHealth != null)
            {
                // ... the enemy should take damage.
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }

            // Set the second position of the line renderer to the point the raycast hit.
            gunLine.SetPosition (1, shootHit.point);
        }
        // If the raycast didn't hit anything on the shootable layer...
        else
        {
            // ... set the second position of the line renderer to the fullest extent of the gun's range.
            gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
        }
    }
}
var damagePerShot : int = 20;                  // The damage inflicted by each bullet.
var timeBetweenBullets : float = 0.15f;        // The time between each shot.
var range : float = 100f;                      // The distance the gun can fire.

private var timer : float;                                    // A timer to determine when to fire.
private var shootRay : Ray;                                   // A ray from the gun end forwards.
private var shootHit : RaycastHit;                            // A raycast hit to get information about what was hit.
private var shootableMask : int;                              // A layer mask so the raycast only hits things on the shootable layer.
private var gunParticles : ParticleSystem;                    // Reference to the particle system.
private var gunLine : LineRenderer;                           // Reference to the line renderer.
private var gunAudio : AudioSource;                           // Reference to the audio source.
private var gunLight : Light;                                 // Reference to the light component.
private var effectsDisplayTime : float = 0.2f;                // The proportion of the timeBetweenBullets that the effects will display for.

function Awake ()
{
    // Create a layer mask for the Shootable layer.
    shootableMask = LayerMask.GetMask ("Shootable");

     // Set up the references.
    gunParticles = GetComponent (ParticleSystem);
    gunLine = GetComponent (LineRenderer);
    gunAudio = GetComponent (AudioSource);
    gunLight = GetComponent (Light);
}


function Update ()
{
    // Add the time since Update was last called to the timer.
    timer += Time.deltaTime;

    // If the Fire1 button is being press and it's time to fire...
    if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets)
    {
        // ... shoot the gun.
        Shoot ();
    }

    // If the timer has exceeded the proportion of timeBetweenBullets that the effects should be displayed for...
    if(timer >= timeBetweenBullets * effectsDisplayTime)
    {
        // ... disable the effects.
        DisableEffects ();
    }
}


public function DisableEffects ()
{
    // Disable the line renderer and the light.
    gunLine.enabled = false;
    gunLight.enabled = false;
}


public function Shoot ()
{
    // Reset the timer.
    timer = 0f;

    // Play the gun shot audioclip.
    gunAudio.Play ();

    // Enable the light.
    gunLight.enabled = true;

    // Stop the particles from playing if they were, then start the particles.
    gunParticles.Stop ();
    gunParticles.Play ();

    // Enable the line renderer and set it's first position to be the end of the gun.
    gunLine.enabled = true;
    gunLine.SetPosition (0, transform.position);

    // Set the shootRay so that it starts at the end of the gun and points forward from the barrel.
    shootRay.origin = transform.position;
    shootRay.direction = transform.forward;

    // Perform the raycast against gameobjects on the shootable layer and if it hits something...
    if(Physics.Raycast (shootRay, shootHit, range, shootableMask))
    {
        // Try and find an EnemyHealth script on the gameobject hit.
        var enemyHealth : EnemyHealth = shootHit.collider.GetComponent (EnemyHealth);

        // If the EnemyHealth component exist...
        if(enemyHealth != null)
        {   
            // ... the enemy should take damage.
            enemyHealth.TakeDamage (damagePerShot, shootHit.point);
        }

        // Set the second position of the line renderer to the point the raycast hit.
        gunLine.SetPosition (1, shootHit.point);
    }
    // If the raycast didn't hit anything on the shootable layer...
    else
    {
        // ... set the second position of the line renderer to the fullest extent of the gun's range.
        gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
    }
}

Related tutorials