Tutorial: 1st-person sneak in Unity 5, part 5

This part adds logic for the PC and keep track of things such as health, dying. I also add functionality for invisibility (that have already partly added in Guard class).  I also add HUD using the new GUI system to show health and energy (that is used to maintain invisibility).

Previous parts:

Lets start with setting up HUD. We need Canvas (GameObject -> UI -> Canvas), couple Images (GameObject -> UI – Image) and Sliders (GameObject -> UI -> Slider). I add canvas under GameManager. I first created background for Invisibility bar and Slider for displaying the energy left. You can drag then object where you want them to be, After that was ready, I copied objects (and modified the parts to make it fit to another corner and have correct texts.)

HUD objects set-up. After creating  images for HUD, set the texture type of the textures as Sprite (2D and UI) and after that you can attach the textures to UI image object.
HUD objects set-up. After creating images for HUD, set the texture type of the textures as Sprite (2D and UI) and after that you can attach the textures to UI image object.

(Unity UI tutorial is a good starting point to understand how the UI system works.)

As I want to health and invisibility bars to be at the bottom corners, I need to anchor them to those corners.

Setting  up Invisibility/Energy bar. Note the anchoring and values (marker with the red circle),
Setting up Invisibility/Energy bar. Note the anchoring. Sprite to be drawn is dropped to Source Image. If you  want to image to keep it aspect ration despite of the screen aspect ration, select Preserve Aspect.

Slider object needs some fine-tuning also.

Slider Object and Source Image sprite setup in Sprite editor. Select Image Type Sliced and unselect fill center to make the only outlines drawn as in the figure. Configure also bordets in in Sprite editor to  show what areas can be stretched to when fitting the slider background.
Slider Object and Source Image sprite setup in Sprite editor. Select Image Type Sliced and unselect fill center to make the only outlines drawn as in the figure. Configure also bordets in in Sprite editor to show what areas can be stretched to when fitting the slider background.
Slider area setup. Set Fill source image to none and fine tune the colour.
Slider area setup. Set Fill source image to none and fine tune the colour.
Modify values marked in red to match the values in figure. Modify the slider object to look good.
Modify values marked in red to match the values in figure. Modify the slider object to look good.

Copy the Energy/Invisibility bar and fine tune it to health bar.

Now we have HUD ready to be used. I need to pass pointers to the PC object to sliders so that they actually shows the values they are intended to so. So I add functionality to GameAgents.cs

GameAgents.cs

public class GameAgents : MonoBehaviour {
	
	public Slider energyBar;
	public Slider healthBar;

and then the end of Awake():

	PlayerLogic pc = player.GetComponent();
	pc.energyBar = energyBar;
	pc.healthBar = healthBar;

Drag-and-drop sliders from the HUD to energyBar and healthBar. And next, it is time to revise PlayerLogic and add functionality there.

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class PlayerLogic : MonoBehaviour, CharacterInterface {

    // Sliders for displaying energy and health
    public Slider energyBar;
    public Slider healthBar;
 
    // how much energy maintaining invisibility costs
    public float invisibilityCostInterval = 1.0f;
    public int invisibilityCost = 5;
   
    // Delay before start screen is loaded after dying
    public float startScreenDelay = 2.0f;

    public int startEnergy = 0;
    public int startHealth = 100;

    public const int MAX_ENERGY = 100;
    public const int MAX_HEALTH = 100;
	
    // definitions for saving and loading dats 
    public const string HEALTH_KEY = "h";
    public const string ENERGY_KEY = "e";
    public const string CURRENT_LEVEL_KEY = "c";
    public const string LAST_DOOR_KEY = "d";

    private int health;
    private int energy;
    
    private bool invisible;
    
    // effects for later use. Grayscale effect is used when
    // the PC dyes and Glow and AudioLowPassFilter are use
    // when PC is invisible. Camera should have these components 
    // attached.
    private GrayscaleEffect grayscaleEffect;
    private GlowEffect glowEffect;
    private AudioLowPassFilter audioLowpassFilter;
	
    void Awake () {
        invisible = false;

        glowEffect = GetComponentInChildren();
        if (!glowEffect) {
            Debug.LogError("PlayerLogic.Awake(): Camera does not have GlowEffect component");
        }
        else {
            glowEffect.enabled = false;
        }
        grayscaleEffect = GetComponentInChildren();
        if (!grayscaleEffect) {
            Debug.LogError("PlayerLogic.Awake(): Camera does not have GrayscaleEffect component");
        }
        else {
            grayscaleEffect.enabled = false;
        }

        audioLowpassFilter = GetComponentInChildren();
        if (!audioLowpassFilter) {
            Debug.LogError("PlayerLogic.Awake(): Camera does not have AudioLowpassFilter component");
        }
        else {
            audioLowpassFilter.enabled = false;
        }
	ReadSave();
     }

     void Update () {
        // display in HUD the current values of energy and health
	energyBar.value = energy;
	healthBar.value = health;
		
	if (health <= 0) {
              // if dead do nothing...
   	     return;
  	}
        // turning invisible with Fire1 or "o"
  	if (Input.GetButton("Fire1") || Input.GetKey("o")) { 
                // invisibility
  		if (!IsInvoking()) {
                        // Invisibility started. Enabling effects
                        // and start tick reducing energy
  			glowEffect.enabled = true;
  		        audioLowpassFilter.enabled = true;
  			InvokeRepeating("MaintaingInvisibility", 0.0f, invisibilityCostInterval);
  		}
  	}  	else {
                 // not invisible
  		if (IsInvoking()) {
                         // but the PC was invisible in the last frame
                         // disabling the effects
  			glowEffect.enabled = false;
  			audioLowpassFilter.enabled = false;
  			CancelInvoke();
                         // and setting internal state to visible
                         invisible = false;
  		}
  	}
      }

     public static void DeleteSave() {
         // Data is store in PlayerPrefs. Deleting the data
  	PlayerPrefs.DeleteKey(CURRENT_LEVEL_KEY);
  	PlayerPrefs.DeleteKey(ENERGY_KEY);
  	PlayerPrefs.DeleteKey(HEALTH_KEY);
  	PlayerPrefs.DeleteKey(LAST_DOOR_KEY);
      }

      public void ReadSave () {
          // reading the saved data
          int e = PlayerPrefs.GetInt(ENERGY_KEY);
          int h = PlayerPrefs.GetInt(HEALTH_KEY);
          // if health is zero, the PC just died and
          // and we go back to max
          if (h > 0) {
            energy = e;
            health = h;
        }
        else {
            energy = startEnergy;
            health = startHealth;
        }
    }

    bool CharacterInterface.IsInvisible () {
	return invisible;
    }
	
    void CharacterInterface.ReceiveDamage (int amount) {
	if (amount < 0) {
		Debug.LogError("PlayerLogic.RecieveDamage() called with negative damage. Ignoring");
		return;
	}
	health -= amount;
	if (health <= 0) {
		health = 0;
		grayscaleEffect.enabled = true;
		Invoke("StartScreen", startScreenDelay);
	}
    }
	
    void CharacterInterface.PowerUp (int _energy, int _health) {
	if (_energy < 0 || _health < 0) {
  		return;
  	}  	energy += _energy;
 	if (energy > MAX_ENERGY) {
		energy = MAX_ENERGY;
	}
	health += _health;
	if (health > MAX_HEALTH) {
		health = MAX_HEALTH;
	}
    }
	
    bool CharacterInterface.CanUseHealth() {
        // return if the PC needs health from Powerup. If not
        // the power-up won't active
	if ( health < MAX_HEALTH) {
		return true;
	}
	else {
	        return false;
	}
     }
	
     bool CharacterInterface.CanUseEnergy() {
	if ( energy < MAX_ENERGY) {
		return true;
	}
	else {
		return false;
	}
      }

      void CharacterInterface.Save(string levelName, string door) {
	  Debug.Log ("PlayerLogic.Save(" + levelName + ")");
	  PlayerPrefs.SetInt(ENERGY_KEY, energy);
	  PlayerPrefs.SetInt(HEALTH_KEY, health);
	  PlayerPrefs.SetString(CURRENT_LEVEL_KEY, levelName);
	  PlayerPrefs.SetString(LAST_DOOR_KEY, door);
      }

      public void Save () {
	  Debug.Log ("PlayerLogic.Save() called.");
	  ((CharacterInterface)this).Save (Application.loadedLevelName, "");
      }

      void Start() {
	   Transform startPosition = null;
	   string door = PlayerPrefs.GetString(LAST_DOOR_KEY);
	   if (door != "") {
		GameObject doorObj = GameObject.Find(door);
		if (doorObj) {
			startPosition = doorObj.transform.Find("spawn");
			if(startPosition == null) {
				Debug.LogError ("PlayerLogic.Start(): " + doorObj.name + " has now spawn point");
			}
		}
	    }
		
	    if(startPosition == null) {                            
        	GameObject ps = GameObject.FindWithTag("PlayerStartPoint");
        	if (startPosition) {
			startPosition = ps.transform;
        	}
        	else {
			Debug.LogError("PlayerLogic.Start(): level has not StartPosition waypoint");
		}
	     }
	     Debug.Log("PlayerLogic.OnStart(): spawning the PC at " + startPosition.name );
             transform.rotation = startPosition.transform.rotation;
             transform.position = startPosition.position;
             CancelInvoke();
             invisible = false;
             glowEffect.enabled = false;
             audioLowpassFilter.enabled = false;
    }

    private void MaintaingInvisibility () {
        // ticks while invisibility is maintained
        // reduce energy and quits if energy runs out
        energy = energy - invisibilityCost;
        invisible = true;
        if (energy < 0) {
            energy = 0;
            invisible = false;
            glowEffect.enabled = false;
            audioLowpassFilter.enabled = false;
            CancelInvoke();
        }
    }
    
    private void StartScreen () {
	Application.LoadLevel(0);
    }
}

Published by lankoski

Petri Lankoski, D.Arts, is a Associate Professor in Game Studies at the school of Communication, Media and IT at the Södertörn University, Sweden. His research focuses on game design, game characters, role-playing, and playing experience. Petri has been concentrating on single-player video games but researched also (multi-player) pnp and live-action role-playing games. This blog focuses on his research on games and related things.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: