Game Advanced Edition Alpha Type Version 2
GCC Academy
Hi
I am Lee Youngho, in charge of the shooting game development lecture
In this lesson, we will learn
the second version of the Alpha type
First, we will add the score
Second, we will create the UI to display the score on the screen
Finally, we will learn
how to save and load the high score
Adding the Score
The second version of the Alpha type
Let's start off with adding the score
Come to Unity editor
Let's create an object to manage the score
In the Hierarchy, press +
the object responsible for the score
doesn't have to be visible
So, let's create it as an empty GameObject
press Create Empty
Name it as ScoreManager
You can locate it anywhere
Next, we'll attach a script to this ScoreManager
Let's go to our Scripts folder, click + button and select C# Script
Name it as ScoreManager
Let's attach the script we've made
to the ScoreManager gameObject
In the ScoreManager, we will handle tasks like increasing the score
and other related operations right?
Then when does this score increase
and displayed on the screen? When should these tasks be done?
It's when Enemy is destroyed
So let's implement following these concept
So first, let's add the necessary score
and properties and so on
to the ScoreManager all together
First, let's add a necessary property to the ScoreManager
Which is the current score
To display the current score
let's define a variable for the score
We will define it as
public int currentScore
The currentScore will be added to the ScoreManager
Next, when should the currentScore be increased?
Should be when enemy dies
So, when the player collides with an enemy
or when the enemy gets hit by a bullet, the enemy will die
At that point, let's implement the logic
to increase the score
So, for Player
When Bullet and Player collides
This calls a function, when colliding
increase the score
Let's implement this part in this line here
Where is the CurrentScore right now?
It's in ScoreManager
We want to set the final value of currentScore in the ScoreManager
Let's look at the structure of this concept
Let's first see where it is structured
First, what we ultimately want to do is
the currentScore function
If we look at who owns it
it is owned by the ScoreManager component
Let's say it's S.M
So a component named S.M
internally holds currentScore
If it's a circle, scoreManager component
holds this property like this
Then, who owns ScoreManager component?
It's the GameObject that owns
this component and what's the name of the object?
The GameObject is called ScoreManager
This is what owns currentScore as a property
The GameObject that holds the ScoreManager component
is the ScoreManager GameObject itself
So, for the first step, we need this one
can get the second step's ScoreManager component
Only then, in the third step, we can access
this currentScore and that's the logic for this feature
Then, when Enemy collide
What do we need first in order to increase the score?
From the scene, we need to find
ScoreManager GameObject
Then, we can get the ScoreManager component
attached to this GameObject
and thirdly, let's put increase currentScore
We can understand this sequence of events through 1 to 3 steps
So first, from the scene, let's get ScoreManager
ScoreManager to gameObject
I'll put it as smObject like this
And how do we do this
when looking for gameObject in Scene
You can simply use Find function in gameObject
Now, let's find gameObject
that is named ScoreManager
Then, since we found the GameObject
We also can get the component attached to GameObject
The name of this component is scoreManager component
Let's leave it as sm
Where is it? it's attached to sm Object
To get the component in it
you can use the .GetComponent function
to access its properties or functionalities
Get me the desired ScoreManager script
Get me the desired component
using this function, we can get the component
After that, you can increase
the currentScore in sm like this
If we do this, the score will increase by 1, right?
Then, let's check if the score is increasing correctly
by displaying the current score on the screen
Let's put "Current Score" in the print
and try printing sm.currentScore like this
Save and back to Unity
and click play button
So bang bang bang!
That should be enough
Let's check the printed output
Go to the console window and we have score of 1, 2, 3, 4, 5, 6
We can see that the score is increasing by 1
This way, we've tried increasing the current score
Creating the Score UI
Now that we've created the current score
let's create a UI to display
this score to the user
How are we going to build this?
If this is the game screen, on the top left
Let's create a structure that will
display the current score here
whenever Player destroys Enemy
Each time Player defeats Enemy
score will be displayed here
Back to Unity again
To display UI in Unity
Expand Hierarchy using the + button
Click the + button and open it
There's UI in the drop down list
In the UI, you will see Text
and if you expand it, there's TextMeshPro
So there's TextMeshPro
And in the Legacy, choose Text
It doesn't matter which one we use for now
However, recently Unity recommends
using TextMeshPro
Whether it's 2D or 3D, TextMeshPro is recommended
because it offers better performance
and handles resolution more efficiently
So, this time, we will use TextMeshPro
Click on it, we can see TMP, which stands for TextMeshPro
and also an Importer will appear
We see a new button
called Import TMP Essentials here
So you get this window
Let's click Import TMP Essentials
Once you click it, it imports something right? Close this window now
In the project window,
We can see a new folder created
called TextMeshPro here
What is the structure of UI in Unity?
Let's talk about it more
In Unity, the UI elements are attached
under a GameObject
called Canvas by default
and you know there is some kind of button for events
that triggers inside the UI
for us to handle such events, an object
called the Event System
is additionally and automatically created
We have simply created a button here
or in this case, we have created a TextMeshPro text
When we create UI elements
to handle events occurring
in first canvas and in Event System
these objects automatically added
So they are registered as child objects under the Canvas
to display the entire UI on the screen
the Canvas is automatically created
with a size that corresponds to the resolution
which refers to the current monitor's resolution
It refers to the resolution of the smartphone's display
So, for example, if there is
a resolution size like 1024 or 2048
These numbers represent the resolution size
when moving objects in Unity
The units corresponds to meters
But this unit, 1024, 2048
that we use in this Canvas, UI
we use pixel units
A pixel is a small dot that determines
the color displayed on the screen
Let's draw a square, these dots
Each of these dot squares has three elements
R, G, and B, which represent the color values
This single square is what we call a pixel
How many of these are there?
If 1024 pixels are arranged horizontally, that represents the pixel grid
which can be considered as the coordinate system on the screen
The space where objects are placed in Unity is
the world space
Screen and World are different
in a virtual world, we use meters
The units used for displaying objects on the screen's UI
are based on resolution pixel units
So, the size of the Canvas is adjusted
to match the actual screen size
So, since we are trying to display both the meter based
units for objects in the virtual space and the pixel based
screen coordinates for the Canvas
at the same time in the scene view
Our player is present right here
when the Canvas elements are added
If you look at this in details
There is a white line drawn
What is this?
Let's double click on it to view it
Let's double click the Canvas to view it
Then how is it?
As you can see, there is a rectangular border
drawn around the outer edge
What is the size of this?
If we go to the right and check the Canvas
we can see that the Rect Transform is set like this
The width is set to 640, and the height is set to 960
It is set to match
the resolution size we defined for the shooting game
But since it's in pixel units rather than meter units
this screen is displayed using meter units
Since Player is meter based
It's set to this size
Since we are trying to display it in a single space
How is it for Canvas?
It will be displayed in the scene as if it were 960 meters in size
But in reality, the unit it uses is what?
It is in pixel units, specifically 640 here
Then the Text here
is also in pixel based units
If you look in the bottom left here
we can see it in the game window
These can also be grabbed and moved
allowing us to position and display them
if we were to add a Button
it will also appear in the UI just like the TextMeshPro
we can add it the same way
the structure where we add elements one by one, like this
For this project, we don't need the button
Let's remove it by pressing Delete button
So, UI elements are attached under the Canvas, like this
Because these elements correspond to the screen size, they are placed accordingly
And if you look at some of the other objects
like ScoreManager
or Background
There is a Transform component attached
So, the position, rotation, and size values are set like this
If we look at Canvas first
Which UI related elements are attached?
It's the RectTransform that's attached
Why? simply because Transform uses meter based units value
This one uses pixel-based units
Additionally, since various options need to be used for this
these UI elements have
a special RectTransform attached
And these are all attached as children
to be handled like this
There, we added a Text UI element
to the TextMeshPro
Now, let's arrange it
I will place it in the top-left corner like this
Currently, I've placed it here
and in this state, it's located here
If we stretch it horizontally like this
it will stay in the same position
What happens if we change the resolution like this?
The element that was here earlier will move to an unexpected position
Also the size will change
4K, then it gets too small
The position changes, and it behaves like this
If we switch it back to shooting, it will look like this
So, when objects change their position
according to resolution
Displaying UI becomes difficult
If we select the Text UI and go to the right
we can see an option called Anchor Preset
If we click this one
we need to determine the reference point for alignment
So we will select
Top left
because we want left and top to overlap
Click this to select
and then select its position at the top left
changing it to top left
we can move it like this
or position it with W on keyboard
I will place it at the top left like this
This one is aligned
to Left and Top like this
Now, I'll change the size to something else
and see how it looks
Then, how is it? it became strange
when we used a different value and why?
What's the resolution for this now?
This is the resolution, the whole thing in this window
It's definitely positioned at Left Top continuously
No matter how much we change the resolution or switch to Free Aspect
even when the screen is set to full screen
it will always stay in the top left position that we specified
It will stay in the exact same top left position
How about background? Depending on the resolution
the background will need to be automatically adjusted
or it would be necessary to create
the texture at the required size from the beginning
Anyway, we've arranged the UI like this
Let's rename it to CurrentScore
So, change it to CurrentScore
The default width and height are set like this, right?
The default size was
probably 200 by 50
So, we'll adjust the Width and Height to match like this
and then go to bottom
The text content is
New Text right now
Let's change it to current score
Current Score
and then put a space, followed by a colon
I will make sure it is displayed correctly
And if the text exceeds the space
it will be displayed with a line break on the screen
So we need to set it
so it doesn't do a line break
and keep the text to be added horizontally
How can we do that?
If you scroll down a bit
you'll see the Wrapping Mode
Wrapping means to cover
This means that when it reaches the end of the size
it will move to the next line
So, if you set the Wrapping to Disabled
the text will continue horizontally like this
This works thanks to this property called Overflow
What is Over? having more than enough, Flow, to run
it means that even if the text exceeds this size
it will still be fully displayed
put Wrapping to Disabled
You can display it like this
For the current score, I'll exclude this and take a look
The font size is currently set to 36
Oh, but there's an issue here
Korean text is not being processed here
English texts or numbers were fine
There is an issue for Korean text
So, let's try to solve this issue
Please open the Explorer
We will search in the Explorer, but where should we go?
Go to C drive first
into the Windows folder
After you find windows folder
there is Fonts folder
Windows folder and to Fonts folder
Press F in your keyboard then it will take you to Fonts
and then in there, search for Fonts
on the top, there is a search box
let's look for Nanum Gothic
There we can access the font we want
Nanum Gothic is displayed here
So, what you can do here is
you can set detailed view mode
Anyways, we have Nanum Gothic here
We will drag it and drop it in a proper place
Where should we do that? Remember TextMeshPro?
Under this folder, we can find Fonts folder
Let's place the font we wanted
From the file explorer, drag and drop Nanum Gothic
into the fonts folder
If you have a different font you want to use
You can freely use that font
I am just going to use Nanum Gothic
So that's that
Then, it's loading right now
It's because this folder is using import feature
So this part will take some time
When the loading finishes
we can see that the font is added into the program
From Bold font
Everything is added here
Once Nanum Gothic is added
we need to apply it
to the font we intend to use
To do that, we need to add some content
provided by TextMeshPro
There is something called TextMeshPro in Window
There, we can find Font Asset Creator
Even if it's registered like this
you need to recreate it
using the font provided by TextMeshPro
So, click on the Font Asset Creator
and the Font Asset Creator window will open
To here, the font we found
Nanum Gothic, drag and drop it
to this Font part here
called Source Font File
Make sure to drag and drop it like this
For the size, use Auto Sizing
Let's use other values as default
Padding 5
Packing Method at Fast
For Atlas Resolution
Your default must be different
But I will make it bigger by 4096x4096
The Atlas is an image that combines all the individual images
placing them all into a single texture of size 4096
into one large image
Make sure your values match mine
and in the bottom of the list, we have Generate Font Atlas button
Let's click it
Then, the process of packing is included
If you can see that Generation completed
In the bottom of the window, there is Save button
And the images or textures created here will also appear
Let's click the save button
Then, a dialog box will appear
asking where to save this file
Save this
Let's just save wherever it directs us to by default
In here, NANUMGOTHIC.SDF
Like this, we completed our creation
Now, select this NANUMGOTHIC.SDF
and go to inspector window on the right
If you look here, in the part below a property called Generation Settings
You can see Atlas Population Mode
This Atlas Population Mode is
set to Static right now
Let's switch it to Dynamic
And let's change
Sampling point size to 60
Then an Apply button appears in the below
Atlas Population Mode to Dynamic
and Sampling Point size
to 60, like this, we've made the change
Click the Apply button
Close this window
Now, delete this in currentScore
Even if I write in Korean...wait
We haven't changed the Font Asset to one we wanted to use
Now, let's drag and drop this
into the Font Asset section
If you grab it to drag and drop here like this
ABCD, it works fine now
So in this part, we can finally
write in the text of Current Score
By adding a working font
we've managed to fix broken font
Up to this point, we've added the font to TextMeshPro and applied it
ensuring that Korean characters are now displayed correctly
Now, let's work on setting up the currentScore so that the current score
can be registered and displayed properly
Let's go to ScoreManager in Visual Studio
Bring TexMnshPro UP here
we need to change the Text value here,whever?
TextMeshPro under CurrntScore
What is this GameOject?
The one that says TextMeshPro with UI, right?
We need to change the sentence written in this box
So we need to be able to bring
this component and fix the value inside that component
So how do we do this? In here
Let's register the current score UI
Its name is public TextMeshProUGUI
I will name it curScoreText
As you can see, in Visual Studio, the namespaces TMP and TMPro
are automatically included to use it
Make sure to check if this one is registered under using
If this isn't done properly, an error can occur
So, please check this here
If there's no problem, in this UI
put current value
into the value this function has
So, where is the current score being set?
Let's move on to the Enemy
In Enemy.cs, in the OnCollisionEnter
we were simply displaying
the current score to the console
This part here
Let's change it to display the current score in the UI instead
The curScoreText
remember that we just added is in the sm
Let's change the current score value
in this TextMeshPro
So, how do I access the current score value?
there is something called dot text
Here, text starts with a lowercase letter
So, with the current score set like this
what is the value?
If you set it as sm.CurrentScore
it will apply this value to the UI
Let's save it now
Back to Unity
go to ScoreManager
and select GameObject
We would add in the Cur Score Text
To maintain TextMeshPro
if you drag and drop currentScore here
it will be added like this
Save it now, and play again
So, current score
we can see it changing for each enemy destroyed
So, right now, since the current score
isn't displayed at the start, it's a bit lacking
Let's add a 0 or some number after
he current score here to display it
Starting from 0 points, and when you play again
you should be able to confirm that the score is displayed correctly
Saving and Loading the High Score
So, this time, let's display the high score
and after saving it
load it back on
Just like we displayed the current score here
we'll display the high score
in the same way right here
To display the high score here, we first need to have the high score itself
Let's first declare a property in the ScoreManager
to display the high score
So, let's move over to Visual Studio
find our way to ScoreManager
To display the high score here
I shall copy this and place it like this?
This will be bestScore instead of currentScore
add a new function like this
Not current, but best
Like so
We need an UI as well
So, let's copy both
and place them here
This one shall be the Best Score UI, right?
And then, change this curScore
into bestScoreText
Now, both the high score and the current score
are set up and displayed
To make it look better
let's move it up and place it like this
currentScore, bestScore
To display both scores
we've set up bestScore to remember the score
the UI to show the high score
and so on
So, when should the high score be updated?
The currentScore should increase
when the Enemy is caught, right?
Then how about the bestScore?
The high score should be updated like this
The condition for bestScore updates?
What should the first one be?
If the current score exceeds the high score
If the current score is equal to or less, we don't need it to be updated
should be updated only
when current score exceeds the best score
what are we going to do then?
Update the best score
and then? simply display the best score in the UI
So, the high score should be updated
when the current score exceeds the high score
At that point, the high score should be updated
and displayed on the UI, this is the way how it should work
Where should this logic go?
It should compare the current score with the high score
when the enemy increases the current score
I will add this logic below
where the current score is displayed
Press Ctrl and V to paste it
Then, how can we tell when current score
exceeds the best score at all?
use function if, what's the current score?
sm's currentScores
How bigger is it than which? Than the best score?
If it is greater than sm's bestScore
this is the simple translation What would you like to do at this point?
These are what we want
Let's update the high score, but what value should we use to update it?
With the current right?
So, sm's bestScore should be updated
with sm's currentScore like this
Next, just like we displayed the current score
we will also display the bestScore in the same way
assign the high score to bestScoreText
instead of the current score, like this
Then, let's assign currentScore and bestScore accordingly
So, update bestScore vlaue
Next, let's process this value so that
We will handle this value accordingly
Save it and back to Unity
Now what do we need to
We need an UI elemnet in order
Let me select currentScore
and copy it by using Ctrl and D
Let's rename it bestScore
like this, bestScore
set it to display the bestScore instead of the current score
the position overlaps, so I will grab this and
place it slightly in the lower position
Then you can see that a current score and the best score
are placed nicely like this
Now, let's drag and drop bestScore
inside the ScoreManager's bestScoreText
Now, let's save the changes and run the game by clicking Play
Now, you can see that the current score and the high score
are being updated correctly
Then, ultimately, what we want to do is
not only just displaying current score
but also save and load the program back on
So now, let's take a look at the topic of saving
In Unity, in order to save
there's a library that they provide
You can save in various ways depending on your purpose
We can save it in different file formats
json or other binary files
In addition, you can save text files in various formats
such as XML and others
But here, let's use the PlayerPrefs object
which helps easily save data in Unity
There is no need to save
the current score here
I will make it so that only your highest score is recorded, like a ranking
So, let's add the functionality to save the highest score
below the section where we record the highest score
So, what we're doing here
is using something called PlayerPrefs
if put a dot after PlayerPrefs
Setint, which is integer value
Since the score is an integer, it is currently represented as an int value
So that's that
This one is supposed to be a key, Value
Here, you just need to put the key
that will be used to save or load the data
So here, we will put the key value
Best Score in a string written in the text
Next, we will assign the corresponding value
which is sm's bestScore, to it
Then, the value has been saved
Then, this value needs to be loaded from somewhere
We need to load it and display it on the UI
To display this value at the beginning
we will load the bestScore value
in the Start part of the ScoreManager
in the beginning, bring bestScore
and show it on the UI
So, what we want to do here is
load the highest score, assign it to the bestScore variable
Let's say I want to display it on the screen like this
So what do we do first?
We will need to call the bestScore
and then display it on the UI as well
to load the highest score
We need to first retrieve it and store it in the bestScore variable
because we are about to declare
Starting with bestScore, Playerprefs
It was set int back then
This time, there should be GetInt
So, here you will use Best Score with a space in between, like this, Best Score
For key value, the name should be the same
If you have nothing to load after this
nothing at all, just put 0 for default value
Like this, add in the default setting
If you have nothing saved
the Best Score will be 0
If it exists, we will load the saved value and assign it
Shall we display this value in the UI?
You can then assign the bestScore
to the bestScoreText like this
Then save this part
Let's check if it works well
Go to Unity to play
Current best score, we already have 17 points
I played till 20 points
17 points match was what I tested, which is the best score
Let's run it to check again
Now we see that the best score has been saved with 20 points
We could see that loading works
But now that I see it, the value is fine
but there's a bit of a burden in the Enemy section
In reality, it's directly manipulating the value of a specific property
and others are modifying that value
These kinds of things are actually not ideal
in object oriented design
So instead of directly manipulating these values
it's more efficient to handle them through encapsulation
by creating Get and Set functions to manage the process
So, instead of directly accessing bestScore or setScore
and manipulating currentScore directly
we will access them through functions
So, you will create Get and Set functions
for currentScore here
First, let's create the Set function
I will name it public void SetScore
and pass int value like this
Next, I will create it
This time, I will create a function called GetScore
a function for returning the currentScore
It will simply return the currentScore
just like this
If this is done what is the Enemy
doing directly now?
By bringing in the ScoreManager, increasing the value
displaying it on the UI, and updating the highest score
all of these tasks don't really need to be done by the Enemy
The ScoreManager can handle all of these, instead
So, there is this part where the current score is being increased
Let's change this part to use Set instead
So this part right here
Let's cut out everything from displaying
the current score up to the point
where the current score is increased
including steps 3 through the last part
You will press Ctrl and X to cut it out
So, where should you place it?
You will place it in SetScore
You will place it in SetScore within the ScoreManager
After that, you will remove
all the references related to sm
Why? It's the ScoreManager, so it's itself
Therefore, this one doesn't require sm
A tip here is to select it by dragging
then press Ctrl and F
and the search function will appear
If you open the dropdown here
you will see the option to replace as well
If you click on Next Find on the right
which is Alt + R, and what happens?
with sm
we can change it to blank without any text
Once you do this, you'll have successfully removed all instances of sm at once
Exactly! You’ve moved everything into SetScore
So, what should be done in the Enemy section?
In the Enemy section, using the Get and Set functions
this is how it works
You're saying that we should use the Get or Set functions
of the ScoreManager to set the value
We can just increase the value by one
So, to increase the value with SetScore
in ScoreManager, you first need to have the value
So, by getting the value from GetScore
you can increment it by one and then pass it to SetScore
which will set the new value in the current score
So we should handle it this way and how does it differ?
Enemy became a little lighter
We will save it as is now
Let's save it and then move to Unity
and click play button
Current score updates normally
We can see the best score updates fine as well
Let's save it and turn it off and on again to see if it actually works for sure
the best score is being loaded correctly
So, up to this point, we've covered
the entire content of Alpha Type Part 1 and Part 2
We have completed the Alpha Type content
including saving and loading the values
Next time, we will look into the Beta Type content
Let's summarize the content we learned in this chapter
First, we created the ScoreManager
to add the score and display it on the UI
Next, we learned how to use the PlayerPrefs class
to save and load the highest score
At this point, we learned how to use encapsulation in object oriented programming
by accessing properties through Get and Set functions
Nice work everyone!
Add score Create score management object Click the [+] button in the hierarchy to create an Empty object and rename it to Score Manager
Create a C# script in the [Assets - Scripts] path of the project window and rename it to Score Manager
Add the ScoreManager script to the ScoreManager game object
Displaying Current Score In Enemy.cs, with the logic in the OnCollisionEnter event function, when the enemy collides with another object, adds score
Flow for Implementing Score Increase Find the ScoreManager object in the scene Get the ScoreManager component Increase and display the current score
Create Score UI Create the current score UI and set the anchor Click the [+] button in the hierarchy window and select [UI - Text - TextMeshPro]
When the TMP Import window appears, click the Import TMP Essentials button, and then rename the object to CurrentScore
Select the Text (TMP) object, click the Anchor Preset in the Rect Transform of the Inspector window, and choose the top left alignment Text Component
After modifying the Text component value of the CurrentScore object, enter 'Current Score:' in the Text Input field In Explorer, go to the C:WindowsFonts folder,
search for Nanum Gothic font, select it, and add it to the TextMesh Pro - Fonts folder After opening [Window - TextMeshPro - Font Asset Creator],
add Nanum Gothic font by dragging and dropping it to Source Font File and set Atlas Resolution to 4096 Click the [Generate Font Atlas] button,
save it after completion, and when the file is created, select NANUGOTHIC.SDF and move it to the Inspector window Go to the Generation Settings category
Change the Atlas Population Mode to Dynamic set the Sampling Point Size to 60, click the Apply button Afterward, check for correct text display
Create Score UI Writing Script Register a member variable of type TextMeshProUGUI called curScoreText in ScoreManager.cs
Add the code to display the current score UI in the OnCollisionEnter function of Enemy.cs
Assign CurrentScore to CurScoreText in the ScoreManager game object in the hierarchy
Save and Load best Score Display best Score In ScoreManager.cs, declare a UI for displaying the best score and a variable to store the best score
implementing flow for the best score current score exceeds the best score updating the best score displaying best score on UI
Implementing best score update requirements Select CurrentScore, duplicate it, rename it to BestScore, and then change the Text Input to best scores
Place the BestScore object below the CurrentScore in the hierarchy Select the ScoreManager game object, assign BestScore to the Best Score Text field
Save and Load best Score Using PlayerPrefs for saving PlayerPrefs.GetInt will return the saved value when you provide the key
Without saved value, can specify the default value The PlayerPrefs stores objects in a key and value format Load and display in the start function
Encapsulation Since the score increment logic is in the Enemy properties, we will handle it by creating Get and Set functions through encapsulation
Create SetScore and GetScore functions in ScoreManager.cs Move the current score from Enemy.cs to the SetScore function in ScoreManager.cs
Since the content from the Enemy class has been moved to ScoreManager, remove the code where it was previously referenced using the sm variable
Modify Enemy.cs to call the Get and Set functions