Wednesday 24 August 2016

Zelda Style Life System Unity Tutorial – Part 4 of 10

Zelda Style Life System Unity Tutorial – Part 4



The end result of the full tutorial.


At the end of Part 4 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 3’ 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.


http://joseph-easter.blogspot.co.uk/2016/08/zelda-style-life-system-unity-tutorial17.html


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 5

Part 6

Part 7

Part 8

Part 9

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 4 and the completed project file of Part 4.

In this part we will work on finishing the foundations of our framework, after this we will add more functionality and refinements. We will:

  • Reformat some of our code to make it more reusable
  • Instantiate any number of images by parsing it an integer
  • Set a starting health
  • Instantiate a number of images at the beginning of a scene based on starting health

Step 1: – Reformatting our code

We need to a bit of house keeping before we go any further. Some of our code needs reformatting. Specifically how we instantiate our ‘lifeImg’ game object. At the start we instantiate it as we normally would. However, this is no good as we need to be able to instantiate it both on the first frame of the scene and when the player gains a life (or whenever you want to in your project).

If we were to keep this the way this is, it would be a lot of code to write every single time. Not only this, when you spawn the number of lives at the start you could have to copy and paste this multiple times, that’s just messy and inefficient.

We need to create a function for instantiating the ‘lifeImg’ game object, and place our code in there. For now we will call our function ‘
AddLives’.Code:

void Start ()
{
    GetScreenDimentions();
    SetImageSize();
    AddLives();
}

void AddLives()
{

    Image tempImg = Instantiate(lifeImg, 

                                transform.position, 
                                transform.rotation) as Image;
    tempImg.transform.parent = transform;
}



Then respectively add a reference to it in the ‘Start’ method. If we enter ‘Play’ mode, the behaviour of our code will not have changed, we’ve made it easier to read and more usable.


Step 2: - Instantiating multiple prefabs

At the moment with our code, the ‘AddLives’ method only instantiates one ‘lifeImg’ when it's referenced. This means every time we will have to reference the function every time we want to Instantiate a ‘lifeImg’. This in cumbersome, inefficient and produces redundant code. What we need to do is tell the method how many ‘lifeImg’ we want instantiated when we reference the function.

The way we need this to work is, we need to parse the function an int value of how much health we need to add (or how many prefabs to Instantiate). Then tell it to Instantiate one prefab per iterative loop until it has completed the for loop based on the value parsed to it.

First we need to add an int called ‘n’ for the number we want to parse to the function. Then create a for loop which starts at value ‘0’ and stops when it has reached value ‘n’. When this is done place the Instantiate and parenting code inside of the for loop.


Code:

void AddLives(int n)
{
    for(int i = 0; i < n; i++)
    {

    
    Image tempImg = Instantiate(lifeImg, 
                                    transform.position, 
                                    transform.rotation) as Image;
        tempImg.transform.parent = transform;
    }
}



As we want to check that our code works and at the start of the game we want to see we have a full bill of health we will parse a number to the ‘AddLives’ method reference in ‘Start’ function.
Code:

void Start ()
{
    GetScreenDimentions();
    SetImageSize();
    ResetGridLayoutProperties();
    AddLives( 3 );
}



Good stuff, it works! The best thing is the ‘GridLayoutGroup’ component has positioned all of the newly created prefabs for us so we do not need to code that.

For ease of use when designing your project we will make a slight alteration to this before we go any further. Rather than using an arbitrary number, we will create an int called ‘startingHealth’ and parse that to ‘AddLives’ in the start method. This way you can tweak this in the inspector, it will do exactly the same thing. (See figure 1.0)


Code:

public int startingHealth = 3;

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

Figure 1.0

This is good but unlikely what you need, and not the effect we are after, with them descending horizontally. We need the starting axis to be vertical. This is quite a simple fix, we can fix this in the inspector or through code. We will do it through code. In the ‘ResetGridLayoutProperties’ we need to change ‘gridLayoutGroup’s’ starting axis to ‘Vertical’. (See figure 1.1)


Code:

void ResetGridLayoutProperties ()
{
    gridLayoutGroup = GetComponent<GridLayoutGroup>();
    gridLayoutGroup.cellSize = new Vector2(lifeImgWidth, 

                                           lifeImgHeight);
    gridLayoutGroup.startAxis = GridLayoutGroup.Axis.Vertical;
}


Figure 1.1


Good stuff. Now we have this pinned down we can move onto ‘X’ and ‘Y’ spacing which we will cover in ‘Step 3’.
Step 3: - Setting the Image Spacing

When we enter ‘Play’ mode our images appear on screen. However, they look like one long rectangle because we have not added our own image yet. In your project you have have alpha’ed out parts of your image around the sides (or you may not). This will give the illusion of spacing. However you may no have this or you may want more control over the spacing of these images.

Thanks to the ‘GridLayoutGroup’ component this is quite easy to do. You set it once at the start and it takes care of the rest when you Instantiate more images on the fly.

First we need to create two new float variables. One called ‘spacingX’ and the other ‘spacingY’. Then we create a new method called ‘ImageSpacing’ and instead of it having a ‘void’ return type we will use ‘Vector2’ instead. This is because the ‘GridLayoutGroup’ has a spacing variable called ‘spacing’. This is a ‘Vector2’ which is composed of two floats. In this method we want this function to return a type of ‘Vector2’ in order to assign it to the ‘spacing’ variable. To do this we create a temporary ‘Vector2’ called ‘spacingVector2’ and assign it as new ‘Vector2’ properties with it’s ‘X’ and ‘Y’ properties as ‘spacingX’ and ‘spacingY’. After this we return the new ‘spacingVector2’.


Code:

public float spacingX;
public float spacingY;


Vector2 ImageSpacing ()
{

    Vector2 spacingVector2 = new Vector2(spacingX, 

                                         spacingY);
    return spacingVector2;
}



There is one more thing to in this step, and that is to assign returned ‘spacingVector2’ to the ‘spacing’ variable of the ‘gridLayoutGroup’. In the ‘ResetGridLayoutProperties’ method we assign the ‘spacing’ variable the ‘ImageSpacing’ method and out job is done here. You may think this is strange as we are assigning a method to a type, but this is perfectly legal. The return type for the method can’t be void, and must return the same type you are assigning it to (example shown below). As long as you have a return type for the method, and it returns the same type you are assigning it to, everything is fine. It also saves you creating another variable at the top that will take up resources which you will only use once. (See figure 1.2)


Code:

void ResetGridLayoutProperties ()
{
  gridLayoutGroup = GetComponent<GridLayoutGroup>();
  gridLayoutGroup.cellSize = new Vector2(lifeImgWidth,
                                         lifeImgHeight);
  gridLayoutGroup.startAxis = GridLayoutGroup.Axis.Vertical;
  gridLayoutGroup.spacing = ImageSpacing();
}





Figure 1.2


Great, part four is finished. We have sorted out the X and Y spacing between the images and made the images spawn vertically in a row and not horizontally in a descending column.

We have learnt how to:



  • Reformat our code to easily instantiate images
  • Parse types to methods
  • Use parsed integer in a for loop
  • Instantiate an image multiple times with one method reference using a for loop
  • Change GridLayoutGroup starting axis to vertical via code
  • Set GridLayoutGroup spacing via code
  • Make a temporary Vector2 out of two floats and use it as a return type
  • Assign a method to a variable that has the same return type

In Part 5 we will learn how to set a maximum number of UI elements per row and create a new row under the last one if the max is exceeded. Click here for Part 5.
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.

https://db.tt/chdHl92L


Wednesday 17 August 2016

Zelda Style Life System Unity Tutorial – Part 3 of 10

Zelda Style Life System Unity Tutorial – Part 3 of 10



The end result of the full tutorial.

At the end of Part 3 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 2’ 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 4

Part 5

Part 6

Part 7

Part 8

Part 9


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 3 and the completed project file of Part 3.

This tutorial is split up into several parts. In this part we will continue working on completing the foundations:

  • Resize the width and height of the ‘lifeImg’ game object using the variables from Part 1 
  • Lean how to manipulate the RectTransform component variables of the ‘lifeImg’ object 
  • Lean how to use the Group Layout component for multiple UI objects 
  • Effect one variable of multiple UI elements easily using the Group Layout component 
Step 1: - Resizing the image width and height using ‘sizeDelta’

In this step we will learn to work with the RectTransform of the ‘
lifeImg’ prefab. At the moment when the ‘lifeImg’ is instantiated during runtime, it is spawned at a scale factor of one on all three axis. It also has the same width and height as the one in their prefab folder. This is typical and how we normally want prefabs to behave.

However in our case depending the resolution of the player’s screen and how large we want the instantiated object to be we want it to cover a different amount of the screen. The UI image you may want to use may be bigger or smaller or the resolution could be lower and you may want it to maintain visual integrity or prevent the image from looking squashed or over lap another image. Maybe you need it to cover more or less of the screen to make it more visible.

Now in order for us to do this we need to do a few things. We need to access the width and height of the ‘
lifeImg’ RectTransform as Vector2 variables. Then we need to set them to ‘lifeImgWidth’ for the width and ‘lifeImgHeight’ for the height of the image. In order to do this we need to access the ‘rectTransform’ of the ‘Image’ game object, but here is the thing, we cannot directly access the height and width variables of the component through code. This is mostly because it’s stored as a Vector2 variable. We need to set both the width and height at once. In order to set the width and height we set them through accessing the ‘rectTransform’ class of the image object the accessing the ‘sizeDelta’ Vector2 variable of it. When we set the ‘sizeDelta’ to a new Vector2 we set the ‘X’ or axis or width to ‘lifeImgWidth’ and the ‘Y’ axis or height to ‘lifeImgHeight’.


Code:

void Start ()
{
    GetScreenDimentions();
    SetImageSize();
    Image tempImg = Instantiate(lifeImg, 

                                transform.position, 
                                transform.rotation) as Image;
    tempImg.transform.parent = transform;
    tempImg.rectTransform.sizeDelta = new 
Vector2(lifeImgWidth, 
                                                  lifeImgHeight);
}


You might be thinking what is a '
sizeDelta'? Well it is the size the the UI element relative to the distances between it’s anchors. (See figure 1.0)



Figure 1.0

Now we press Play. (See figure 1.1)




Figure 1.1

This is the result. It’s not a bad start. However when we add more ‘
lifeImg’ UI elements to the canvas we need to make sure each one is the correct width and height. Each time we create a new ‘lifeImg’ we could just set the sizeDelta ourselves along with Instantiating it but this would involve more code than required, there is an easier way to it. Now your thinking ‘why haven’t put this in it’s own function yet and put the ‘sizeDelta’ in there?’. We will do this in a later, but at the moment it’s not necessary. Also the method for it will get quite complex later on as we add more functionality and we want our code to be easy to read and do one specific task.

In the next set we will automate this task using the Group Layout component.



Step 2: - Resizing the width and height using the Group Layout component manually

In this step we will cover how to use the Group Layout component to automatically resize would UI image elements.

The Group Layout component is a simple yet powerful component. It can be used to automatically space out UI elements in the editor or Play mode. It can also be used to automatically set size of a new UI element without having to write any extra code each time you create a new UI element. You can set the width and height of an item once in the inspector panel or code when you need to and all children of said object are uniformly resized automatically.

We will cover the rest of it’s functionality later, for now this is all you need to know.

Making sure the LifeSystemManager is highlighted, in the inspector add a GridLayoutGroup component to it. Now if we enter Play mode, not much will happen apart from the ‘lifeImg’ will slightly change position so all of it in view and not out of the screen. There are also about five pixels between the top of it and the top edge of the screen, same thing for the left edge. This can also be done if you change the padding properties manually. (See figure 1.2)



Figure 1.2

With the GridLayoutGroup it normally makes UI elements are with in a UI panel (if there is one). If not it makes sure things are spaced nearly on screen, it’s quite smart.

Note: You can’t change any part of the RectTransform of the child elements the GridLayoutGroup is attached to (It actively greys it out so you physically can’t do it). This is because the GridLayoutGroup takes care of these properties for us automatically, also they adopt the same anchoring as their parent UI element.

We can keep it’s setting more of less as they are for now. What we need to do it look at the ‘Cell Size’ variable. Every item that that is a child of ‘LifeSystemManager’ is treated as a cell. So if we dynamically create a new image as a child, the GridLayoutGroup will automatically take care of all the settings, such as the X and Y pixels (width and height of the cells). If we want to we can take care of this by hand. Go to the inspector and change the ‘Cell Size’ properties to any number to want and press Play. (See figure 1.3)


Figure 1.3. I have set the properties to X: 50, Y:50.



This is OK if everything will stay at a consistent size but we want this to adjust to our screen resolution automatically. For this we need to go back to our code and change some things.



Step 3: - Resizing the Cell Size Dynamically using the Grid Layout Group component

We need to go back to our code and rewrite a few lines. First we need to create a private variable of type ‘
GridLayoutGroup’ and called it ‘gridLayoutGroup’ just under our ‘lifeImg’ variable.


Code:

public Image lifeImg;

private GridLayoutGroup gridLayoutGroup;



We do this to store it locally in case we need to access it’s properties later on.

Then we create a method called ‘ResetGridLayoutProperties’. When we are done we reference it in the Start method just before the Image is instantiate since it will rely on those properties.

In the ‘ResetGridLayoutProperties’ method we first need to assign it the GridLayoutGroup component attached to the ‘gameObject’ using ‘GetComponent’. We do this so it has local access to it’s properties.

Then we access the ‘
cellSize’ property of the gridLayoutGroup variable and assign it a new Vector2 value using the ‘lifeImgWidth’ and ‘lifeImgHeight’ for the ‘X’ and ‘Y’ properties respectively.


Code:

void Start ()
{
  GetScreenDimentions();
  SetImageSize();
  ResetGridLayoutProperties();
  Image tempImg = Instantiate(lifeImg,
                              transform.position, 

                              transform.rotation) as Image;
  tempImg.transform.parent = transform;
}

void ResetGridLayoutProperties()
{
  gridLayoutGroup = GetComponent<GridLayoutGroup>();
  gridLayoutGroup.cellSize = new Vector2(lifeImgWidth, 

                                         lifeImgHeight);
}



Let the code compile and enter play mode and see what happens. (See figure 1.4)




Figure 1.4

If you change the screen resolution the image will change size. If you do not have ‘Maximise On Play’ enabled you can see the values change once on the first frame you enter play mode.

Great, part three is finished. We have laid down even more foundations, made our code a bit easier to read, more modular and made our job for Part 4 easier.

We have learnt how to:

  • Resize an image using the sizeDelta function of the RecTransform component class
  • Use GetComponent to store a Script or MonoBehavior locally
  • Access and set variables of said script via the local variable
  • How the GridLayoutGroup effects the RectTransform of child UI elements
  • Set size of an image automatically sing the GridLayoutGroup component

In Part 4 we will make our code more modular. Not just that. We will learn more about the Grid Layout Group component and how we can use it to:

  • Create a row of UI elements on the screen (our ‘lifeImg’ game object)
  • Create new UI images on demand
  • Destroy the last created UI element on demand

Click here for Part 4.


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.

Wednesday 10 August 2016

Zelda Style Life System Unity Tutorial – Part 2 of 10


Zelda Style Life System Unity Tutorial – Part 2 of 10


The end result of the full tutorial.

At the end of Part 5 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 1’ 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.

http://joseph-easter.blogspot.co.uk/2016/08/zedla-style-life-system-unity-tutorial.html

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 3

Part 4

Part 5

Part 6

Part 7

Part 8

Part 9

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 2 and the completed project file of Part 2.

This tutorial will be split up into several parts. In this part we wIn this part we will work on completing the foundations:

  • Create an image prefab and assign it in the inspector 
  • Instantiate said image as an Image GameObject 
  • Parent it to the ‘lifeSystemManager’ GameObject connected to the Canvas 

Set the RectTransform position if the image to the top left corner of the screen (where the manager is)

Set the size and rescale the instantiate intend image


Step 1:

In the first step we need to do two things. Because we are dealing with the uGUI classes and the Image class we need to include a reference to the name space in the ‘LifeSystem’ class. We do this because other wise the class and compiler will not know what we are talking about and won’t know where to look when referencing the classes and associated functions.

At the top, just below we reference the to the UnityEngine UI namespace code library.

Code:

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



Then we need to add an ‘Image’ variable to reference our uGUI image Prefab. We put this under our float variables from Part 1.


Code:

public Image lifeImg;



Step 2: - Setting Up the Prefab

Next up we need to create our Image Prefab. Create an Image gameObject from the UI section of the ‘Create’ drop down menu. Rename the gameObject to ‘LifeImage’ and create a prefab of it or turn it into a Prefab. Delete the ‘LifeImage’ that is in the Scene view but keep the one in the ‘Project’ view. To keep everything organised in the ‘Assets’ folder create a folder called ‘Prefabs’ and put the ‘LifeImage’ prefab in there. (See figure 1.0)


Figure 1.0

A simple image UI object (left). ‘LifeImage’ prefab located in its folder.

Now you are probably thinking ‘why have we not added an image to this object yet?’. Well because it’s not needed yet and we will stick to the place holder image for the moment. We will add an image to it later on.

With the ‘LifeImage’ prefab in the ‘Prefabs’ folder in the project view, drag it into the ‘LifeImg’ variable of the ‘LifeSystem’ script in the inspector attached to the ‘LifeSystemManager’. Make sure the ‘LifeSystemManager’ game object is selected in the scene view and its components are visible in the inspector.

Step 3: - Instantiate the Image Game Object

The next thing we need to do is create an Image game object at run time in the Start method (we do this for now so it only runs once and to see the results quickly). Because we are creating a UI element we want to create a UI object (specifically an image object) and not a normal game object. This is pretty easy to do. It’s just like Instantiate a normal ‘gameObject’.

When we create a temporary variable in our code for instantiating a game object instead of writing ‘GameObject tempObject’ we would put ‘Image tempName’. Then at the end of the statement instead of casting it as a ‘gameObject’ we cast it as an ‘Image’.

Code:

void Start ()
{
    GetScreenDimentions();
    SetImageSize();
    Image tempImg = Instantiate(lifeImg, 

                                transform.position, 
                                transform.rotation) as Image;
}


Now if we run the code as it is this happens.


Figure 1.1

This is because the object is not a Child of the Canvas and is being treated like a normal game object and created in world space. This is not just because it’s out of the camera view either. It happens because you can only see UI objects when they are a Child of a Canvas, then they are rendered by the Camera in Play mode. This requires a simple fix.

Code:

void Start ()
{
    
GetScreenDimentions();
    SetImageSize();
    Image tempImg = Instantiate(lifeImg, 

                                transform.position, 
                                transform.rotation) as Image;
    tempImg.transform.parent = transform;
}


Now when we let it compile then press Play, we can see it in the Game view. It’s spawned where the LifeSystemManager object is positioned with in the Canvas because we used ‘transform.position’ and the script it attached the this object. (See figure 1.2)



Figure 1.2

The newly instantiated image parented to the ‘LifeSystemmanager’.

Now is is all good because we can see the image on screen in Play mode. However, adjust the screen view port and see what happens to the game object in Play mode. Depending on how you adjust the screen it will either not show up, move to a different part of the screen or now show. This is down to how we have set the anchoring of the ‘RectTransform’. Because we want this to work on most screen sizes and resolutions this simply will not do. This however is easy to fix in the inspector and for now we won’t need to write any code to fix it.

In the ‘Scene’ view make sure the ‘LifeSystemManager’ game object is selected. Go to it’s ‘RectTransform’ in the inspector. Click on the ‘anchor presets’ box to the left of the position variables. Click on the top and left option, then place the game object in the top left corner of the canvas, making sure the object is fully inside the canvas so it’s in view. (See figure 1.3)




Figure 1.3

Anchoring system of the ‘RectTransform’ component.

In your project you may want it positioned differently which is fine, I have done this because most games that use this put thing on the top left, such as Zelda, or they start at the top left depending on the variables they need to show. When we press play, this happens. (See figure 1.4)




Figure 1.4

The image is more or less where we want it to be. We will refine this later, but at least the foundations are being laid down. You may want to change the width and height of the ‘LifeSystemManager’ game object to make it more compact and position it more precisely. I have done this but it is not required, it us up to you if you do.

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

We have learnt:

  • How to create an ‘Image’ variable and reference Unity’s UI library namespace 
  • How to Instantiate an image object at runtime in the same way we would a game object 
  • A UI game object that is not a child of a canvas is not visible in Play mode to the player 
  • How to make the instantiated image a child of the canvas 
  • How to maintain a UI elements position on screen in a canvas regardless of screen resolution

How to manipulate the RectTransform anchoring system to maintain a UI elements visual integrity

In Part 3 we will learn how to resize the image created during runtime using the variables from part one. We will also learn how to use the ‘Grid Layout Group’ component to size our image and how it can make creating and positioning new multiple images easier with little code. Click here for Part 3.
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.