Adding a Hit Point variable & having different coloured in-game text

Hello,

I had the idea of adding an integer variable for Hit Points (lets call it ‘HP’), which can change depending on the player’s choices,but I’m unsure as to how to proceed.

Also, how can I have different parts of the texts in different colours?

Any help would be greatly appreciated.

Thanks!

Pavlos

[quote=“PavlosKon, post:1, topic:6529”]
I had the idea of adding an integer variable for Hit Points (lets call it ‘HP’), which can change depending on the player’s choices,but I’m unsure as to how to proceed.[/quote]

Hi @PavlosKon, I like the sound of this!

You already have the different states in place for the Text101 game, so maybe just try something really simple to start with and then expand on it over time, for example, using one of the existing options causes an injury.

So, how could this start… well…

you are going to need something to hold the player’s health points (HP), and most likely you will want to set this at the start of the game to perhaps = 100 (representing a percentage of full health for example).

Maybe when a specific event is triggered you are going to want to deduct a value from the player’s HP, and who knows, perhaps later on there may be opportunities for the player to gain HP (maybe the player can sleep, eat some provisions or use a magic potion!).

I might to create some form of GameController ordinarily, but as TextController is currently managing all of the decisions regarding what text to display based on different player selections we can use that for now.

We will add a script to handle the storing of the player’s health points, let’s create a script called Player and add it to the TextController.

Our Player is for now just an entity which isn’t going to do anything specific from the perspective of Unity, so we can tidy it up a little bit by removing the two using references and the MonoBehaviour inheritance. Change;

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour

to

public class Player

Within the Player script you’ll want a variable to store the health of the player, perhaps this is called healthPoints.

private int _healthPoints;

Let’s also add a public property which we can use to return the player’s health points when we want to;

public int HealthPoints
{
    get { return _healthPoints; }
}

Next we’ll create three methods, Spawn, ReduceHealthPoints, IncreaseHealthPoints. These methods are likely to be called from our game controller (currently TextController) so let’s also make them Public methods.

It would be tempting to let these methods just change the health points of the player up and down by a set amount, hard coded, but what happens when the player chooses to be a Barbarian instead of an Elf, we may want to have their initial health set to a different value, also, what if the player doesn’t just stub their toe, but falls into a pit - we will want to hurt the player more!

So why not pass these values in to our methods and then set them;

private void Spawn(int healthPoints) {
    _healthPoints = healthPoints;     
}

…and a value to represent the amount of damage to incur, or health to revive respectively;

public void ReduceHealthPoints(int points) {
    _healthPoints -= points;
}

public void IncreaseHealthPoints(int points) {
    _healthPoints += points;
}

There may also be some conditions where you just want to kill the player without worrying about how much damage to injure them by, let’s add a method for that too whilst we are here;

public void Kill() {
    _healthPoints = 0;
}

We are going to need to create a constructor for the Player too, a way of creating a new instance of this object, this should work;

private Player() { }

and

public Player(int healthPoints) {
    Spawn(healthPoints);
}

Why two constructors? Rather than having the ability to create an empty Player object without any health points (or any other specific attributes later) the above creates a private default constructor which you can’t use from the game controller (in our case the TextController). The second constructor is overloaded, it takes a int value for the health points for the player which it subsequently passes to our private Spawn() method. As you can see from the above Spawn() is the method which actually sets the health points for the Player.

This is a little bit more than you really need at this point, but it will give you an idea of how you can re-use the code you are writing - from here you could create a player of various differing strengths as I mentioned earlier (barbarian, elf, dwarf, wizard etc).

So - at this point we have a Player class which provides methods for creating a Player object with a specific amount of health, we have also written functionality to support injuring and healing our player, and finally, although we may not use it yet, we have a specific method for killing the player completely.

But we need to integrate it with the existing Text101 game…

Let’s open up the TextController script and add two new variables;

public Text text;

public Text playerHealth;
private Player _player;

playerHealth is a Text object which we will add later to display the player’s current health, it is set to Public like text so that we can access it via the Inspector.

player is our Player object which we will instantiate and keep here so that we can access it anywhere from within our TextController script.

When the current Text101 game starts it calls the Start() method within the TextController as you know and sets the game state to States.cell, we can use the same method for creating our Player.

void Start()
{
    myState = States.cell;

    _player = new Player(100);  // instantiate the Player and give them 100 health points

    playerHealth.text = "Current Health : " + _player.HealthPoints.ToString();
}

As you can see above, we set our local _player variable to equal our instantiated Player, we use the overloaded constructor to pass in a integer value of 100 so that our Player begins with 100 health points.

Next we update playerHealth.text, we use the public HealthPoints property of our Player (_player.HealthPoints) to return the current number of health points and add them to the text to be displayed on the screen.

Ok - so if you are still with me, all that is left to do is add something to our state handler methods which will perhaps harm our player. Let’s make taking the mirror cut our player’s finger.

void mirror() is the method which allows us to take the mirror if we press T, let’s make a change here;

void mirror()
{
    text.text = "The dirty old mirror on the wall seems loose.\n\n" +
                "Press T to Take them mirror, or R to return to cell.";

    if (Input.GetKeyDown(KeyCode.T))
    {            
        _player.ReduceHealthPoints(15);  // cause a minor injury to the player

        myState = States.cell_mirror;
    }
    else if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell; }
}

You can see above that if the player choose to take the mirror by pressing T we call the ReduceHealthPoints() method on our _player and pass a small value in, in this case 15 health points.

We will want to indicate to the player that this has just happened, so let’s change the message displayed to them in void cell_mirror();

void cell_mirror()
{
    text.text = "Taking the mirror from the wall you cut your finger!  Ourch!\n\n" +
                "You are still in your cell, and you STILL want to escape! " +
                "There are " +
                "some dirty sheets on the bed, a mark where the mirror was, " +
                "and that pesky door is still there, and firmly locked.\n\n" +
                "Press S to view Sheets, or L to view Lock.";

    if (Input.GetKeyDown(KeyCode.S)) { myState = States.sheets_1; }
    else if (Input.GetKeyDown(KeyCode.L)) { myState = States.lock_1; }
}

Note - this is slightly less than ideal, as each time the player returns to the cell it will tell them again and again that they cut their finger removing the mirror (I had limited time to knock this together for you). You can work around this by creating more States and update the text for each accordingly. You could also have a separate UI.Text on the screen which displays one off announcements, if you used this approach you could update the text on that to say that the player had cut their finger removing the mirror, but without change the specific text for the story in the above State. You will want to consider you approach carefully as some injuries may be able to be healed, so having specific text messages that say you are injured when you are not would not be very good.

We should also make sure that we update the displayed health points to our player, otherwise although the player now only has 85 health points, we will still be displaying 100 (from our Start() method call), this can be achieved by updating the Update() method as follows;

// Update is called once per frame
void Update()
{
    print(myState);
    if (myState == States.cell) { cell(); }
    else if (myState == States.sheets_0) { sheets_0(); }
    else if (myState == States.sheets_1) { sheets_1(); }
    else if (myState == States.lock_0) { lock_0(); }
    else if (myState == States.lock_1) { lock_1(); }
    else if (myState == States.mirror) { mirror(); }
    else if (myState == States.cell_mirror) { cell_mirror(); }
    else if (myState == States.corridor_0) { corridor_0(); }
    else if (myState == States.stairs_0) { stairs_0(); }
    else if (myState == States.stairs_1) { stairs_1(); }
    else if (myState == States.stairs_2) { stairs_2(); }
    else if (myState == States.courtyard) { courtyard(); }
    else if (myState == States.floor) { floor(); }
    else if (myState == States.corridor_1) { corridor_1(); }
    else if (myState == States.corridor_2) { corridor_2(); }
    else if (myState == States.corridor_3) { corridor_3(); }
    else if (myState == States.closet_door) { closet_door(); }
    else if (myState == States.in_closet) { in_closet(); }

    // update the player's health on the screen
    playerHealth.text = "Current Health : " + _player.HealthPoints.ToString();
} 

Note - as per the comment above the code, this is being called once per frame which means that all of the statements in our state handler methods are being called over and over again, this isn’t overly efficient, but I have continued this example in the same fashion so that we do not drastically change the whole project.

Finally, the one thing we haven’t done yet is to actually add a UI.Text object to the canvas so that we can display the player’s current health. Create a new UI.Text object, call it PlayerHealth. It’s not really necessary, but you could change the default Text to read "Current Health : " also, at least when you are not running the game and are in scene view you will be able to more easily see this UI.Text object.

Now select your TextController in the hierarchy and you will see that there is a new property called PlayerHealth, drag your UI.Text object (under the canvas in the hierarchy) across to this.

Below is a full copy of the two scripts I have for this little game for your reference.

Player.cs

public class Player
{
    /// <summary>
    /// Stores the number of health points for the Player
    /// </summary>
    private int _healthPoints;

    /// <summary>
    /// Returns the number of health points for the Player
    /// </summary>
    public int HealthPoints
    {
        get { return _healthPoints; }
    }


    /// <summary>
    /// Default constructor, set to private to prevent creating a Player with no health points
    /// </summary>
    private Player() { }

    /// <summary>
    /// Overloaded constructor
    /// </summary>
    /// <param name="healthPoints">The number of health points the Player will start with</param>
    public Player(int healthPoints)
    {
        // spawn (initialise) our Player
        Spawn(healthPoints);
    }

    /// <summary>
    /// Initialises our Player
    /// </summary>
    /// <param name="healthPoints">The number of health points the Player will start with</param>
    private void Spawn(int healthPoints)
    {
        _healthPoints = healthPoints;
    }

    /// <summary>
    /// Reduces the Player's health points by the specified value
    /// </summary>
    /// <param name="points">The number of health points to reduce by</param>
    public void ReduceHealthPoints(int points)
    {
        _healthPoints -= points;
    }

    /// <summary>
    /// Increases the Player's health points by the specified value
    /// </summary>
    /// <param name="points">The number of health points to increase by</param>
    public void IncreaseHealthPoints(int points)
    {
        _healthPoints += points;
    }


    /// <summary>
    /// Reduces our Player's health points to zero
    /// </summary>
    public void Kill()
    {
        _healthPoints = 0;
    }
}

TextController.cs

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

public class TextController : MonoBehaviour
{

    public Text text;
    public Text playerHealth;
    private Player _player;

    private enum States
    {
        cell, mirror, sheets_0, lock_0, cell_mirror, sheets_1, lock_1, corridor_0, stairs_0, stairs_1,
        stairs_2, courtyard, floor, corridor_1, corridor_2, corridor_3, closet_door, in_closet
    };
    private States myState;

    // Use this for initialization
    void Start()
    {
        myState = States.cell;

        _player = new Player(100);

        playerHealth.text = "Current Health : " + _player.HealthPoints.ToString();
    }

    // Update is called once per frame
    void Update()
    {
        print(myState);
        if (myState == States.cell) { cell(); }
        else if (myState == States.sheets_0) { sheets_0(); }
        else if (myState == States.sheets_1) { sheets_1(); }
        else if (myState == States.lock_0) { lock_0(); }
        else if (myState == States.lock_1) { lock_1(); }
        else if (myState == States.mirror) { mirror(); }
        else if (myState == States.cell_mirror) { cell_mirror(); }
        else if (myState == States.corridor_0) { corridor_0(); }
        else if (myState == States.stairs_0) { stairs_0(); }
        else if (myState == States.stairs_1) { stairs_1(); }
        else if (myState == States.stairs_2) { stairs_2(); }
        else if (myState == States.courtyard) { courtyard(); }
        else if (myState == States.floor) { floor(); }
        else if (myState == States.corridor_1) { corridor_1(); }
        else if (myState == States.corridor_2) { corridor_2(); }
        else if (myState == States.corridor_3) { corridor_3(); }
        else if (myState == States.closet_door) { closet_door(); }
        else if (myState == States.in_closet) { in_closet(); }

        // update the player's health on the screen
        playerHealth.text = "Current Health : " + _player.HealthPoints.ToString();
    }

    #region State handler methods

    void cell()
    {
        text.text = "You are in a prison cell, and you want to escape. There are " +
                    "some dirty sheets on the bed, a mirror on the wall, and the door " +
                    "is locked from the outside.\n\n" +
                    "Press S to view Sheets, M to view Mirror, and L to view Lock. ";

        if (Input.GetKeyDown(KeyCode.S)) { myState = States.sheets_0; }
        else if (Input.GetKeyDown(KeyCode.M)) { myState = States.mirror; }
        else if (Input.GetKeyDown(KeyCode.L)) { myState = States.lock_0; }
    }

    void mirror()
    {
        text.text = "The dirty old mirror on the wall seems loose.\n\n" +
                    "Press T to Take them mirror, or R to return to cell.";

        if (Input.GetKeyDown(KeyCode.T))
        {            
            _player.ReduceHealthPoints(15);  // cause a minor injury to the player

            myState = States.cell_mirror;
        }
        else if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell; }
    }

    void cell_mirror()
    {
        text.text = "Taking the mirror from the wall you cut your finger!  Ourch!\n\n" +
                    "You are still in your cell, and you STILL want to escape! " +
                    "There are " +
                    "some dirty sheets on the bed, a mark where the mirror was, " +
                    "and that pesky door is still there, and firmly locked.\n\n" +
                    "Press S to view Sheets, or L to view Lock.";

        if (Input.GetKeyDown(KeyCode.S)) { myState = States.sheets_1; }
        else if (Input.GetKeyDown(KeyCode.L)) { myState = States.lock_1; }
    }

    void sheets_0()
    {
        text.text = "You can't believe you sleep in these things.  Surely it's " +
                    "time somebody changed them.  The pleasures of prison life " +
                    "I guess!\n\n" +
                    "Press R to Return to roaming your cell.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell; }
    }

    void sheets_1()
    {
        text.text = "Holding a mirror in your hand doesn't make the sheets look " +
                    "any better.\n\n" +
                    "Press R to Return to roaming your cell.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell_mirror; }
    }

    void lock_0()
    {
        text.text = "This is one of those button locks.  You have no idea what the " +
                    "combination is.  You wish you could somehow see where the dirty " +
                    "fingerprints were, maybe that would help?\n\n" +
                    "Press R to Return to roaming your cell.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell; }
    }

    void lock_1()
    {
        text.text = "You carefully put the mirror through the bars, and turn it round " +
                    "so you can see the lock.  You can just make out fingerprints around " +
                    "the buttons.  You press the dirty buttons and here a click.\n\n" +
                    "Press O to Open or R to Return to your cell.";

        if (Input.GetKeyDown(KeyCode.O)) { myState = States.corridor_0; }
        else if (Input.GetKeyDown(KeyCode.R)) { myState = States.cell_mirror; }
    }

    void corridor_0()
    {
        text.text = "You are out of your cell, but not out of trouble.  " +
                    "You are in the corridor, there is a closet and some stairs leading to " +
                    "the courtyard.  There's also various detritus on the floor.\n\n" +
                    "C to view the closet, F to inspect the Floor, and S to climb the Stairs.";

        if (Input.GetKeyDown(KeyCode.S)) { myState = States.stairs_0; }
        else if (Input.GetKeyDown(KeyCode.C)) { myState = States.closet_door; }
        else if (Input.GetKeyDown(KeyCode.F)) { myState = States.floor; }
    }

    void stairs_0()
    {
        text.text = "You start walking up the stairs towards the outside light.  " +
                    "You realise it's not break time and you'll be caught immediately.  " +
                    "You slither back down the stairs and return to the corrdior.\n\n" +
                    "Press R to Return to the corridor.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_0; }
    }

    void stairs_1()
    {
        text.text = "Unfortunately weilding a puny hairclip hasn't given you the " +
                    "confidence to walk out into the courtyard surrounded by armed guards!\n\n" +
                    "Press R to Retreat down the stairs.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_1; }
    }

    void stairs_2()
    {
        text.text = "You feel smug for picking the closet door open, and are still armed with " +
                    "a hairclip (no badly bent).  Even these achievements together don't give " +
                    "you the courage to climb up the stairs to your death!\n\n" +
                    "Press R to Return to the corridor.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_2; }
    }

    void floor()
    {
        text.text = "Rummaging around the dirty floor, you find a hairclip.\n\n" +
                    "Press R to Return to the standing, or H to take the Hairclip.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_0; }
        else if (Input.GetKeyDown(KeyCode.H)) { myState = States.corridor_1; }
    }

    void corridor_1()
    {
        text.text = "Still in the corridor.  Floor still dirty.  Hairclip in hand.  " +
                    "Now what?  You wonder if that lock on the closet would succumb " +
                    "to some lock-picking??\n\n" +
                    "P to Pick the lock, S to climb the Stairs.";

        if (Input.GetKeyDown(KeyCode.P)) { myState = States.in_closet; }
        else if (Input.GetKeyDown(KeyCode.S)) { myState = States.stairs_1; }
    }

    void closet_door()
    {
        text.text = "You are looking at a closet door.  Unfortunately it's locked.  " +
                    "Maybe you could find something around to encourage it open?\n\n" +
                    "Press R to Return to the corridor.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_0; }
    }

    void in_closet()
    {
        text.text = "Inside the closet you see a cleaner's uniform that looks about your size!  " +
                    "Seems like your day is looking up.\n\n" +
                    "Press D to Dress up, or R to the corridor.";

        if (Input.GetKeyDown(KeyCode.R)) { myState = States.corridor_2; }
        else if (Input.GetKeyDown(KeyCode.D)) { myState = States.corridor_3; }
    }

    void corridor_2()
    {
        text.text = "Back in the corridor.  Having declined to dress-up as a clear.\n\n" +
                    "Press C to revisit the Closet, and S to climb the Stairs.";

        if (Input.GetKeyDown(KeyCode.C)) { myState = States.in_closet; }
        else if (Input.GetKeyDown(KeyCode.S)) { myState = States.stairs_2; }
    }

    void corridor_3()
    {
        text.text = "You're standing back in the corridor, now convincingly dressed as a cleaner.  " +
                    "You strongly consider the run for freedom.\n\n" +
                    "Press S to take the Stairs or U to Undress.";

        if (Input.GetKeyDown(KeyCode.S)) { myState = States.courtyard; }
        else if (Input.GetKeyDown(KeyCode.U)) { myState = States.in_closet; }
    }

    void courtyard()
    {
        text.text = "You walk through the courtyard dressed as a cleaner.  " +
                "The guard tips his hat at you as you waltz passed, claiming " +
                "your freedom.  Your heart races as you walk into the sunset.\n\n" +
                "Press P to Play again.";

        if (Input.GetKeyDown(KeyCode.P)) { myState = States.cell; }
    }
    #endregion
}

So, what does it all look like…

Take the mirror!

You will find that there are obviously many different ways of achieving a task, the above is just one that fits reasonably well with the existing Text101 game from the course, I hope you find it of use and can perhaps take it further forwards, improve on it… and get to call that _player.Kill() method :wink:

I’ve not tried this myself, however I found this on the Unity documentation website:
https://docs.unity3d.com/Manual/StyledText.html

1 Like

Hello Rob,

Thanks for the in-depth reply! I will implement it and get back to you.

As for the text colour, I had already checked that link but was unsure as to how to use the formatting within strings in my code . I need to look into that a bit more.

Anyway, thanks again for your help Rob.

Best,

Pavlos

1 Like

Hi, you’re more than welcome - I hope it is of use. Let us know how you get on :slight_smile:

Privacy & Terms