Create a 2D Mobile Game with Unity | Max Vynnyk | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Create a 2D Mobile Game with Unity

teacher avatar Max Vynnyk, Software Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Course Introduction

      1:28

    • 2.

      S1L0: Section introduction

      0:55

    • 3.

      S1L1: Core scene setup

      4:36

    • 4.

      S1L2: Add Path Creator

      2:37

    • 5.

      S1L3: Move balls

      8:24

    • 6.

      S1L4: Spawn balls

      12:21

    • 7.

      S1L5: Destroy balls

      9:33

    • 8.

      S1L6: Shoot balls

      12:10

    • 9.

      S1L7: Add different colors

      6:09

    • 10.

      S1L8: Land balls

      16:04

    • 11.

      S1L9: Destroy same colors

      5:58

    • 12.

      S1L10: Mid-section refactoring

      1:56

    • 13.

      S1L11: Move separated balls back

      5:57

    • 14.

      S1L12: Move → Destroy cycle

      2:53

    • 15.

      S1L13: Add special balls

      8:25

    • 16.

      S1L14: Add bomb-type ball

      5:02

    • 17.

      S1L15: Add reverse-type ball

      5:26

    • 18.

      S1L16: Add slow-type balls

      3:14

    • 19.

      S1L17: Section-end refactoring & bug fixing

      1:49

    • 20.

      S2L1: Add new sprites

      3:51

    • 21.

      S2L2: Activated balls sprites

      4:50

    • 22.

      S2L3: Shooter active sprite

      5:00

    • 23.

      S2L4: Add Music & SFX

      7:12

    • 24.

      S2L5: Add path & spawner sprites

      1:21

    • 25.

      S2L6: Add destroyer animation

      3:12

    • 26.

      S3L1: Main menu

      7:00

    • 27.

      S3L2: Pause menu

      6:36

    • 28.

      S3L3: Add levels

      11:19

    • 29.

      S3L4: Add game-over scenario

      6:17

    • 30.

      S3L5: Start new game

      6:06

    • 31.

      S3L6: Fix bugs & optimize

      14:35

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

21

Students

1

Projects

About This Class

Get new experience by making a cool dungeon-style 2D mobile game!

We both know that to get better in something we need to practice it.
Especially if it comes to being a developer we need continuously update our knowledge and gain skills.
As in games we need to improve our skills, level up to continue playing.

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 designed to give you new experience, tools, practice skills and confidence.
This course is created for you if you already have some beginner experience with Unity and C# and understand basic concepts of game development and C# programming.
It will give you a good gain in experience and you'll get new practical skills in creating similar tile-matching games.

I made it to help you get a level up as a game developer.

Don't be afraid of making mistakes.
During the course you will see that mistakes are actually a way to get deeper knowledge & understanding.
All we need is to find them and fix.
And get ready to spend more time than it might seem would take and get a lot more experience than it might seem to give.

Are you ready for the mission?

Hope you enjoy the course and feel free to message me or leave your thoughts on the conversation below each lecture!

Meet Your Teacher

Teacher Profile Image

Max Vynnyk

Software Developer

Teacher
Level: Intermediate

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

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.