Transcripts
1. Course Introduction: Welcome to the course. This course is not
a tutorial to teach you how to be a game
developer from zero. There are a lot of courses on the Internet that you
can take for that goal. Instead, this course
is like a mission in a game and is designed to
give you a new experience, tools, practice skills,
and confidence. I made it to help you get a
level up as a game developer. This course is divided
into three sections. In the first section, we'll make just a working
prototype of the game. In the second section, we will make it look and sound
like a good game. And in the third one, we will add menus, saving, and other things. Don't be afraid of
making mistakes. During the course, you
will see that mistakes are actually a way to get deeper
knowledge and understanding. All we need is to
find and fix it and get ready to spend more time
than it might seem to take. But instead, you'll get more experience than
it might seem to give.
2. S1L0: Section introduction: Welcome to the first
part of the course. In this part, we're going to
learn how the game works. We will keep things
simple with basic price and will not use any music
or sound effects yet. We will begin by making balls
move along the path with the help of free tool
called Baja Pass creator. After that, we will
create a way to shoot balls so they
land on the path. Then we will explore
how to shoot, destroy, and bring balls back along
with a few extra details. To wrap up this section, we will add some special
balls like revers, bombs and ones that
slow down time.
3. S1L1: Core scene setup: In this brief lesson, we will set up the
basic elements of our game the ball,
word and shooter. Let's start by creating
a new project. We will use version 2021, as it has the largest community online and still receives
frequent support updates. Next, we will select the MT
two D project and plate. We will adapt our game to mobile specific
requirements after finishing the main game logic. We will name the project Eternal Dungeon and
click the Create button. The first step is to go to the build settings
and ensure we are developing for a mobile platform
such as IOS or Android. Select IOS and click
the switch button. Next, let's rearrange the layout two by three and move
the project view down. This arrangement places
the scene and the game windows on the left with
the rest on the right. Let's also verify
the resolution. For this course, we will work
with a specific resolution of 1080 per 1920 pixels. We can adjust our game to accommodate other
resolutions later. We need to adjust the
orthographic size of our camera to 9.6. This setting is essential as it determines how much of the
scene the camera can capture. You may wonder why exactly 9.6? That's because the
orthographic size is half the height of the
camera's viewing area in word units. When dealing with a vertical
fuller D resolution, which in our case is
1080 per 1920 pixels. And aiming for a pixel
perfect rendering or a specific view scale, you need to calculate the
correct orthographic size based on the vertical resolution and your chosen pixel per
unit settings. In short, PP. PPU defines how many pixels represent one unit
in the unit world. So if our PPU value is
set to the default, which is 100 and the screen
height is 1920 pixels, then the half of the
head would be 60 pixels. Therefore, our cameras
orthographic size should be 960/100 equals 9.6. That's it. Save the scene, navigate
to since folder, and rename it to game. Then click Reload. First, create a new folder named sprites and place the images provided in this lesson
into the folder. Then drag each type of image into the scene
objects individually, naming each as necessary. Next, create an empty
object and name it board. This object will manage most of the game's functionality
related to the balls. We need to ensure that the
ball and the shooter have their own layer order so that the ball is
always about the shooter. Let's adjust this now. We will assign ten to the
ball and five to the shooter. I will create a new
folder named scripts. I will then generate scripts for each object we
have in the scene, namely ball, shooter, and board. Each script will be assigned
to its respective object. So these are the main object
we will primarily work with.
4. S1L2: Add Path Creator: In this lesson, we will add the pass creator
asset and learn how to edit it to create
our desired pass. Let's find the pass
creator component that we're going to
use in our game. I have already edited it, so there is only the open in
unity button from my site, but you would probably
need to click the add to my Assess button and
then open in Unity. So if your Unity project
is already open, then you will see this
packet manager window. Let's wait until it
finishes loading packages. And then you need to find the BuspsGreator component
here. Click Download. And then Import button. In the pop up window, click another Import button. Wait until it's finished. Then you will see a
new asset folder here. Let's create an empty object
and call it pass creator. When you create an empty object, its position is set to
the center of the SNU, but it will be easier for us
later if we set it to zero. Let's do it for the board
and pass creator objects. And then for the
pass creator object, I'll add a script with the
same name pass creator. Now we can edit that pass, but make sure that the moving tool is
selected at the top. We can change the path by moving the red anchor points and we can adjust the ankle by moving
these blue control points. You can learn more
about this pass creator on its *** store page. You can add another point by
just shift like clicking. To make the moving of these
points more convenient, we can increase
the handle scale. Great. We have successfully completed the pass. Well done.
5. S1L3: Move balls: This lesson is to learn how
to move bows on the path. Let's open the bowl script. We don't need these
commons because we already know that information,
so we can remove it. I'm adding the pass
creator field here. The find object of type function will just return
the pass creator, object that we
have on the scene. So we don't need to set it explicitly and we will have public flow
distance traveled field. And in order to move, we need to increase this
distance with time. Then based on that value
using pass creator, we can get the
point on the path. All right, it is moving. Let's create an empty object
and name it ball slot. Then create a script for it. And assign it to the object. I'll open that script. Just copy and paste all the code that we
had in the ball script. Fixing the dependencies. I'll move the ball object
in the ball slot object. Let's make sure that the
position is set to zero. And we can see that it is
working as well as before. I'll add the circle collider to the ball slot as we
will need it later. And now we can see that
our ball is a bit too big. The circle collider
radius is 0.5. So its diameter is exactly one point the sprites ball and we can change the pixel per
unit value here. You can play around with it depending on the
size of the sprite. Here, 200 will work. A drag and drop the bow slot to Assets folder to
make it a prefab. Let's create a prefabs
folder and put it there. I'm going to instantiate the bow slot from
the Bar script. For that, I need a reference
to the bow slot prefab. We can delete that
object from the scene. Let's get access to the pass crater here
in the board script. So we need the
length of the path. A since the diameter of our wall is one point we can place the integer part of the length number of
balls on that bath. So for example, if the
length of the bath is 30.5, we will be able to
place 30 balls there. So for each of that count, I'm going to instantiate one slot on each part of
the path accordingly. We will call that method on the start and let's
check if it is working. As you can see, it created
a lot of ball slots, but it seems like they move all together on the same point. To fix that, we need to set the initial distance travel
of the each of both slots. Let's change the type
of the ball slot prefer to bow slot instead
of the game object. Then we can access the
distance travel field of the ball slot and set it
to be the iterator index. As we can see, all the ball
flots are moving separately, but there is a problem
with each of the bolls. To fix that, we
just need to remove all the code that we
left in the bow script. Since we move that
functionality to the ball slot. Let's check again
and it is working. You can see the small gap between the first
and the second ball. Let's fix that too.
It is because we ignored that non integer
part of the path length. Let's split it up with each of the bow slots and the length of the step between
two slots would be path length divided
by slots count. Oh We are setting that value in two places, so it's better to move it out to a variable and use
that variable instead. Let's check again.
And it's perfect.
6. S1L4: Spawn balls: In this lesson, we
will make bolts bound one by one and then
move on the path. We will also add animation
to that process. Let's drag the ball lot to the stin and remove
the ball from it. We open prefab, remove
the ball and go back. Now we can remove the ballot
prefab from the scene. Let's check if we
didn't break anything. I'll just click on any of the bolt slot and
see if it is moving. Great. Let's clear up a bit and place all the grated
bolt slot in one object. I'm creating an empty object and calling it slows container. Make sure its position
is set to zero. We need a reference to that container in
the board script. And after we instantiate
the bow slot, we need to set it
transform parent, be the container transform. Let's fill that container
field of the boards. And please never mind the
exception on my side. It is only because of the
missing plugins configuration. Let's check if or
change did it work? To instantiate both, we need to have access to both prefab. Let's create the ball object again and make it a prefab. We can delete it now and fill the ball prefab
filled on the board. We need to access
the ball slots on the board so let's
keep it in an array. That should be integer
instead of float. A You can sort all slots by the
distance travelled field and get the first one, which is the closest to
the start of the path. And let's instantiate a ball on that slot. Check in what we
have. Doesn't work. Let's see if we set a proper index in layer for the ball as
we created it again. Okay, now we can set the ball, but there is only one. And the problem is
that in the ball slot, we need to reset the
distance travelled. Each time it is getting larger than the
length of the pass. Let's check again. Cool. Now we see that we create a lot of balls in the same slot. To fix that, we need to check if the slot already
has a ball in it. We'll create a new
field. It will be ball. And we assign a newly
created bowl to this slot. Let's do the check for
creating a new bowl. I now we can see that everything
is working perfectly. I have created a new
field on the ball script. Public bullet is spawning. Let's make a counterfeild, which will track the
scaling of the ball. In the update, we will go to check if the
ball is spawning. And we will increase that counter and set or
transform local scale to be Vctors 31 multiplied
by the counter. If that counter is
greater or equal to one, we set is founding to be false. Let's put the check before
we apply the scale. And add a return statement here. Also, where we create a ball, we will set it
spawning to be true. Great. If you see that little glimpse
when we create a ball, that's because the
initial scale of the ball is one,
so let's fix that. I Great. Let's increase the
speed of upscaling. Seems like it is a
little too fast, to make it easier
to play around with the speed and choose
what fits better for us. Let's move the speed value to the object where we can store similar properties like this. Later we can debug
it from the editor. Create an Idi object and
name it game properties. Also, I'm creating a
script with the same name. Assigning it to the object. So the property is public
float ball of scale speed. Let's put it to be two. We need a reference to this
object from the ball script. And let's replay that timber with the value that
we have created now. Dacing Great. You can see that we can play with that number in real time. But for now, let's
leave it to be two.
7. S1L5: Destroy balls: We'll create an
object at the end of the path that will destroy any ball that
collides with it. Let's make the ball
slots move faster and store the speed value in the game properties object as we did in the
previous selection. Let's check. Great. So Let's create a boll
destroyer object. It will be an empty object. Let's rename it. To need a circle
collider to the. Let's set it to be trigger. To make it possible to
collide with the ball slot, we need to add a rigid body to the component
to the ball slot. Let's create a Bol
destroyer script. Assign it to this object. In the script, we will need on trigger enter to the function, and we need to check
if the object that we have collided with
is the proper one. So for that, we need
to tag this object. Let's bring ball
slot to the scene. Click on Tech, add Tech, click the plus icon, and name it Bol slot. Click Save, select
Ball slot again. Click Tech and click Bol
slot in the drop down. Let's save the progress. And now we can remove the
proof up from the scene again. In the script. We check if the
collided object has tech bow slot using the
compare tag function. If so, we can get the pause slot component
of that collider. And check if the
Blot has a ball. Here, we will simply destroy
the game object of the ball. Let's check if it's working. I'll move the ball destroyer object closer so we
don't need to wait. And great, it is working. To make better
destroying animation, we need to check if the
ball is destroying. We would need another variable
similar to e spawning. But the more such
variables we have, the harder it will
be to maintain that. So the better way is to have one state variable that will just tell about the
state of the ball. So let's create an
empty um script. And for now, we'll
have three states spawning Islat and destroying. Let's replace the e spawning
property with the state here and fix the issues. We will have another case
if the state is destroying. Later on, we will
have more states, so it is better to handle it in the switch case
instead of if ande. The destroying case would be really similar to
the despondent case. We will downscale
instead of upscale. In the destroying case, let's replace the
upscale counter with the downscale counter. H adds another value to the game properties object. It will be ball
down scale speed. Here we won't switch state, we'll just destroy
the game object. Let's fix issues in
the bord script. Also, in ball destroyer script, we will set the state
to be destroying. Unassigned the ball
from the ball slot. Et's check. Whoa. We forgot
to change something. So in this case, we check if our
downscale counter is less than zero instead
of greater than one, because we make it smaller
and smaller by the time. Let's check again.
Great. It's working.
8. S1L6: Shoot balls: Uh huh. In this lesson, we will make the
shooter shoot balls. Let's create a method that will change the ball
state to shooting. When moving the ball
from point A to point B, we also need to
remember the direction. So we need a separate
field for it. The direction can be passed from the shooter using
method parameters. So the state, we need to add
the new ball state shooting. Now we can implement
the state switch case. This will be a simple
logic of changing the position over time. We are going to increase
the ball transform position by the shoot direction vector multiplied by some speed value, which we will start in the
game property subject. And And of course, we need to multiply
it by time to time. Now we can go to
the shooter script. Let's get the
direction of shooting. To get that, we need a
position of the shooter. We can get that by just calling transform position and we need touchscreen tab or
mouse click position. In this method, we will get
the letter in this way. Also, we don't need
that access value, so setting zero can
ease our calculations. A reference to camera main can be utilized in the
start function. It is better for
the performance. So the shoot direction is mouse position minus
transform position. This row will make
the shooter object look in mouse position
or tab direction. We can move this logic to a separate method
to keep it clean. Now we can work on generating a ball which we are
going to shoot. We already have similar
logic in the board script, so I would move it out to a separate script and reuse it instead of just
copying and pasting. This is also good practice. As usual, it will begin from an empty object
mole factory. Then I'll create a script
with the same name, but without white space and
assign it to the object. As in the boll script, we need a reference
to the ball prefab. This method will take
a vector three point as a parameter and
create a ball on it. We can return to
our shooter script now and use the ball factory. We need to remember a ball which we are preparing
to be shooting. So I'll create a field here, and we will be creating another ball only if this
reference is not set. So one ball per shoot. Finally, we can check if a click or touchscreen
tap happens, and then we shoot the ball. For that, we again
need a direction, but this time, it
should be normalized. This means the same direction but in scope of a square 0-1. Then we can just finally call the shoot method of the ball
and clear the reference, I will start preparing
another ball. We still have to make
some changes from the Unity editor in
the boll factory. Fill the ball prefab field. And we can change the
ball instantiation logic to our new boll factory usage. We don't need preference
here anymore. Saving. Let's head to
check in how it works. Oh, no, we broke something. But the good point is that the ball shooting
work as desired. Let's see what's wrong. The logic of moving balls around the board is in the
word and bol scripts. What have we changed
in the board script? If you compare the
instantiation, we can see that before we were creating balls at the
zero slot dot transform, but now we are using just
zero slot transform position. So to fix that, we need
to set the parent of the ball transform to
zero slot dot transform. Let's go to verify if
the fix is working. Cool. There's one more thing that we are going to do in this lesson. Catch a moment
where our shooting ball collides with
any ball on the path. As with ball destroyer, we can use the on
trigger enter today. Now checking if the collided
object has a bowl slot deck. If yes, then using the
Get component function, we can use it as a bowl slot. Then we can check if it's
not empty, and if so, let's output some message. Check. Not yet. The reason is that the ball prefab doesn't have a circle glider to decomponent. So let's add it and make it
a trigger. Checking again. We can see the output even
before we shoot any ball, and this is not
what was expected. Let's find out what's wrong. If you take a look at
the equation here, it doesn't check if one of two collided balls
is the shooting ball. It just triggers when two sibling balls on
the pass collide. So I'll add that requirement. Final check. Great. You can see that
sometimes it collides with two balls at
once, but don't worry. That's okay. And it's
working as expected.
9. S1L7: Add different colors: In the lesson, we will add
red and green ball colors. Similar to the ball state
in the previous lessons, let's create another
um called ball type. For now, we will have red, green, and blue balls. I'll add a field type
to the ball script. And in the ball factory script, we will have a
method that creates a random bow at the
given position. I'm starting by creating an array with all
available ball types. I will name it colors. Red, green, blue. It can be static and red only since we aren't
going to modify it. Also, we need reference
to ball sprites. This method should now accept
ball type as a parameter. We will set the type
field of a ball. And here we need to
get the sprite render component and set
the right sprite. I will just set the
blue sprite for now, but let's create a
method that will return sprite based
on the ball type. Now we can use the method. We need a functionality
that creates a random ball. The method will be very
similar to the existing one. We can use it inside. But we need to pick
a random ball type. I'm creating another
private method. Here, make sure that you impart a random
from Unity engine. Basically, we just pick a
random element from the array, and now we can call it here. Let's replace the old method
usage with a new one. Seems like we might it private, so it's not visible
outside of the class. Let's fix that. Replacing methods in
board and shooter script. We finish it with the cod. I'm switching to the
Unity editor now. Let's move this ball destroyer
to the end of the path. The last thing we
need to do is to set the price fields of
the ball factory. And check the sprites
pixel per unit value. At the beginning of the section, we changed the value of the
blue ball sprite to 200. So its size equals
exactly one unit. We can see how it looks
if you don't change that. So we need to do it. Check it again. Perfect.
10. S1L8: Land balls: Let's help the
shooting balls land. We will begin by adding
two new ball states, landing and switch slots. In the boar script, let's create a method that
will handle the landing. We will need to know references to the collider slot
and the landing ball. It would be good to have all slots sorted
by distance here. We can just copy paste this part of a line
from above for now. And here we need to know the
index of the collided ball. Then let's find the index of the first empty slot after
the collided ball slot. When the shooting ball lands, all the balls that
are in front of the line should be pushed
by one slot forward. We could just take the
first and the last of them. But in case when there
is a gap between them, we need to find out either
the index of the last slot that is not empty or the index
of the first empty slot. We will just go step by step and check if a slot
has a ball in it. If it doesn't, then we will remember the index
and stop the loop. But let's move this out
to a separate method. And as a good practice, if there is no such index, we will return minus one. We have the indexes. Then now, let's shift all these
balls by one slot forward. It is handier to do so in reverse order because a slot after the last one
will always be empty. Here, we should take
a slot by index and put a ball from the
preceding slot in there. But we also need
to make sure that this ball will no longer
be assigned to the slot. It would be great to have a method that will
handle this logic. I'm going to create such
method in the slot scripts. A We will need a reference to a slot
from a ball script too. So let's use this new method. Y. After that, we need to decide what we want to do with
our landing ball. Should it land before or
after the collided one? We can do it in this way using the get closest
distance along path. It returns a distance on the path of any point
projection to it. So not this and also not this
one, but the closest one. So if the distance travelled of the collided ball is less than the projected distance
of the landing ball, then it's better to put that landing ball
before the collided one. Otherwise, we will push the collided ball one slot forward and place the
landing one after it. What we have done
is that we just assigned all balls to the
slots where they should be. Now we need to physically
move them there. First, let's create
a land method in the ball script and call
it for the landing ball. It will be just a
change of state. For the rest of the balls, the balls that aren't
landing and spawning, we need to create another, but a very similar
method, move to slot. And also call it for them. Y. And we will add these date cases. But first, let's actually call
that word and ball method. We will call it from here. We need a reference
to the board. So the collider slot this ball. Going back to the switch cases. I'm generating all the
missing switch cases, but I'm going to leave
only two of them. We can use vector
three move towers to gradually move the
landing ball to its slot. Et's put five here as a speed value and later we will move it out to
the game properties. Now, as usual, we need to
check if our object is close enough to its
target position so we can finish the landing. A but we need to make sure that we update
the parent field of the ball transform the ball will
move along with the slot. The switching slots case
will be very similar. Going back to the bar script, we need to place the new
ball assignment method. Everywhere we assign
a ball to a slot. Search will help us find
the rest of such places. Also, let's move this row
to a separate method. Replacing this line two, I guess it's time to
check what we have. And we have some exceptions. If you hold a control or command on Mac and click
on the link here, it will open the script right where the error has occurred. I forgot to utilize
the words reference. Now, it kind of works, but it doesn't move other balls. The loop equation is incorrect. It is better, but sometimes it moves a ball
that we shouldn't move. I may or not fix the issue, but we need to
handle a case where we collide two balls at once. To fix that, we need to
disable the shooting ball collider when the
ball is not flying. Let's try if that helps. No, we need to find
another reason. Since it's moving an extra ball, then the problems should
be somewhere here. We were supposed to move balls that are after
the collided one, but this loop can still grab an index of the collided.
Let's fix that. A
11. S1L9: Destroy same colors: Uh huh. In this video, we will make three or more
matching balls destroy. Here, right at the end
of the land ball method, let's call a routine, which we will create soon. This rotine will destroy three or more matching
balls in a sequence. Let's make it wait until
the balls are ready. Either the lot doesn't have a ball or the ball is
in a proper state. Let's see how it would work. We need an input
parameter here to find sibling balls to
the one that is landed. We can pass the
landed ball slot. Let's get an index of this slot. We will be searching for the balls that we
need to destroy, we need some list
to keep them there. I'm going to create two loops. The first loop will go backwards to the start of the ball slot based
distance array, and the second one will go to the other
side of the array. In these loops, we
will search for balls that match the landed one, and as soon as we meet a wrong ball, the
loop should stop. The first one will go straight
from the slot after the landed one and should stop at least at the start of the array. We need to make sure that we don't add the same ball twice. Let's make this check. Here, we demand that the ball should be of the same
type as the landed one. Finally, adding the
ball to the array, if the conditions are okay. In similar way, we should search for the balls
in another direction. After we pick all the balls
that should be destroyed, we can invoke the
destruction function. But first, we need to
create a method for it. Let's head to the ball script. This time, again, the method will do just a change of state. And the date is
already implemented. We can go back and
invoke this method. Also, we need to
clear the ball slot. Let's check the change.
We have a problem. Here, when we check if bowl slot that ball
is the proper type, we also need to make sure that the slot has the ball
so it's not null. And we can stop the
iteration if we meet the first ball that doesn't fit our criteria. Let's check again. We have another problem here.
Let's see what's wrong. We need to check
if the quantity of the matching balls is
greater or equal to three. Otherwise, it would destroy balls even if we have
only two matches. Also, as you can see, it doesn't work every time. Let's find out the reason why. Probably, we forgot to add the landing ball
to the matches. And it seems like we increment where we
should have decremented. Let's fix that and try again. Okay, it is working great. See you in the next lesson.
12. S1L10: Mid-section refactoring: Let's get our code
a bit cleaner. First, I will start by
removing the unused logs. Also, ID suggests
factoring this line. We can move this search
code to another method. I will also put a line break here to
make it more readable. All these codes, I can also
move to a separate method. One more suggestion from the ID is to make explicit
access modifiers. We also can remove these
unused dependencies. You can see that they are
grade out if they are unused. And this value, we can move to the game
properties object. And finally, we can rewrite these conditions to make
the structure flatter.
13. S1L11: Move separated balls back : After we destroy matching balls, we need to remove the gap
and move some balls back. After destroying and before
we move separated balls back, we need to put a pause. 0.5 will be okay. I will create a private
void move separated balls method and call it
right after the pause. What we are going to do is to find out how many
empty slots are between the group
of balls and move all balls from group
A to those slots. To do that, first, in
boll slot by distance, we can find an index of a
first slot that is empty. And second, we can find where this sequence
of empty slots ends. The first non empty
slot in the array. But starting from the
previously found index. Here is the number of empty slots between
two groups of balls. Now, we're going to move balls from the second
group to these slots. The loop starts from
the first empty slot and goes in the direction
of the end of the array, but accept the number of empty slots since they
are going to be empty. That part of the array
should be our empty slots. So we need another for them. A, let's move to slot of
each ball we are moving. Checking the result. We have some error. Let's see what is it about. We need to check if the slot has a ball before
calling its method. Check in again. Another problem. Here, we need to make
sure that we really have the gap between
two groups of balls. If there is no any empty slot, some of these indexes will be minus one as they
will not be found. Nice. Seems like it's working, but we will have to fix a
little animation issue. As we can see, when the balls are moving to their
target positions, they are moving
out of the track. This is because we are using
Vector three move towers, which makes balls
move directly to their points and
in case of angles, they are just being cut. What we can do to fix
that is to make the balls move along the path as
we do with bol slots. Let's go to the ball script and add distance travel field. Also, we need a reference
to the pass creator. And here, where we tell
a ball to move to slot, we need to utilize the
distance travelled field. As we did before,
using this function, we can get the
distance travelled based on the current
ball position. Let's go to the state handling
and update the logic here. We need to move ball towards
its slot along the path. In case when the slot is
further than the ball, we can just increase the
distance traveled field until the ball reaches the slot. But in other case, where the slot is
on the other side, which means that it distance traveled field is less
than we have for the ball, then we need to decrease
that field of the ball. So basically, if you want the ball to reach its lot
position, in first case, we need to add some positive
value to the distance travelled field of the ball
and in the second case, we need to subtract some value, which means adding
some negative value. We can express the direction
in which we move the ball in this way and we can simply multiply the delta that we're
adding by this direction. Let's move speed value out
to the game properties. So for the ball position, we can simply use Get
boot at distance. And to check if the ball is
close enough to its slot, we can use this expression. Let's check. Great.
14. S1L12: Move → Destroy cycle: Let's make the game repeat, destroy and move back
cycle until there are no matching balls left
near the landed ball. I'm going to move all
this code to do Wil Loop. This loop makes one iteration. Then it checks the equation
and based on the result, it stops or repeats again. We need to know when to stop, and we're going to stop
the loop when there are not enough matching
balls to destroy. I'm going to move
this definition out of the loop to make it possible to access the list
in the loop conditional. Here we can set a value
to this variable. Again, we check if there were some matching balls
but not less than three. Also in every iteration, the ball to which we compare
other balls will change, so we need another
variable for it. Starting value will be
the landed ball slot, and let's make sure that
after an iteration, we still have such a ball. The idea suggests
reverting this block. It will make the code
more simple and flat. So after we destroy
the matching ball, we can set a new value for
the collided ball slot. It may be confusing, but it will be the first
slot on this list. The thing is that after we destroyed balls in those slots, those slots become empty. Then we move back balls that were in front of
the destroyed ones. So the collided ball slot
becomes not empty again, and we can use its new
ball to compare with it. And if there were no balls
after the destroyed ones, the slot will be just empty. To cover this case, let's go to the
get similar balls method and place a check. Also, let's make sure
that it always returns results ordered by the
distance travelled. And we're ready to
check how it works. So we're good to go.
15. S1L13: Add special balls: Let's make a basis for some special balls that we are going to add in
the next lectures. For now, there will
be three of them. Bomb when explodes, destroys
one or more sibling balls. Reverse when explodes makes the ball move backwards
for a few seconds. Time slow makes the slots move at 50% of the speed
for a few seconds. We will begin by
adding fields for three new sprites in
the ball factor script. If you're using Credr, you can press Control and D or comment and D on Mac
to duplicate line, and then you can just change
the name of the field. We need to add new ball
types in the ball type NM. Going back to ball factory, let's create an array that lists all the
special ball types. This method should be renamed to be more clear
about what type it returns and we can create a similar one
but with special types. Having these two methods, I'm going to create Get
Random ball method. Again, but now in some
percentage of cases, it will return a special type. So this part fix a
random value 0-1, and only values that
are bigger than 0.2 will lead to returning
a normal color type. This will basically happen
in 80% of cases because 80% of values 0-1
are bigger than 0.2. So the rest 20% of values will result in returning
a special ball type. Using the ID suggestion, I'm generating additional cases for the switch block to
return proper sprites. There is an archive with new Sprite attached
to this lesson. Let's go to the Unity editor and add the new files
to the SprizFolder. After that, we'll assign them
to the Bol factory object. Now, let's go to the Br Script and call our new method here. Also, here in the
get similar method where we compare balls by type, we need to make updates to
support the new ball types. For this, I'm going to
create a script wall Util. This utility class will be
a published static one. And we'll have a method
get color by type. This method will take a
bold type as a parameter and return its
corresponding color as a bold type num value. Basically, it will
be a switch block. For the color cases, we will just return
the same noms. But for the special types, we'll return their colors. We can go back to the
get similar bowls method and use a new utility here. I will just put a line
break to align this code. Now we can copy pase
it to the second blog. I guess we can test it now. The scale problem
can be solved by updating the pixel per unit
value of the new sprites. We can see that for the
normal color sprites, we have set 200. Let's set it for these two. And here we find
another problem that is not related to the
update of this video, but it would be nice to fix. First, we need to disable
ball shooting for a period of time where we
destroy matching balls. So that will save us from interfering with the
process of destroying. To implement it, let's go to the bore script and
add a value here, which will be false by default. Now we can set it to be true
at the start of our routine. And here, in the end, we'll wait until
all balls on track, finish their
switching slots work. So we can set the
field back to falls. Finally, in the shooter script, we need a reference
to the board. Here, where we handle
shooting click, we can use the field to
make sure that it is false. We can test now. This has not fixed the back completely because if you
click twice really fast, you are still able to
shoot this tining ball. But this behavior is
acceptable for now, so we can fix it later and concentrate on the
special balls. See you in the next video.
16. S1L14: Add bomb-type ball: A bump when explodes destroys
one or more civilian balls. First of all, we need
to check if there is a bump pole among the balls
that we are going to destroy. We can do it like this. Then we need to find out what
balls can the bomb destroy. So first, I'll find an index of amp ball
in the global array. For now, let's destroy one ball before and one
ball after the bomb. We can make it a loop and move the bomp
radio to a variable. Index of a slot after the bump, I will just name it left
index because it is on the left side of the bump if looking from the screen edge. And the index of the one before the bomb will be right index. For the indexes, we need to
check if they are valid, so they aren't less than zero or bigger than the sorted
array list index. Also, we need to make
sure that this lot is not empty and that we don't already have this
lot in our destroyer list. If all is okay, then we can
add this ball to the list. And the very similar logic we will have for
the right index. We can move the bomb reduce
value to the game properties. Let's add a reference to
it in the Bar script. Now we can use it here. Also, to make each method
shorter and cleaner, we should move the logic
to a separate method. Let's test. Great. We can also see a bug when balls reach
the end of the path, but we shouldn't worry
about this bug because later we will make the game stop when a ball
reaches that point. So see in the next video,
17. S1L15: Add reverse-type ball: Okay. Reverse, when it explodes makes the ball move
backwards for a few seconds. As in previous video, let's begin from
checking if there is a reverse ball in
the destroy array. Since this time, it is not only about adding more balls
to the destroyer array, but about changing the
flow of the balls, we will need a car routine. I'm going to create one
called Start reverse Co. I will just call it here
so we don't forget later. Just to be safe, we'll make sure that nothing specific
happens when we start this carotin and
it doesn't mess a flow. So we will wait until any
matching ball destroying ends. And all slots should be either empty or balls should
not be instead of landing and not instead
of switching slots. For this flow, we need
another field, reverse. On this point, it
will become true. Let me generate
this missing field. So it created a field here. Let's move it upper
to a similar field. In the way like we made
balls move backward. In the previous
video, you can flip the direction of the slots by introducing a
direction field. It will be one by default. We need to add it here too. So let's go back to our routine. We can create a loop and change a direction to minus one
for every ball slot. Now we will wait for 2 seconds. Now we'll set the direction of the slots and finally set I reverse to falls. Let's check. One problem here.
In this method, we should place a check for I rears and stop producing balls. Also in both slots, where we change distances
at some point in time, it can become a negative value, which as we could see, moves slots to the other
end of the path. So to fix that, we
need to check here. We check if the direction
is negative and the distance traveled
is less than one. So it's close enough
to the beginning of the pass, and finally, the slot is not empty, then we can start
destroying method here. But first, if the ball is
really close to the zero point, it is possible that the ball
will not disappear fast enough until it reaches zero. We can check if the
distance is less than, for example, 0.5, then we can just destroy
the ball manually. Otherwise, we can just call
start destroying of the ball. And in any case, eventually, we need
to clear the slot. Another thing we need to do for every slot is that when the distance traveled
reaches a negative value, then we'll just set it
to the past length, which is at its last
point. Let's check again. It is good for now.
Let's only move this reverse duration to the game properties to
keep the coat clean. That's it. In the next video.
18. S1L16: Add slow-type balls: Time slow makes the slots move at 50% of the speed
for a few seconds. I will just copy this block, but change the ball
type to time slow. Also, we haven't created it yet, but let's call a cotin here with the name time slow co. Now, let's actually
create this orotin. It will be very similar
to the reverse, so I'll just copy these two. I will remove these lines here we need to make
sure that ears is false. Here we will change
the direction value to 0.5 and after the time
slow effect ends, we will reset it to one. Let's go to the
game properties and create a time slow
duration field. Now we can use this field. Also, we can see that there is something wrong with the
direction field here. This is because currently its type is an integer,
let's fix that. But before that, we should rename this field
to speed multiplier because it will be more clear and changing
the type to float. Now we should update this field name
everywhere we use it. And finally, we can
test how it works. The time slow works
as we want it, but sometimes we can see that a gap after dsteroid balls
is not being closed. But we will focus on this
bug in the next video. I should say that often we can discover some game bug
that we have not foreseen, and it makes us decide what
should we do to fix it? And in some cases, the easiest way to avoid
that bug is to introduce a new game mechanics
rule that will not drastically impact
user experience, but will resolve the problem. In this way, we can add one more restriction that
will save us a lot of time. Let's disable ball shooting
when either reverse is true. This way we could avoid a
lot of complicated cases when the landing occurs near
the beginning of the path, and that's it for this
video. In the next one.
19. S1L17: Section-end refactoring & bug fixing: Let's finish the section
with some cleaning. Let's do a small cleaning first. In both script, this
part is repeating, so we can move it out
to a separate method. Let's call it place
in slot transform. So we can replace it here too. In bold destroyer script, we can invert this I blocks. I always add brackets
here to make return operators more visible. In bold script, we
can revert this I. In shooter script, we can add these explicit access modifiers to start and update methods. Now let's fix that back. We can see that sometimes after we destroy
some matching balls, other balls don't move
to empty positions. This happens because here, when we search for a
first empty index, sometimes it returns zero
because at some moment, the zero slot is empty
and a new ball that is going to be generated soon is not yet assigned
to that slot. So instead of a gap here, we end up moving all balls by one slot back to
cover the first one. We can fix it just by
beginning or search, not from zero but from one. That's it for this
video. Good work.
20. S2L1: Add new sprites: Adding new sprites for the
background, walls and shooter. I have attached some new
sprites to this lesson. Let's load them, and now
we can import them into our Sprite folder by just
dragging them into it. Now, let's begin our gray sprite update from
the background. Then the shooter. Then we can click on the Ball factory and
replace Sprite there. So we can check what we have. First, we can see that the
ball sprite is too big. Can try different values in
the pixel per unit input. For example, if you put 200, we will see that it is too
small and the faster way to figure out what value we need is to bring a ball
prefab to the scene, zoom in and change its sprite. So we can see its size compared to the circle
collider to the size. So in this way, we
will find the value. In our case, it will be 135. I will select all
new ball sprites and update their pixel
per unit to 135. So now we can remove this
ball object from the scene. Let's run again.
Great. Let's continue. We can see that the shooter
is not looking good. Let's move it to the center
of this background platform. Also, I would like
balls to appear not in the center of the shooter
but from this part. We can add an empty object as a child of the shooter and
we can generate balls there. It can be named shoot point. For that object, I'm adding a new field in
the shooter script, and we can use it here. I will increase the
shooter order in layer, so it will be over
a shooting ball. Let's assign the shoot point
field and test it all. And we need to move
this shoot point up. Let's see what we
have. The ball is not moving along with
the shooter so we need to make it a
child of the shooter. But we can see that
we need to clear the parent value right after
the ball has been fired. In again. Cool. See you.
21. S2L2: Activated balls sprites: Let's improve the
player experience by replacing normal bowl
sprite with active. In the same way as
for regular sprites, we need a set of fields
for active sprites. I will just copy these
and rename them. Also, we need a
method like this one. But we're going to use it
from outside of this class, so we should make it public. Now we can go to
the ball Script and create a method that will
change the bolt sprite. To access the ball sprite, we need a reference
to the sprite render. And in the method, we simply
set the sprite to a new one. Let's open the board script, go to destroy matching
balls go and move this code to a separate
method that will have the name destroy
all balls in list. Here on the first
line of the loop, let's call or update
Sprite method. To access boll factory
from this method, the method should not be static. Using ball factory, we can get
a new sprite for the ball. Good. Now we can switch
to the Unity editor. Let's populate new
spride fields. And we're ready to test. First, it would be better
to modify or destroy animation to let us see the
activated price better. We can go to the ball
destroying switch case. Here on every update, we decrease or downscale
counter by some small value. Let's call it substrhand
how we can make the ball hand gone longer for some time and then
start disappearing faster. We can take the substrahand and make it even
smaller for some time. So we can have
another variable that if the downscale counter is
bigger than 0.5 will be 0.1, and otherwise, it will be one. And we can multiply the
subsahand by this value. As a result, since
the subsahand will be ten times smaller until
downscale counter reaches 0.9, the downscale counter will decrease ten times
slower for the time. Let's see how this works. We can see that value 0.1
results in too slow speed. So let's change it to 0.3. Also, let's make the landed ball have the same rotation as
other balls on the track. Checking. Nice.
22. S2L3: Shooter active sprite: Similar to the previous lesson, we will replace the shooter
sprite with active one. We need to split the
spawning state into two, spawning on track and
spawning to shoot. And when a ball finishes
spawning to shoot, it will become ready to shoot. This will help us set different shooter sprites based on the state of
the shooting ball. Let's fix the code after we change the
responding state name. So first one plays in word. You can see these red
marks on the scroll bar. It indicates that a
line has an error. Next in bold script. This time, other errors were
caused by the first one. Now we can go to the
shooter script and add two fields for active
and inactive sprites. I also, it is better to change how shooter and board
depend on each other. I will remove a
reference to the board. Currently, the shooter
checks whenever the board is reverse or board destroying matching balls are
false or true. But we can simplify this by providing a new field
in shooter script. Is shooter disabled
from outside. I added from outside part
to underline that we will set this field only from outside of the
shooters script. So now we can use
this field here. And we need a method to
update shooters pride. To change the sprite, we need
a sprite render reference. So if it is not disabled, it will be active sprite,
otherwise, inactive. Also, we need to check
if the next ball is ready to shoot so we can
add an arrow function here. And we need to consider it
in the Update Sprite method. Now we can actually call this Updates Pride
method on each update. Also here, when we create
another bolt shoot, we need to set its date
to spawn and to shoot. I think that's all of
the shooter script. Let's go to the board. We need to add a reference
to the shooter. Here in destroying
matching bowls core, we will disable the shooter
at the beginning of the crotin and enable it
back at the end of it. The same is in the
reverse cotin. Now, let's head to
the ball script and add some cases
for the new states. I will just copy paste the spawning one with
one difference. It will change the
state to ready to shoot after it
finishes spawning. Also, I'm generating
cases for other states. We don't do any specific
action on them, but it is good just
to list them here, including the default one. So later in case we add a new state and forget
to implement it, it will see a warning
from the IDE. Good. Now we can go
to the Unity editor. Let's set the
shooters price field, and we can test the game. Good for now. See you in the next video.
23. S2L4: Add Music & SFX: Let's use free music
and sound effects from the Unity Asset Store. I have attached all the
links to the lesson, so we can easily find the assets that we
are going to use. Let's add them one by one. The first is Earth materials. We will use only the
soft land concrete file. The second is the RPG or kestrl. We will add only the
heated lens file. The third is the epic boss. It has only one file. The next is the here fantasy. We will import two
files from here. Then we need this
epic Och cell pack. We import only one
file from here. And last but not least, is this free sound effect pack. We need these three
files from it. As you can see,
the imported files are located in separate folders. This is not really convenient, so let's move them to
some common folder. I'm creating a folder
here in assets. Let's name it audio. Now we can move all
these audio files to the audio folder. And empty folders
can be deleted. The audio is taking
up a lot of spice, so we can compress
it to save some. Also, we can change the lot type to make sure that the
game floads fast. The lot type is set to the
compression load by default. But the problem is that this will make the
game load slower, we should set it to streaming. Here, we can see that the default compression of
this file is good enough. Let's go to the next file. I'm changing the
compression format to verbs and the quality to 80%. So on the outputs, we have less than 18%
of the original light. Let's do the same
with the other files except for the small ones. A now we can select all these files and just dragon drop
them to the scene. While they all are
selected on the scene, let's make sure
that they all have the play on a wage
check box unchecked. To organize this object, let's create an
empty game object and give it a name
audio manager. We can make these objects to be children of the audio manager. It is better to make sure that the audio manager and its children are positioned
at the zero point. I'm going to create a script for the audio manager and
assign it to the object. This script will control
the audio in the game. So we need to list
the audio files here. One last for sound effects
and one list for level music. Also, a separate field
for the menu music. Let's assign these properties. We need some methods
to play the audio. The first will play
sound effects. As an argument, it takes the index of the sound
effects in the array. We will randomly
change the pitch of the audio so that it will
not be too repeatable. For the level music, we will just play a random
track from the list. For the menu music, we will simply need
to play one track. Also, let's go back
here and make sure that we don't play the
same sound effect twice. Now we can go to
the Unity editor and since we are going
to reuse this object, let's turn it into a prefab. Now we're ready to
use what we created. We can go to the
shooter script and add a reference to
the audio manager. So here, when we shoot a ball, we are going to play the
sound effect with Index two. Let's do the same
in the Br script. Here, when we land a ball, we will play a sound
effect with Index zero. And finally, when we
destroy matching balls, we will play a sound
effect with Index one, and it's time to
play some music. We are going to play
the level music in the start method
of the borscript. Miss Duke. Is the next one.
24. S2L5: Add path & spawner sprites: A small but needed improvement. We will add a ball path
and sponno sprite. First, I will add this level
sprite to the game scene. We need to set its
order in layer value to something that will
set the sprite between the ground
and other sprites. Three will work for us. Also, since it's
related to the pass, I will place it below
the pass creator object. We can see that the pass is
not aligned with the sprite, so we can correct
the pass a bit. I Now let's add this
ball spon sprite. It is better to rename it to be consistent in naming style, and would be good to place it under the level object
in the hierarchy. Most important, let's move it to the top left
corner like this. So finally, we can test it. Right. Right, right.
25. S2L6: Add destroyer animation: To make the user's
experience better, let's create an animation
for the bold destroyer. There is an attachment
for this lesson that includes the sprites of the animation that
we are going to add. It should be unpacked here in the sprite folder like this. Let's go to the creation
folder and direct this bold destroyer 11 file
to the synth hierarchy. Now let's open the
animation tab. By the way, if this
tab is not visible, then you can find it in the
Top Manu Windows animation. In the animation tab, I click on Create. Then in this Explorer window, I go to the Assets folder and create a new folder
named Animations. Inside it, I create a
folder named Bl Destroyer. And finally, inside it, I save this animation
file as creation. Let's now select
all the sprites in this folder and direct them
to the animation window. While all markers are selected, we can just drag by this bar and expand the time of the
animation to 1 second. We can check how it works now. Setting the order in
layer to be eight. And we can rename this
object to boll destroyer. Now let's create another
clip for this animation. The name will be Idle. Go to the folder named
Idle, and as before, select all the files
and drag them to the animation tap timeline
and expanding it 1 second. Great. We have two
clips of the animation, so let's make it switch from the clip creation
to the clip Idle. It can be done in
the animator tab. We just need to right
click Make transition. And in the chart here, we need to arrange
it in this way. And a few more things. Let's move it here to the real ball destroyer
on the scene. Also, maybe it's better
to rename this to ball destroyer animation because the real ball destroyer
already has this name. Let's put the animation
right under the real one. Testing how it looks. Just moving around
a bit. Fantastic.
26. S3L1: Main menu: In this lesson, we
are going to create a main menu scene with
only three buttons. There are men sprites
attached to the lessons, so you can load them
and after that, let's add them here
to the Sprite folder. Then let's create a
new scene using file, new scene, basic two D, and create it's better to
save the scene right away. It should be saved
in the sins folder with the name main menu. Now let's go to the
Sprite folder and direct the Bground dungeon
sprite to the scene hi arche. Renaming it to Background. Also, as we did in the
Cursin setup video, we will set the main
camera size to 9.5. To make the background a bit different than in
the game scene, let's make it darker. Now let's implement
a menu canvas. First, we need to
add an empty canvas. Now, let's add a
text mesh p button. Click Import here. We need a lot picker buttons and font sizes of 80 will
be more suitable. This button will
be the new game. I would like to make the button transparent with the white text, changing the color
to completely white. For the button backgrounds, I'm clearing this default sprite by selecting it and
pressing back space. Then changing this
color opacity to zero. Let's go back to the text again, changing the material
per set to outline, making it bold, and increasing
the outline thickness. The button style is ready. Let's make a few
copies of this button. The first copy will
be a continual button and the second will
be an exit button. I should rename these buttons
according to the text. We can select all three buttons
and move them together. Now, let's add an empty
image slot and select it. We need to direct
the logos pride to the source image field and
click Set Native Size. I'll move it to the top of the screen and also would be good to rename
the object to logo. It will be very convenient
to have these objects in the same order they are
displayed on the scene. I'll move this to
the top here too. You are almost finished
with the UI part and therefore we can create a
script with the name main menu. Attaching it to the canvas. The first method will be
for the continuum button. Here, we just need to
load the game scene. The new game button will
have the same for now. And the exit button will
quit the application. Just a warning that
the exit button will do nothing when running
the game in the editor, so don't worry when
you notice it. We can see this highlight
stating that we need to add the game scene to the build
settings. So let's go there. It can be done by just
drag and dropping the game and the main
menu scenes here. And now we can simply
close the window. We have buttons on UI and we have a script assign
it to the canvas. Now let's connect all of it. I'll drag canvas here and select the corresponding method
of the main manuscript. Similar in the next two buttons. A good main menu
should have its music. Let's go back to the script. We need the audio manager. So we can just call this method. Going back to the
Unity editor to add the Audio manager prefab
to this hierarchy. Finally, we can test it.
27. S3L2: Pause menu: Now, we will add a pause and pause
functionality to our game. I will create a canvas, add a button to it. This button will have a specific sprite so we
can remove the text here. Let's direct the sprite to the sort image input
of this button. Click in the set native size. We can place this button
in the top right corner, setting the anchor
point to this, and the position should be equal approximately half of the
button width and height, but with a minus sign. Now, let's add a panel that
will darken the game scene, so it will be clear that
we're in some kind of menu. I will rename it to the POWs panel and the
button to the Powe button. Let's duplicate this button, so we will have a
separate one for pausing. Now, let's add a button
to go to the main menu. We will anchor it to
the bottom center and the rest will be similar to what we did in
the previous video. We need a new script. Let's name it Pause menu and
assign it to the Canvas. In the script, we will
need a reference to the POS panel to disable
it when we press on Pause. We need a method to
go to the main menu. And we need two methods
for pausing and pausing. So they just simply disable
and enable the pause panel, which should be
disabled by default. Besides this, we
need a reference to the board to tell you that
the pause is enabled. Let's create a new
bull fields for this. We can set it in our
pause and pause methods. Also, to stop the time counting when we
are in Pause menu, let's set the time
that time scale to zero here and set it
back to one here. Now we can go to the
Unity editor and assign the Pause panel
field and also the buttons. Contested. There are three problems here. First, we need to stop the shooter rotation when
the pause is enabled. So I'm adding a
reference to the board here and wrapping these two
methods with an if statement. Second, when we
pause or unpause, the shooter takes it as a click and shoots a ball in the
direction of the unpause button. To avoid this, we can change the G Mouse button down method here to get
Mouse button up. It will trigger node when we
click on the mouse button, but instead when we
release the button. It gives us some time to
set the espouse field to true and we can add
this check here. And when we unbounce, we should not set espouse
to falls right away. But rather, we can wait for
1 second and unbound it. We are going to use
a code in here. The third problem is that
if we press the pause, it will set the
timescale to zero. And if we want to go to the
main menu and click Continue, the timescale is still zero because we didn't
press on pause, so we didn't reset
the timescale. To fix this, we can
just set it here. Let's test again. We Men. Great.
28. S3L3: Add levels: Let's add level system and save the last play at level
so we can continue the game. First, we're going to
create a game UI panel. This pause button can
be moved here now. We also need to
display level time, adding a new text mesh p here. The text will look like this. The phone size is 80. The anchor point
is the top center. And the text alignment
is the center, and it will look better if bold. Also, let's duplicate this to
display the current level. To program this, we
need a new script. Let's give it the
name game UI Canvas, and we can assign the
script to the canvas. In the script, we need references to the level
time and level number. And we need a method to
update the level time. Is parameter will take
a number of seconds that passed from the
beginning of the level. We can take the means part
by dividing the value by 60. And taking an
integer part of it. The second part, we can get from a leftover
from such division, and again, we take an
integer part of it. The time will look like this. Minutes colon and seconds formatted to a two digit number. We also need a similar method
to update the level number, but it will be much simpler. We just prefix the number
with a level word. Now we can go to the Br script
and call these methods. I will just put these values to check if it's
working correctly. Yes. Looks good. It is time to store the
last plate level value, so let us continue playing from it after we close the game. We can implement this in
the game property script. Let's create a new
field, last level. I will make it
private so it's not accessible from the
outside of the script, and we're going to strictly control what we do
with this field. First, we will let reading this value by providing
this arrow function. Then we will let incrementing of this value using
this simple method. Now let's go to the board
script and use this. First, we need to
track a level time, so I will create
a new field here. We will update this
field as time passes and we will update the
Gami with this value. If the level time
reaches 60 seconds, we will increment the
last level value, update the level number on UI, and reset the level
time counter to zero. Also, at the start stage here, we need to get the
last level value from the game properties and the level time will
be zero by default. As for the level duration, it will be good to have it in the game properties
too. Let's do it. I will set it to 10
seconds for now, so it will be easier to test. We can use this value now
in the board update method. Let's test how it works. Good. Now, let's save the last level value in
the longtime memory. Just a basic way, we can
use player prefs here. We will use this custom key for the last level before board
start method is called, we can use the awake
method here to load the last level
value from memory. Maybe better to move the
K out to some field. Let's make sure that we
say a default value for the last level when we run
the game for the first time. When we are testing the game and need to reset
all saved values, we can go to edit, clear all player prefs. Let's run the game and
test what we have. Right. It starts from the saved level. Since we increase the ball
speed on every level, we need to make sure
that effects like time slow also take this
speed up into account. So let's move it out
to a separate method. This method will
take a parameter for the effect multiplier, and we will be passing
this value here. Also, it should not be static. The speed value will be
the effect multiplier, multiplied by the level speed. Let's suppose that the
basic level speed will be one plus last level
number multiplied by 0.3. Actually, we should move this method to the
game properties. It should be public here, and the 0.3 can be
started as a field. Slots speed up per level. It should be float
instead of integer. Let's go back to the words
and use this method. You should also set
the initial speed when we init both slots. That's how it works. We can see that this ball cannot get up to the speed
of the others. So let's add this multiplier on the landing and other
sides of the ball. We can start from the
destroying state. The landing. The
switching slots, I think that's
enough. Let's test. Why why, why, why. Seems like this level
multiplier of 0.3 is too big, so let's try 0.1. We should also make sure that we change it here in
the Unity editor, clearing the player prefs
and checking again. Cool.
29. S3L4: Add game-over scenario: Let's define and trigger
a game overflow. When a player loses a game, the game over message
should not appear abruptly, but rather we should let the player figure out
that something is wrong. We need a corrupt in here. Also, we will need a
field for that state. Let's go to the cotin. So first, we disable the shooter and set the
game over field to true. Then we will wait for 2 seconds. And we will need to show
the message on the game UI. Let's go there and create an
empty method for it first. Can call it after
2 seconds here. Going back to the gamei, we don't need this code,
so I'll remove it. We need a reference
to a game over panel, which we need to create later. Oh, yeah, we need to start Mt. So at the start, we disable the game over panel, but we enable it when
it is the game over. Let's go to the Unity editor and create a new
panel in the canvas. It will be the game over panel. It should be darker. We need to add a text message. The style will be similar to
what we have done before. But the phone size
will be bigger. Also, we need some buttons, so we can just go
to the Power panel, duplicate this button and
move it over to our panel. This button will
be the play again. We need another button
for the main menu. Let's create metals
for these patterns. The player game button will
just reload the scene, and the main menu will
load the menu scene. Besides this, we need to disable the game I panel when we
show the game over panel, adding a reference to the
game I panel and changing its sit here and here. Go to the Unity editor
and assigning the panels. And we can assign our
metals to the buttons. Finally, we can go to the boldest royal script and trigger the game over scenario. We need a reference
to the board. When the ball destroyer
collides with the ball on track, we check. If it is not the game
over yet, we start it. Also, it would be
nice if we speed up all balls when the game
over is triggered. Let's find this
place in the code. Actually, it is duplicated
in a few places so we can move it to a separate
method. Speed up slots. It should take the effect
multiplier as a parameter. Now we can use it in the
game over cotin here. Also, we can place it
instead of duplications, but we need to make sure that we pass the correct
parameter value. Let's test it. We can see that while we were looking at the
game over screen, the level somehow jumped 12-14. To fix this issue, we
just need to place an I statement here where
we count the level time. So it will stop counting
when I game over is true. Let's test again. Great. And
30. S3L5: Start new game: H. In this lesson, we will add functionality to the new game button
in the main menu. If you are already tired, let me cheer you up because this video will
be really simple. So let's go to the
main manuscript and create two empty mates. Confirm new game and
cancel new game. We need a reference to
the game properties and the public field
for confirmation panel, which we will create later. I will move this line to the
confirm new game method. Instead of it here, I will make the
confirmation panel active. Let's go to the game
property script and create a method
for set last level. In this method, we will set
the last level to be equal to one and save this value
to the player prefs. We're going to call this method from the
confirmation one. In the cancel method, we will just activate
the confirmation panel. I forgot to initialize game properties
here, let's do this. In the Unity editor, let's add a new panel to the canvas and name it
confirmation panel. I'm making it darker enough to cover most of the tax
on the background, and we need a text here. Are you sure that you want
to start from level one? I will make it bigger
and center it. A Now, we can duplicate the exit button and make a yes button from it. Once again, duplicate
the yes button and make a no button. Now, we can assign
these buttons. And it will be
cleaner if we move all these menu items to a separate panel and disable it when we show this
confirmation message. So I will add this
main menu panel. And more all related
items under it. Now we can create a field
for the main menu panel. We can assign these panels. And here we do like this. Enable conformation,
disable main. Disable confirmation,
enable main. Disable confirmation by default. Less test. H. We have an error. It is because the
game properties object doesn't exist on
the main menu scene. So let's save the scene, go to the game scene and turn the game properties to a prefab by dragging it
to the prefab folder. We can save and go
back to the main menu now and add this new prefab to the scene here here.
Let's test again. Great. Mm hmm.
31. S3L6: Fix bugs & optimize: U. This video, we will fix most of the bugs
that we could notice before. We will start from this bug. When reversing,
sometimes, we can see small balls that move back
beyond the spawning point. Let's go to the ball
script and create a maton that will check if the ball is located
close to the path start, which is our spawning point
for the balls on track. We can use this function to get the distance on
track for that ball. Let's say that if this
distance is less than 0.2, then this ball is close enough. When we destroy a ball, we can check if the board is in reverse and the ball is really close to
the spawning point. In that case, we immediately
destroy the ball. Let's test a few times. Seems like we fixed it. The next bug is that we can shoot a sequence of small balls, despite that this could be a
feature if you would like. We would need to do a lot of changes to make it look good. For now, let's assume that this is a bug
and let's fix it. We can go to the shooter
script and inside the shoot next ball method,
we can add a check. If there is no next ball or this ball state is not
ready to shoot, return. Testing it. I think that we need to move
the defect inside the method and after the
check that we just added. Checking again. Great. The next thing is
not fully a back, but rather nice to have feature. If balls go back in reverse, after it, the new balls are completely
different from before. But we can remember the
balls that we destroy in reverse and generate
them back after it. For this feature,
we need a stack because it works using the
first lost out principle. I will make it read only because we will
initialize it only once and we will add or remove elements from
the same instance. Just removing unused code. When we generate balls on track, we can call this method to
get a random ball type. In this method, we will check if there is any
type in that stack. If yes, then we will return the last item from it
and automatically, it gets removed from the stack. Also, we need a method that
we will use from outside, which will add a
type to the stack. Since the functionality
that destroys walls on drivers is located in the ball slot script,
let's go there. We will need a reference to the ball factor here, so it it. And let's add explicit access
modifiers to these methods. We can see that there is a bit
of complicated logic here. Maybe we can simplify it first, this block checks
if the distance traveled is larger
than the path length. It is similar to the block below where we check if the
distance is less than zero, so we can put these
two blocks together. Then we can move the logic inside this big block
to a separate method. We can name it destroy ball because of what
it really does. Let's go there and we
can see that we can add a parameter to this
method to move the logic related to
distance traveled out. And here should be 0.5, but I will just move this out to separate method
while I remember. The only thing that these two
blocks do is that it trims the distance travelled to not go over the range
of the path length. So we can name it trim
distance travelled. Also, we should put s here to make them more
clear to understand. Going back here, we can add the type of the wall that we're going to destroy to the stack. And we should not
forget to put 0.5 here. Testing it finally W. W W. Looks really good.
The other thing that we can fix is that when the reverse ball
is located here, it's unclear that
if we destroy it, the balls will move back
because the arrow on the ball actually points in a completely
different direction. Let's fix it. We should do something here in the
update of the ball slot. We will eventually move such
logic to a separate method. Let's do it vice versa and
call a method here first. Let's say it will be looked
over path direction. Now we can create this method. To get the right direction, we need two points A and B. If point B is the point on the path where the
ball is located, which is this, then point A is some other point that is
one unit back at the path. So we can get the direction
vector like this. A minus B, and we can set the transform dot up of the slot the point
in that direction. Do that axis can be skipped
since it's to the game. Also, it's actually
opposite instead of towers. Now let's go to the
ball script and make the ball transform that up, equal to its lot up
vector here and here. Testing we can see that the ball is
flipping at this point. Probably, we can pick
a smaller vector because if the distance
travelled is less than one, then the direction vector
will be like this. We can set 0.1 here. Try again. Cool. The next buck occurs
when there are a few bomb type balls that
are going to be destroyed, then only the first
bomb actually explodes and the rest are
counted as plain balls. We can make it easier to
reproduce this case by going to the ball factory and
editing this line. Now let's see what
we can do to fix it. In the board script, this
code only searches for the first bump and then we
check if this bump exists, then finally add
sibling balls to the destroy list and we can
simply make it to be a loop. First, we filter all slots
that have a bomb inside. Then we iterate over each of
them using a for each loop. We only need to make sure that the variable name is correct, and then we are
ready to test it. Great. Don't forget to
revert this change. The next bug is
that on some level, we need the shooting ball
to be generated faster. So let's fix this two. Just go to the ball slot,
copy this line, pass it here, and
here, thes in it. Done. The next issue happens when we shoot a
ball outside of the board. We can see that they are
not being destroyed. Instead, it's just
more and more of them. So what we can do is create some object outside of the board that will
destroy these balls. Let's create an empty
object and name it ball fill out pan. Then we add an edge
collider to D to it. We need to make sure
that its position is set to zero on each axis. Now we can edit the collider. Also, we need to add
a rigid body to D and change the body type
to kinematic because balls don't have rigid
body to D and we need at least some of the
colliding objects to have it. We can place this
pain to be below the ball slot container
in the hierarchy. Now let's go to
the script folder and create a new script. Ball fill out pain. Let's assign it to
the pain object. In the script, we
can clean this up. We will need only the on
trigger enter to the method. So we're checking if
the object that we collide with has the tag ball. Then we destroy its game object. And we don't yet have this tag, so we need to go to
the Unity editor, click on layers
at the top right, and then edit layers. A new tech ball. Then we need to go to prefabs, double click on the ball prefab and assign
this tag to it. We can go back and we
are ready to test it. Great. The next thing, it would be nice to reward a player for finishing
a level by destroying all balls that are currently on track at the moment when the
level changes to the next. Also, it will be consistent
with the case when you load a level by clicking
continue from the main menu. This time, it will be
only one line change. In the bar script,
the update method, we will call destroy all
balls in list method. The argument value
will be all slots from both slots by distance
where the ball field is not empty. We can do it. Morning. Great. And last but
not least think, we will tune up some
game properties. So let's set the level
duration to 60 seconds, and the ball low
speed to 1 second. Don't forget to change
them here, too. And we need to apply
overrides to make sure that the changes are
saved to the prefab to. We are ready to test sit Nice work.