Wednesday 16 November 2016

Zelda Style Life System Unity Tutorial - Part 9 of 10

Zelda Style Life System Unity Tutorial - Part 9 of 10




The end result of the full tutorial.


At the end of Part 9 we will have produced this (See image 1.0).
Image 1.0


Before you start you should know these things:

You should have gone through and completed ‘Part 8’ of this tutorial before going any further. If you find yourself not understanding some of the terminology or code of the tutorial I would also recommend going through the previous tutorial to get up to speed. I will leave a link to it below to go through at your leisure.



If you follow this tutorial and find I am moving too fast or if you don’t know the things in the list above, I would recommend getting up to speed and then come back to this tutorial when you are ready.

Links to Parts 1 - 10 of this tutorial series:

Part 1

Part 2

Part 3

Part 4

Part 5

Part 6

Part 7

Part 8

Part 10


With this tutorial if you want to convert this into JavaScript by all means do so but it may be easier for you to follow this in C# and stick with the language of the tutorial to make it easier.

PROJECT RESOURCES: At the bottom of the post there is a download link to a zip file. This files includes all files needed to follow Part 9 and the completed project file of Part 9.

In this part we will work on polishing what we have by adding more detail and flexibility to the system.

We will:

  • Fix all the errors created in Part 8

Step 1: – Fixing the 'Divide By Zero' Error

At the moment a few things will happen when we try our code during runtime. When we click the ‘Take Some Damage’ button a few times we get an error ‘Divide By Zero Exception’ (See figure 1.0). This is down to a few things. In the inspector we need to change ‘startingHealth’ to 30, then change ‘healthPerLife’ to 10. Then in the Start function where we have a reference to ‘AddLives’ we parse it ‘startingHealth’ we need to tells it to divide that by ‘healthPerLife’. (See figure 1.0)


Figure 1.0


Code:

void Start ()
{
  GetScreenDimentions();
  SetImageSize();
  ResetGridLayoutProperties();
  AddLives (startingHealth / healthPerLife);
}


After this we need to change some of the code in the ‘ModifyHealth’ function and clamp the ‘currentHealth’ variable (this is causing part of the problem). We need to make sure ‘currentHealth’ does not rise above a set maximum nor go below zero. Unity has a function for this called ‘Mathf.Clamp’, you set the variable you want to the function, then parse it the variable you want clamped, the minimum and the maximum. Then it takes care of the rest for you. The minimum we will set it to is zero and the maximum we will set it to is a new private variable called ‘maxLives’.

Code:

private int maxLives;

void ModifyHealth (int amt)
{
  currentHealth += amt;
  currentHealth = Mathf.Clamp(currentHealth, 0, maxLives);
  UpdateLives();
}


Step 2: – Fixing the 'currentHealth' value in 'AddLives' with 'maxHealth'

Now there is another bit causing the problem. In ‘AddLives’ ‘currentHealth’ is incrementally added to by ‘n’. This worked ok when we just have one icon her health, but since we now have multiple health values per icon it’s causing us problems. First off we want to increase ‘MaxLives’ when we gain more lives when our health if full. Then we want out ‘currentHealth’ to be the same as ‘maxLives’. In order to get max lives to increase based on health, we need to multiply ‘n’ (parsed to it by the ‘AddHealth’ function, and multiply it by ‘healthPerLife’.


Code:

void AddLives(){
  for (int i = 0; i < n; i++)
  {
    Image tempImg = Instantiate(lifeImg,
                                transform.position,
                                transform.rotation;
    tempImg.transform.parent = transform;
    livesArray.Add(tempImg);
  }

  maxLives += n * healthPerLife;
  currentHealth = maxLives;
  UpdateLives();
}



Now we have this as a quick summary of what the ‘lifeImagesIndex’ does is, if each ‘lifeImg’ is equal to e.g. ten, and we have ten images in our ‘lifeImagesArr’ then each image is equivalent to one health. Next we will use ‘lifeImagesIndex’ to reference the ‘lifeImagesArr’ element we need in the ‘Sprite’ array and assign it to ‘lifeImg’.

Code:

private void UpdateLives()
{
  bool restOfLivesAreEmpty = false;
  int i = 1;
  foreach (Image life in livesArray)
  {
    if (restOfLivesAreEmpty)
    {
      life.GetComponent<Image>().sprite = lifeImages[0];
    } else {
      if (currentHealth >= i * healthPerLife)
      {
        life.GetComponent<Image>().sprite =
                            lifeImages[lifeImages.Length - 1];
      } else {
        int currLifeHealth = (int)(healthPerLife –
                                  (healthPerLife * i –
                                   currentHealth));
        int healthPerLifeImg = healthPerLife / lifeImages.Length;
        int lifeImagesIndex = currLifeHealth / healthPerLifeImg;
        life.GetComponent<Image>().sprite =
                            lifeImages[lifeImagesIndex];
        restOfLivesAreEmpty = true;
      }
    }
  }
}


Step 3: – Fixing the 'foreach' iterator logical error

If we leave things as they are we can see the result of our bug fixes, but there are more to go. If we were to run our code, and taking some damage, the ‘currentHealth’ variable would drop (visible in the console), but the hearts would not deplete (until we get to zero where the middle one becomes empty). This is still not good enough. (See figures 1.1 and 1.2).



Figure 1.1 left, figure 1.2 right.


Fixing this nasty bug is very simple in ‘UpdateLives’ where we declare ‘i = 1’, we need to change this to zero ‘0’. Then at the top of the first else statement we need to increment ‘i’ by 1 to properly keep track of the latest element.
Code:

private void UpdateLives()
{
  bool restOfLivesAreEmpty = false;
  int i = 0;
  foreach (Image life in livesArray)
  {
    if (restOfLivesAreEmpty)
    {
      life.GetComponent<Image>().sprite = lifeImages[0];
    } else {
      i += 1;
      if (currentHealth >= i * healthPerLife)
      {
        life.GetComponent<Image>().sprite = 

                                  lifeImages[lifeImages.Length - 1];
      } else {
        int currLifeHealth = (int)(healthPerLife – 

                                  (healthPerLife * i – 
                                   currentHealth));
        int healthPerLifeImg = healthPerLife / lifeImages.Length;
        int lifeImagesIndex = currLifeHealth / healthPerLifeImg;
        life.GetComponent<Image>().sprite = 

                                  lifeImages[lifeImagesIndex];
        restOfLivesAreEmpty = true;
      }
    }
  }
}



Great, part four is finished. We have laid down more foundations to the frame work.

We have learnt how to fix:


  • A divide by zero error using ‘Mathf.Clamp’ with our ‘currentHealth
  • Add health at the start properly by dividing ‘startingHealth’ by ‘healthPerLife
  • Increate max lives by new lives added time by health per life
  • Modify an iterator


In Part 10 we will learn how to show a quarter heart when a heart is nearly empty, adjust a few parameters, heal the player and finally see our code in action. Click here for Part 10.


If you liked this tutorial leave a comment below telling me why you like it and share it with a friend who will find it useful. If you didn’t like it, please leave a comment below saying why.

If you would would like to see more of these tutorials, please leave a comment below. And if you want more of this particular tutorial say what you want to see more of in the comments.


Download resources and project files.

No comments:

Post a Comment