Game Advanced Beta Type Version
GCC Academy
Hello
I am Lee Youngho, in charge of the shooting game development lecture
In this chapter, we will learn the Beta Type version
First, we will learn how to create a manager using the Singleton design pattern
We will understand design patterns and learn how to create
and use the Singleton pattern for score management
Next, we will learn about memory management using objectPool
We will implement objectPooling
using array and list data structures
Creating a Manager Using the Singleton Design Pattern
Let's learn how to create a manager
using the Singleton pattern
The pattern you see here refers to a design pattern
which is essentially a way of structuring or designing the system
Singleton is like this
Singleton literally means something singular
When using these single instances
C# provides grammatical elements in the pattern
A key grammatical element used in the Singleton pattern
is static
Like this
Let's draw a circle
We call it a class or an instance
Anyway, these are objects
Right? these are objects
Object A
Object B
Each of them has its own member variables
properties and features like this
properties and features are going to be here like this
The variables inside this function
are called local variables
The variables that are inside the class but outside the functions are called member variables
and from the class's perspective, they are considered global
From its perspective, these are considered global
Why? Because it can access them as well
But this one, called member variables
Let's say each object is like a school
Jane is here
John is here
For each object
In school, when they want to share information with everyone
what do they use?
They utilize a white board
white board right?
On the board, detention
John
we can write like this
then who would've written this?
Of course, someone must have written this
for contents to be registered on the board
The area where everyone has access to
is what we call the Static area
So this is a static area
In this area
we could have a detention list
like this
the important part is actually, who wrote this
Someone must've written it to be there
When the teacher comes in and asks
who wrote this?
So, they place the reference
Who wrote this? Jane wrote it
Right here, we specify Jane wrote this
Attach Jane as a reference
Jane. reference, like this
Jane is the name of the class
We can use the class name this here
But then she writes again
under Detention list, John, Jane wrote the same thing again
If they wrote the same thing over and over
the teacher can
be angry with the kids
So, in here
Just like I mentioned
Can there be two variables of the same name?
There shouldn't be
Since this is a storage, data area
you can use static to register data here
with static attached
and utilize it
of course we can add functions
With one reference
We can only register one variable
This is what static area is all about
This design method is
called the Singleton pattern
We can use this method
to make our ScoreManager
There are several ways of making
different singleton patterns
Here, we will create and use it
in a simplified form
But the content is the same
Open ScoreManager.cs
Open it from the visual studio
I will go here
GetScore
below that
and right above Start function
let me declare
Here, we will create a Singleton object
public static here
attach static over here
grammatically
and then, with whose name?
with this ScoreManager's name
it can be registered as a reference
for the variable that comes after
So, we first write the data type after this
We will register ScoreManager here
since the name is a variable
Typically, Singleton objects are written in uppercase
Instance like this
Let's leave it without any value
Then, a singleton object
gets registered right here
With the name of Instance
The data type will be registered as ScoreManager
How can we make it?
ScoreManager.Instance
Like this
then we don't have when
There's no value to it
when are we going to specify the value here?
What kind of value goes in there?
When you press the play button
the ScoreManager object will be
loaded into memory
meaning it will be created
and it is instance
When it's being created
we can have the value in here
then we can use Start function
it can be good if we do that
Start and similar ones
are life cycle function right?
Life cycle functions
go through a life of getting created and removed
These also have the point of view
First, we have Jane here
the number one
this one is created first
and then the number two is created next
when that happens, number two wants to know about number one
So, it uses number one dot, name dot and so on
accessing will face no problems
but what's the problem? this could happen
If it's trying to access the second instance
before it has been created
it would cause a problem because the instance doesn't exist yet
So, would it be a problem or no?
Of course it will be, because it doesn't exist yet
It just doesn't exist
So, it is required to be assumed
that they must exist together
For that
Which life cycle function should we use?
There's a function called Awake
This Awake function, when we press play
we have in our world
the ones that are registered
ones that are already added into the hierarchy
In those cases
the Awake function is called first
That's why this one is already created
it's not born yet, only been conceived
Then, those objects will call
all of their Awake functions first
and handle their initialization
after all the Awake functions
of the objects are called and process is complete
Then, the OnEnabled function
an event function is called next
After this one
Only after that, the Start function
will be called for all the objects collectively
In other words, if we set the
ScoreManager's Instance value
in the Awake function, the value will already be assigned
so there won't be any problems
So, we will create an Awake function
and set the value there
Right above Start function
I will make an Awake function like this
In here, when the Instance has to no
If, Instance has no value, null
What are we going to do then?
By using this, you are referring
to the instance of the object itself
The Instance object refers to the ScoreManager object
This Instance Object
Itself wants to be in there
So, in the world, who is it?
It is the ScoreManager object itself
being assigned to the Instance variable
How do we access it?
Use ScoreManager
Then how do we want
others to access me?
When you access it here
we are referring
to myself by using Jane.me
We can access and use the Instance object here
as much as we want
Now, let's take a quick look at how
this makes things much more convenient
Go into Enemy.cs
To get the value for this one
Find the GameObject first
There was the inconvenience of having
to retrieve the ScoreManager component
Now, this part is no longer necessary
Need no more
Instead of using sm, you can directly use ScoreManager.Instance
to access the ScoreManager instance
Just like so
Because it's directly the ScoreManager instance
we can directly assign it here like this
By doing this, we can access and use it conveniently
without having to find other objects
This is the advantage of utilizing singleton pattern
Save it and go back to Unity
To press play button
the normal increase of the current score when running the game has been confirmed
You can see that the current score is
increasing correctly
But if we only use it this way
it can feel a bit inconvenient
Why?
It’s quite difficult to interpret here, right?
Let's say you guys just came back after a few days of sleep
If you looked at this code after about ten days
you'd have to deal with the issue of interpreting it
What is this all about?
It means that the readability is low
To improve the readability of this part, we can make adjustments
Let's try using the Get Set properties
provided by the C# language to improve this
Let's move to ScoreManager.cs
By using Get/Set Property form C#
Let's create the ones
that are under GetScore
and SetScore
Get set Property's grammar is very simple
Look at it this way
Do you know what Sphinx is?
It has human head
But what body does it have?
It has a body of a lion, some kind of man beast
It seems like the Sphinx might be strange, but it's that kind of feeling
What is this?
Two different species are combined together
In this way, different types of forms have been combined
So, you can think of the Get/Set properties
in a similar way
The head part is the variable being used, so it functions like a variable
The head part is the variable
and the body part
the body part is the function
This is how the Get/Set properties are used
As we use ScoreManager
Let's look into it in details
Shall we first create a SetProperty
to make the Get/Set properties below?
Let's create GetProperty and SetProperty like this
We will add public to allow external access
set the type to int, and name it Score
You will access it using the name Score like this
How is it? Up to this point, it just looks like a variable
but now the body part has been added
This is the characteristic of Get/Set properties
By adding Get/Set properties here
we can create a function like GetScore
Just like creating a Get function
The role it does
We will take the role from here and place it here
Then, it will be able to perform exactly the same role as the GetScore function
The content is implemented like a function
Now, let's add the SetProperty
The Set works the same way as SetScore
So, you can take the part from SetScore
select everything
Let's cut it and place it elsewhere
Ctrl and x and place it inside the Set function
We have transformed the function into a Get Set property
Why? This is done for usability and readability
So, how do you use it?
You can use it like a variable
If you use it like a variable, you want to get the value
Usually, when getting the value, how do we do it?
In the form on the right, we have the assignment operator
When you have int a, let's say we assign 10 to it
Then call this assignment operator
Right? assignment operator
Then what do we call this part on the right?
Usually we refer to it as R value, the value on the right side of the equation
So, when we have this
We call Get right away
Get, right?
How about on the left side?
We are inserting the value, making it Set
Did you understand? L value for this one
Sending the value on the right side to the left
Get for this side, and Set for the other side
Did you understand?
When you want to get a certain value
For this value, use Get
and for the other, use Set
And for this one here
use it like it's a variable
Let's take a look at how to use it
Since the Get and Set functions
will be replaced with Get/Set properties
we will go ahead and delete them
Now, we will use Score
and move on to Enemy.cs
Things are made complex like this, right?
How can we handle this part?
We can make it a single line like this
In ScoreManager, the Instance part remains the same
Getting the Singleton object is the same
But here, which do we use?
the Score variable is a Get/Set property
It looks like a variable, but its body is a function
We want to increase the value here
like this
Then we are increasing the value
How are we supposed to use this?
Actually it is set up like this
this code essentially retrieves the current value of Score
and then adds 1 to it
You can do this to increase by 1
Originally, this is implemented in a way
that allows it to be shortened
So, as you can see, here it is getting the value
which is the Get
Then, when we add the two values and put the result back here
it becomes Set
Did you understand?
How is the readability between the one above
and the one below, given how simple it is?
At once, you can know that
this one increases the score
This one is somewhat complicated
So, with this part
we will upgrade it
Save it
When we save it inside here
and actually have the screen updated
in details
check every bit of this saves properly
Let's play it now
Right now, I see the current score and the high score are being updated
I will turn it off and then turn it back on
We can see that the save is properly done
By applying the Singleton design pattern
and implementing the Get/Set properties
you can create a solution that allows you to implement
this content in such a simple way
Memory Management Using an objectPool
Then, this time, let's learn
how to utilize an objectPool
for memory management
An objectPool is
a technique where, as I mentioned before
when shooting bullets, you take the bullets from the magazine
and use them
There will be two places where the objectPool is used
The first place is where the bullets are created
The bullets are being created where?
in the PlayerFire.cs file
a bullet is fired whenever it's needed
So, here, let's create a magazine where
we can use the objectPool
Let's first name this the objectPool Magazine
Then, let's add the number of bullets to be placed in the magazine here
Next, let's create an objectPool array
corresponding to this magazine
Then, this will be the size of the array
So, let's add public int poolSize for that
The name will be poolSize, and the value will be set to 10
That means the magazine will hold 10 bullets
The size of this array will be the value you set
So, you will add GameObject
and then insert the array like this
And then, you will name it
bulletObjectPool like this
So, when do we add bullets to this magazine?
At the start, we create all the bullets at once
Then, when we fire bullets during the game
we can take them from the magazine and shoot them
So, when it is created, to the objectPool
which is the magazine
it will generate and add one bullet at a time
Let's go ahead and implement this
The first step is when it is created, so we will do it in the Start function
And ultimately, what we want to do is
add bullets to the magazine
want to add bullets to the objectPool
This is our ultimate goal
Then, the second step is to generate the bullets in the bullet factory
And how many bullets should be generated in the bullet factory?
We need to generate 10 of them in total
So, you need to loop to create as many bullets as the number of bullets according to magazine capacity
So, what do we do?
You need to first create the magazine
with a size that can hold the bullets
Then, if you do it in order, it will work like this
Create the magazine with the appropriate size
Then loop through the size of the magazine
and create one bullet at a time
What do you do? add it to the magazine
So, first, I'll create the size for the magazine here
So, you will create an array by adding
new GameObject to bulletObjectPool
The size of the array will be equal to poolSize
Then, for the second step
you need to loop and repeat the process
Repeating means using a loop, a for loop would be appropriate
int i equal to 0 how many i?
so the loop will run timed by poolSize
i++
Each time, you can create one bullet
and add it to the objectPool
Where do we create the bullets?
Currently, when the fire button is pressed
bullets are created and placed like this
that's what we've done so far
Do you know the part where the bullet is created?
You can press Ctrl and C to copy it
and then paste it here
Then, this part becomes the bullet creation in the bullet factory
Next, you want to add the bullet to the objectPool
That means you want to add it to the array
So at bulletObjectPool
Which order? at i-th position
let's add the created bullet to the pool like this
After saving it, when you go and press the play button, right?
Then, all the bullets will fire at once
How is it? they all releases at once
This is not what we want
So, instead of automatically flying out at the start
we want to disable the bullet
by setting its SetActive to false
This will deactivate the bullet
preventing it from moving or performing any actions
Let's go and see what happens
Click play button
Then, you can see that a total of 10 bullets
have been created from here to here
and It is disabled
So, when we press the fire button
we can check each one
and make it fly one by one
Now, let's go ahead and implement
the part where we fire the bullet
Until now, when the user presses the fire button
we created a bullet and set its position
Now, how should this part be modified?
Exactly, now we need to take a bullet from the magazine and fire it
However, when we take a bullet from the magazine and fire it
you'll see that we fired one bullet
This is the screen, and we fired one bullet here
So, it is flying now
It's flying, and then you fired another bullet
Should it fly again? No
Exactly, we shouldn't suddenly take it and shoot it again from here
At the very least, we need to wait until the bullet leaves the screen
before we can take it back and fire it again
or perform similar tasks
In other words, we shouldn't reuse a bullet
that is already activated and flying
the deactivated ones
We need to make sure that only the deactivated ones
are taken and fired from here
If all the bullets are already created
meaning there are 10 bullets
and they're already prepared
you won't be able to shoot if there are no more available bullets
In that case, you would need to increase the size of the magazine or perform a similar task
Fortunately, after shooting all the bullets
this one bullet went outside, and we deactivated it
Then what happens?
Now, we will be able to fire this bullet again
when firing a bullet from the magazine
the first condition that it should be deactivated and ready to be reused
You must fire a deactivated bullet
This means that we are activating the bullet again
Let's go ahead and implement this
When taking a bullet from the magazine to fire
it should be a deactivated bullet
Then, we need to loop through the entire magazine
Start with for function
int i = 0
Since it's the same as above, let's copy it
if we copy it inside this function, it will work as expected
let's try adding it up to this point for now
this should also be inside the for loop
but we can do that later
let's see what happens
First, we need to figure out if it's deactivated bullet or not
How do we do it?
if i-th bullet in the ObjectPool
if bulletObjectPool
the i-th bullet
if it's deactivated, let's process it
let's place it in a way that's easier to understand first
Let's first take one bullet
So, we can place it as GameObject bullet
and then position it like this
Let's move it into the for loop as well
When this is done
for deactivated bullet
you can check if the bullet is currently activated
or deactivated by asking it
Bullet, dot give me activeSelf
This will tell us whether it is
currently active or inactive
If it is true, it means the bullet is activated
and if it is false, it means the bullet is deactivated
What is this value?
since it is deactivated, it will be false
What do we want to do here?
What we want to do is this part
after firing, placing the bullet at the muzzle's position
First, let's place the bullet at the muzzle's position
So, you set the bullet's position and then activate it
Bullet.SetActive then true
It would be activated then
So, now we've already fired
Do we have to continue to figure out?
You need to shoot only once
We can use break to exit the for loop
Let's save it see what happens
As you can see, initially, the bullets are all
in a deactivated state when they start
Let me fire one
after bang bang bang, suddenly
there are only 7 bullets left
It seems that an error has occurred, as seen in the console window
Why does this happen, MissingReferenceException?
Let's double click it to expand it
This part is where an error occurred
What this means is that when we collide with the enemy
we're handling the situation where both
the enemy and the player die
In other words, we made them disappear from the world
they were completely removed
But when I try to use it, it says
It's not there
Do you understand the point of the issue?
Then, when it collides with an enemy
or when it collides with the DestryZone
If it's a bullet, what should we do?
We would need to add it back to the magazine
In the Enemy OnCollisionEnter
after reviewing where that both the enemy and the bullet are involved
if it's a bullet, what should we do?
Then we should add it back
if we don't, Players and other objects
will disappear
and in order to figure out it's a bullet
we need to proceed with this first
if, the one collided, other
We took a look at the name of other.gameObject
If the name contains Bullet
if the name includes Bullet
what do we do when there's Bullet?
It is Bullet
What do we do?
Let's deactivate it
So, let's deactivate this
This is the process of loading the magazine
so, use other.gameObject.SetActive and false
Otherwise, from else, use Destroy
With this set up, when the colliding object is Bullet
it will deactivate and put it back to the magazine
or, it will be destroyed
set this value like this
Which other part should it go into?
We shall set one up for DestroyZone, too
Let me choose this part here like this
Ctrl and C
Comeback to DestroyZone
Want to destroy one that's colliding
Ctrl and V to this part
If the object that collided is a bullet, we should deactivate it
If it's not a bullet, we should destroy it
That's that
Let's play a bit
Play
So far, we've managed to create the bullets
and set them up with an objectPool
Now, let's handle the enemies in the same way
and set them up with an objectPool as well
To make EnemyObjectPool
Currently, as we can see, the EnemyManager is handling
these tasks for each Enemy
Here, we have the EnemyManager as shown
If each EnemyManager has its own objectPool
it could become difficult to manage
So, we'll use a single EnemyManager and
let the others just reference the position where they should be created
allowing them to use the same objectPool
So, let's have it be used
with the name of SpawnPoint
Press Ctrl and D in EnemyManager to duplicate
Then Enemy will be created at this position
Let's name all the duplicates as SpawnPoint
SpawnPoint, like this
Change the name to SpawnPoint for all of them
So, without the EnemyManager
select all the others
Enemy Manager script on the right
shall be removed in Remove Component
Then, there will be only one EnemyManager left
and a Transform component
And then, for this EnemyManager
we will set it up so that enemies are created at the locations of these spawnpoint objects
So the enemies will be generated based on their respective spawn points
So let's dive into EnemyManager
Just as before, let's set it up
to use the objectPool in the same way as with bullets
So, you'll create a size for the objectPool
and then you'll also need an array for the enemies
You can set public int poolSize
so that 10 enemies can spawn at once
just like bullets
having maximum enemies of 10
Next, let's create the objectPool array
like this
You can create the GameObject array
and name it enemyObjectPool like this
Next, there were the SpawnPoint objects where they would be created
So, right now, you've placed 5 SpawnPoint objects
To specify the positions of the 5 SpawnPoint objects
Let's create a Transform array
Here, let's store the relevant details
for the spawnpoint objects in the array
and we'll create a public transform array
and name it SpawnPoint
Shall we save it now?
and go back to Unity
In the EnemyManager
you can see the Spawn Points here, right?
The Pool Size is also set to 10
by pressing + button, you can add one vehicle by another
If you look at the top right of the Inspector window
There's a lock here
Let me lock this up
So even if you select other objects
if you click it, the lock stays locked
it remains fixed on the EnemyManager
Now, let's select the SpawnPoint and choose them all at once
and then grab this SpawnPoint
drag and drop it in here
All the objects will be arranged at once
Once this task is complete, I will unlock the lock again
Now, let's implement it so that we can use the objectPool
Let's implement this
First, we need to fill the objectPool when the objects are spawned
Let's add the data to the objectPool below here
For that, the objectPool size needs to be created in advance
So, for the enemyObjectPool
how many should we add in the new GameObject
Let's first create an array
with the size of the Pool Size
Then, we need to repeatedly add
one object at a time to the pool
for(int i=0;〈poolSize;i++)
Let's create it repeatedly
GameObject enemy equals Instantiate
What should be added here?
enemyFactory
Actually, you don't need to write this yourself
as this content is already in the Update
Take this whole thing and paste it like this
Then, let's add one object at a time to the objectPool each time
And what do we need to do? deactivate
This should fill in there
So in here, enemyObjectPool
place enemy in i-th order
Then, deactivate it
Deactivate by using enemy,SetActive and false
After saving
Let's see of enemies actually spawn
If you press play button
Not only the bullets, but if you scroll down
you will also see that 10 enemies
are pre created
Everything is disabled right?
In this way, you can create enemies
in the same way as bullets
Next, to use it, how do we use it?
Come down, and in the Update
Instead of simply creating an enemy at a specific time
what do we do now?
we can take an enemy from the objectPool and use it when needed
So, this part
Take objects from ObjectPool to use
What do we need to do first?
We need to detect the ones that are deactivated
So, for(int i=0;i〈poolSize;i++)
When this is possible, we can create enemy
Once you are done with this, make sure to move it up
First, let's take the enemy
and add it to gameObject
Let me get the i-th element from enemyObjectPool like this
This part is the same as Bullet
Let me skip whatever we've covered before
If it's deactivated
we will be using only the ones
that are deactivated in the if enemy
enemy.activeSelf
What is the value? When false
it will be deactivate right away
You can set up it's position first
And what do we need to do for this one?
simply just Activate it
enemy.SetActive and true, all is good
enemy will be activate as well
Now that you've created one, what's the next step?
We don't have to keep repeating, right?
break and end the search
By doing this, finish up the results
and now Enemy will be utilizing
the ObjectPool just like Bullet
But, we haven't set up what happens when Enemy collides
When OnCollisionEnter happens in Enemy.cs
When OnCollisionEnter happens
and the enemy is supposed to die
what are we supposed to do?
we should deactivate the enemy and return it
to magazine or to the ObjectPool just like for Bullets
Do not use Destroy gameObject
How? return it to ObjectPool
How do we handle this?
gameObject.SetActive and false
this way it will work fine
we have to consider one more thing, it is the DestroyZone
If it's a bullet, we can do it this way
but if it's not, we destroy it
This part shouldn't be written like this
For Bullet or Enemy
For both Bullet and Enemy, they need to be deactivated
So, add or in this part
I will copy and paste the code above here
Name for this
when the name contains Enemy
then it means this is Enemy
If it's a bullet or an enemy
we will deactivate it
we can add this logic to the DestroyZone
Let's save it and see what we've got
hit Play button
Doesn't the enemy seem to spin around
or come in an odd way here?
So, select the Enemy in the Prefabs folder
Let's open the Rigidbody section like this
In the Rigidbody, you'll see a section called Constraints
Here, you'll see that everything is set to Freeze
The Freeze on Position and Rotation in the Rigidbody means
that it prevents the object from being affected by physics
Let's play again
We can see that enemies generate normally
Next, for position
Since enemy is being generated only from this one position
So, let's modify to have enemies created
from the SpawnPoints
When creating this at EnemyManager
It is currently set to its own position
But what do we do now?
Let's have enemies come out from SpawnPoints
We will set it randomly
So, let's pick a random index
Pick a random index with int index and then where?
Random.Range
from 0, spawnPoint size and length
If we set up like this, what happens?
From here, it will pick the corresponding value, up to -1
Because arrays start from 0
so bring our attention here
instead of using the transform's position, who?
index-th which is at spawnPoints
We’re saying let’s take the position
from the one we randomly selected and use it
Now it will spawn between these spawn points
We can see it randomly spawning from various places
If you think it's coming out too slowly
In this case, in EnemyManager
the minTime and maxTime are not public
So if you change it to public
it will be exposed in the editor
Save it, and let's see
It would be good if you could adjust
the Min Time and Max Time appropriately
If we set it between 0.2 to 1 second, or between 2 seconds
it could spawn very frequently in short intervals
By making these modifications
you will have learned how to use the enemy
objectPool
Now, let's look at the issues
with using an array in this case
Right now, the array is set to 10 elements
but if we had 10,000
for example, and we had to detect
the 10,000th element in a for loop
we would have the problem
of the loop running 10,000 times every frame
This kind of problem can occur
when simply detecting the element
So, when using an array, the detection time can become long
and this could result in a situation where not using
an objectPool is actually better
This time, by using a List
Let's improve the objectPool's search speed
In an array, data is written in order
but in the case of a List, the data is stored here
and then in the below, there's an address
It's not written in order
in the memory space like an array
instead, the data scattered across different locations in memory
So, who does this data point to?
You can point to it like this, using its address
And it can point to its address like this
This is what you can consider the structure of a List
If you want to remove it
then how?
If we remove this, we can just cut this List
and switch the reference address
And if you want to add it, you can simply add it like this
So, the List makes adding and removing data very convenient
So, by adding only the deactivated ones to the List
when they are activated, we can remove them
By constantly taking the first item, deleting it, and doing this
the remaining items will stay in the List
So, we only bring in and use the ones that are deactivated
Then, after using them, add the ones that are finished to the back
and this way, we can work very efficiently
Even if there are 10,000 items like in a for loop
we can create a structure that allows us to retrieve them
through a search in a single step
So, now we will change this part
where we are using
what are we going to do? change an array to a List
So, instead of using an array
let's change it to public List and GameObject
and switch the array to a List
Let's first change the bullet, not the enemy
In PlayerFire, set it as a public List
and then we’ve created it using a List
So, instead of being limited to the array size
we can create it using equal sign and new
Now we've created the List object
From now on, instead of specifying
the index to insert it
you can simply add it here
By using the .add function with the List
when you add the Bullet
it will be inserted at the end of the List
continuously adding it to the back
You can structure it exactly like
an array by using a List in this way
The way you use it and retrieve items is the same as with an array
How? You can retrieve it like this
However, if until now you had to detect
and find deactivated objects every time the fire button was pressed
here you will make sure to only store what?
Only store deactivated objects
Already, here, the deactivated objects have been set up
So, you only need to retrieve the first one here
However, if the magazine is empty and there are no bullets left
then that won't work
So, how do we handle this part? It should be done like this
If there are bullets in the magazine
let's take the first bullet from it
This way, it will work
Why? Because this magazine will only contain
deactivated objects
So if you look at if there are Bullets in the magazine
look at, if, bulletObjectPool
We can use a function called Count
This uses the size of the List
to get how many are currently present
If there are any, it would be if the size is greater than 0
Since there are always items
At that point, instead of using a for loop
we can change it to an if statement
We don't need break here
Since we're only retrieving one
instead of using i, we just need to get the 0th element
And there’s no need to detect deactivated bullets
Since only deactivated bullets will be placed here anyway
So, I will move this part up like this
And I will remove the part where detection is done as well
Then things get really simple
After retrieving the bullet and activating it
you need to remove that bullet from the list
Once it's activated, you can remove it from the bulletObjectPool by using Remove function
Who? Remove the bullet
Then, that removes the bullet from the list
Why? Because it's been activated
It can no longer be added to the pool
which only contains deactivated objects
That's why we remove it
Now, let's check it out
after writing it this way
Press the play button, and it should be deactivated in the same way
I fired a bullet
However, if you look now, all the deactivated bullets
have disappeared
but if one is deactivated, it should be activated
yet there are cases where that doesn't happen
What’s happening is that when the enemy collides with the bullet
it should be put back into the magazine
but right now
all we're doing is deactivating it
So, we need to add the bullet back into the magazine
Add it back into the magazine, this step is necessary
In case of Bullet
So, where is this magazine?
It's in PlayerFire
This is a component attached to Player
So this PlayerFire is
I would put it as Player
We can use GameObject.Find
to find the Player in the world
Among the components attached to it, which one?
PlayerFire
This is what we are looking for
Who should we get it from, attached to it?
You can use Add on bulletObjectPool
to add the bullet
Now, you can add this bullet
By simply inserting the gameObject into other
since it is deactivated
this will complete the task of putting it back into the magazine
save it now
he same issue would occur when the bullet collides
with the DestroyZone
You can press Ctrl and C
and then add it to the DestroyZone as well
in the OnTriggerEnter of the DestroyZone
you perform these actions
when it's a Bullet or an Enemy
It seems like there’s a need to separate
and handle these tasks
So I would close this OR part
After placing it like this, using else
or else if to modify it again
For the bullet case, we can do it like this
Since deactivating them is the same for both
let's handle it like this
For the enemy case, we haven't handled it yet
For the bullet case, this task of reassigning it
to the player is necessary
Now, save
In the Enemy, this part of adding it to the magazine
In the DestroyZone, the part
where we currently add to the magazine
is handled like this
Save the changes, and then go ahead and test it again
It seems like the part is working correctly now
But we have a problem here
If you click on it, what issues do you see?
It says we have an error, here
It seems like the error
we are encountering is a null reference error
what is a null?
In here, what null is
This happens when you're trying to retrieve a value from ones that don't exist
Exactly, since the player has died
after colliding with the enemy
But it is already dead
but trying to access the PlayerFire component
We cannot retrieve the component of an already dead object
Nope
So, this part needs to be handled
by detecting it beforehand
Sure, let's try modifying the code here a bit
Let's first create a GameObject and add it here
Let's name it "player" and proceed
Position it like this
Let's change the name of the object here to pf
Since it's PlayerFire
Let's change it like this
Now, retrieve the component from this player object
this is what we are going to have
Found the Player now
Then, retrieve the PlayerFire component from the player
Yes, isn't this the same issue?
But here, if the player exists
we can add a check to detect it like this
You can handle it like this
so that the code inside the
if block only runs when the player exists
Only to be handled when the player exists
the same issue would occur with the enemy as well
Let's take, add Bullet into the magazine
Ctrl and C
Just like Player, when Enemy dies
we are not supposed to handle this
add Bullet into the magazine, this part
change it like this
Save it and then run test again
Now, we should be able to see that the issue has been resolved
We don't get the error message at all
So like this
We've tried changing the objectPool to use a List
The same work can be done for the enemy as well
In EnemyManager, you can change it like this
and switch it to a List
Then, you can simply use new
and add it like this
Next, instead of directly
assigning the enemy with equal sign
and use Add function
change it to add the enemy in the form of an addition
It's exactly the same as adding the bullet
Then, move down and update it so that after a certain amount of time
we have the process of detecting
So, when it's in the magazine or objectPool
objectPool
use if and enemyObjectPool.Count
What do we want to do?
Instead of using a for loop
we just retrieve the 0th element
and is there any need to check if it's deactivated?
No, therefore move it outside of the if statement and remove the if block
you can remove the if statement like this
And finally, you should remove
the enemy from the enemy objectPool
So, use enemyObjectPool.Remove
to remove Who? the enemy
from the objectPool like this
Great! Now, when it collides
When the enemy dies, what happens?
we need to add it back to the objectPool in the same way
Enemies can't collide with each other
so this part needs to be handled accordingly
this should only be handled in the DestroyZone
At this point, the enemy will be deactivated
but where is it in the objectPool?
It's in the EnemyManager
If you add it to the EnemyManager
it will make the process easier to manage
if you make it a singleton design pattern, it will be easier to manage and access
I will just let you guys figure that part out
We will take the content here and
use it by simply changing the name
Deactivate
This time, instead of the magazine, what instead?
Enemy, enemyObjectPool
This time, we are looking for whom?
we need to find EnemyManager
The one in the EnemyManager
let's put it as em
Since it's EnemyManger, right?
Who do we need to get inside EnemyManager?
You need to retrieve the EnemyManager class
this component
get EnemyManager component
We can keep the variable as pf as it is
This would now be which, not bulletObjectPool?
enemyObjectPool
If it’s a bit confusing, you can refer to it as manager
Great
We just changed the name here
It’s exactly the same as the upper part
By simply changing the name, get GameObject
then get the EnemyManager component
attached to that GameObject
Afterward, we access the objectPool attached to it
and complete the task in this way
So, we've learned how to retrieve
deactivate, and add it back to the enemy objectPool
completing the entire process
Let's save and find out if it works
Now, you can see that the enemies are coming in like this
then it stops coming in
we've got ourselves a problem here
Let’s take a look at what issues might be present
When the EnemyManager is created
In the DestroyZone
when the enemy collides with it
it's correctly added back into the objectPool
When the enemy collides with a bullet or the player
is there a part where it's added back to the objectPool?
We have none, when the bullet hits the enemy
there's currently no possibility for that enemy to be recreated
or added back to the objectPool
because the enemy isn't being added back to the objectPool
So, this part in the DestroyZone
if it collided with a Bullet
for the enemy when it collides with a bullet
it's just deactivating enemy only
but what about me? what am I doing?
It's simply being deactivated
where should I add into?
it should be returned to the objectPool
So, let's take this part and place it
in the corresponding section of
Enemy.cs from DestroyZone
For the deactivation part, let's add it back to the objectPool
Ctrl and C this part
In Enemy.cs, after SetActive false, go ahead
and press Ctrl+V to paste it there
What happens?
after deactivating it, you need to bring the EnemyManager back
and reassign it, all the way through this process
worked right?
In the OnCollisionEnter section of the Enemy
the enemy itself is not currently being added back
to the objectPool at the end
So, you can add that part here to ensure the enemy is added back
Save and go play
before pressing play, for testing
You can try setting the objectPool size
to just 3 for now
When you play
the objectPool will contain 3 objects
What happens if I shoot once
Now
It seems that in the objectPool
other objects besides the enemy are
being added to the enemy objectPool
If you look, Bullet is added
We see that bullets are being added to the objectPool as well
Why is this happening?
If you take a look at code, we did something wrong
It looks like this happened because of a simple copy paste
Which do we need to return the enemy to the objectPool?
Instead of adding the object that collided with
it should add myself back to the objectPool
by only adding myself
since it's the enemy, you need to add the enemy back into the enemyObjectPool
We should add its own GameObject
not the GameObject of Other
Let's save it and check again
Play
All the enemies are inside now
Bang!
Now
Enemy comes out
And we see Enemy gets added back into the pool
We can see how it works
If we increase the objectPool size to 10
10 objects will be added at once
As enemies appear on the screen, the pool will gradually decrease
there are 8 objects in the pool, and since 2 have been used, only 8 remain available
So, we have learned how to implement
the work up to the beta version
I will summarize the content learned from this chapter
First, we learned how to create a manager
using the Singleton design pattern
We understood the Singleton design pattern
and learned how to implement
Score Manager as a Singleton object
using the static keyword
Then, using the Singleton object
modified the code to set the score
We learned how to improve code readability
by using the Get/Set properties in C#
Next, we learned about memory management using the objectPool
First, we implemented an objectPool using an array
and explored its drawbacks
Then, we improved the objectPool by using
a list data structure to enhance the search speed
We implemented this objectPool
for the creation of bullets and enemies
Great job so far!
Creating a manager using the Singleton design pattern The Singleton design pattern A method of registering and using only one object instance as static
Since ScoreManager is the only manager that handles the score, it is created as a Singleton object for use
I moved to the Enemy.cs script and changed it to access the Instance member of ScoreManager
Get, Set Property Create a Get and Set property named Score in ScoreManager
Copy the implementation of the GetScore function into the getter of Score Copy the implementation of the SetScore function into the setter of Score
Remove the GetScore and SetScore functions Move to the Enemy script and change the score modification code to use the Get/Set property
Memory management using an objectPool Creating a bullet objectPool Add an objectPool size property and an array for the objectPool in PlayerFire.cs
Implementation order Create a magazine with a size to hold bull Repeat bullet creation Create bullets Add bullets into ObjectPool
Create the bulletObjectPool array with the size of the pool Since it requires repetitive actions, implement it using a for loop in C#
Modify the bullet creation code in the Update function of PlayerFire to use Deactivate the used bullet instead of removing it and return it to the mag
Add the same code to DestroyZone.cs as well
Creating an objectPool for enemies Creating SpawnPoint Select the EnemyManager game object and press Ctrl and D to duplicate it
Exclude EnemyManager and rename duplicates to SpawnPoint Remove the EnemyManager component from all SpawnPoint objects
Declare a SpawnPoint array to remember the positions enemies will spawn Create an objectPool with a size capable of holding enemies, and use a loop
Modify the Update function to use the objectPool and find any deactivated Enemy to activate it Modify Enemy.cs to deactivate the Enemy
Add a condition in DestroyZone to deactivate the Enemy Move to EnemyManager.cs and assign the generated enemy's position to the SpawnPoint location
Create an objectPool using a list Replace the bullet objectPool with a list Change the data type of the PlayerFire.cs object pool to List
Modify the Start and Update functions to use a List Insert the bullet into the objectPool in DestroyZone.cs when it's a bullet
Move to the Enemy class and get the PlayerFire object to insert values into the bulletObjectPool list
Replace the Enemy objectPool with a list Move to EnemyManager.cs and change GameObject enemyObjectPool to a List
Modify the Start function to replace object creation with a List and use the Add function to insert values
Modify the Update function to use a List instead of a for loop for checking Insert the Enemy into the objectPool in DestroyZone.cs using a List
Move to the Enemy class and, on collision, insert the Enemy back into the EnemyManager's List objectPool