Game Dev Kickstart: 2D Game Development Using LibGDX & Java | Brandon Grant | Skillshare
Search

Playback Speed


1.0x


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

Game Dev Kickstart: 2D Game Development Using LibGDX & Java

teacher avatar Brandon Grant, Game Developer and Graphic Designer

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:53

    • 2.

      Install the JDK (Java Development Kit)

      2:40

    • 3.

      Install IntelliJ IDEA

      6:11

    • 4.

      Set a Java Home Environment Variable

      2:53

    • 5.

      What Is LibGDX?

      1:28

    • 6.

      Setting Up LibGDX

      3:50

    • 7.

      LibGDX Basics

      10:51

    • 8.

      Create & Import the Project

      2:53

    • 9.

      Import & Manage the Assets

      11:34

    • 10.

      Create the Game Screen

      8:02

    • 11.

      Window Size, Camera & World Units

      10:29

    • 12.

      Viewports

      10:26

    • 13.

      Create the Fighter Class

      16:48

    • 14.

      Draw & Animate the Fighters

      14:49

    • 15.

      Update the Fighters' Facing

      5:30

    • 16.

      Handle Keyboard Input

      13:13

    • 17.

      Define the Ring Bounds

      7:34

    • 18.

      Attacking & Blocking

      8:10

    • 19.

      Getting Hit

      17:54

    • 20.

      Generate the Fonts

      6:44

    • 21.

      HUD (Heads-Up Display) 1: Wins/Loss Ratio and Difficulty Text

      11:46

    • 22.

      HUD (Heads-Up Display) 2: Fighter Health Bars

      20:15

    • 23.

      Set Up the Rounds

      15:14

    • 24.

      Improve the Round Timer

      4:44

    • 25.

      Handle Touch Input

      8:54

    • 26.

      Texture Filters

      6:33

    • 27.

      Texture Atlases & Packing Textures

      7:48

    • 28.

      Game Over

      16:46

    • 29.

      Pausing the Game

      12:41

    • 30.

      Add Sounds & Music

      22:37

    • 31.

      Add Opponent AI

      31:38

    • 32.

      Blood 1: Blood Splatters

      21:17

    • 33.

      Blood 2: Blood Pools

      16:10

    • 34.

      Main Menu Screen 1: Set Up the Widgets

      18:49

    • 35.

      Main Menu Screen 2: Draw the Widgets

      23:00

    • 36.

      Clicking the Buttons

      10:44

    • 37.

      Changing the Fighters

      16:42

    • 38.

      Manage the Settings

      15:57

    • 39.

      Settings Screen 1: Set Up the Widgets

      21:29

    • 40.

      Settings Screen 2: Draw the Widgets

      21:55

    • 41.

      Changing the Settings

      17:22

    • 42.

      Create the Loading Screen

      11:21

    • 43.

      Conclusion

      0:37

    • 44.

      Install the Android SDK

      2:57

    • 45.

      Create & Import a New Project

      5:08

    • 46.

      Running LibGDX on Android

      7:15

    • 47.

      Transfer the Code

      3:15

    • 48.

      Fix the Audio

      9:22

    • 49.

      Change the Settings Screen

      3:53

    • 50.

      Fighter Controls 1: Set Up & Draw the Sprites

      15:14

    • 51.

      Fighter Controls 2: Attacking & Blocking

      6:54

    • 52.

      Fighter Controls 3: Joystick Movement

      14:46

  • --
  • 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.

5

Students

--

Project

About This Class

Do you want to learn how to create amazing video games for FREE with LibGDX?

I'm Brandon and I'm going to help you master LibGDX by creating a complete and fully functional cross-platform 2D video game.

LibGDX is a free and open-source game development framework. Game development frameworks are collections of libraries and tools that provide building blocks that developers can use to create their own games, and are more lightweight and flexible than game engines, like Unity 3D, Unreal Engine, and Godot.

LibGDX also provides cross-platform deployment, meaning that a game created with LibGDX can be run on multiple platforms, including Windows, Mac, Linux, Android, iOS, and web browsers.

(NOTE: Please be aware that you will need to know some Java in order to get the most out of this course. Taking a short 2-3 hour Java course should be more than enough.)

The Game Dev Kickstart course is very comprehensive and is full of extremely valuable information for creating incredible 2D games using LibGDX. We'll begin by installing the JDK (Java Development Kit) and the IntelliJ IDEA IDE, which we'll be using to write the code for our game. After that, we'll jump right into learning all about LibGDX by creating a complete 2D fighting game called Stick Figure Showdown. Among the dozens of topics we will be covering throughout the course, we'll learn how to:

  • draw images to the screen and animate them

  • manage assets (images, audio, etc.) using an asset manager

  • work with cameras and viewports for full control over how our game appears on different screen resolutions

  • handle keyboard and touch input from the player

  • generate fonts using the Freetype extension

  • display a HUD (Heads-Up Display)

  • create and use texture atlases for enhanced performance

  • add text and clickable buttons to the screen

  • add sound and music to our game

  • add AI (Artificial Intelligence) for the opponent fighter

  • create and switch between various types of screens (main menu, settings, etc.)

  • use Scene2D for more efficient UI (User Interface) layout

  • allow the player to change and save various settings of the game (difficulty, fullscreen mode, etc.)

  • EXTRA: add Android functionality to our game!

You'll also get access to all of the resources and code for the projects in this course!

So what are you waiting for? Join now and let's start developing games today!

Meet Your Teacher

Teacher Profile Image

Brandon Grant

Game Developer and Graphic Designer

Teacher
Level: Beginner

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: Do you want to learn how to create amazing multi platform video games for free? I'm Brandon and welcome to the Game Dev Kickstart course, where together we'll create a fully functional video game from start to finish using LibGDX and Java. We'll begin with getting our development environment up and running by installing Java and IntelliJ idea and setting up LibGDX a cross platform, free and open source game development framework. After learning the basics of LibGDX, we'll get straight to creating our main project, a two D fighting game called Stick Figure Showdown. First, we'll learn how to draw images on the screen, as well as how to use viewports and cameras to get our game looking the way we want it. After that, we'll start drawing and animating the fighters, allowing the player to press keys on the keyboard to make their fighter move and attack. We'll then learn how to draw a hud or heads up display on the screen to show things like the health bars of the fighters and we'll make it so a fight can have up to three rounds. Next, we'll learn how to pause the game, as well as add buttons to the screen that the player can click. We'll then liven up our game by adding some music and sounds, programming the opponent fighters AI, allowing the player to change the difficulty of the game, and showing some blood when the fighters hit each other. Finally, we'll add a few more screens to the game, including a main menu screen where the player can change their fighter and a setting screen where the player can change various settings of the game, which will be saved and loaded the next time the player plays the game. In a bonus section of the course, we learn how to add Android functionality to our game. This will involve learning how to set up the Android emulator on our computer and adding buttons to the screen that the player can use to control their fighter. This is going to be a very comprehensive course, and by the time you finish it, you will know everything you need to know to create amazing two D video games using LibGDX. So without further ado, let's get started. 2. Install the JDK (Java Development Kit): The JDK or Java Development Kit includes a set of toys and libraries for creating Java programs, and we'll need it for all of our projects. If you've used Java on your computer before, you might already have the JDK installed. To find out you can open a command prompt, which if you're on Windows, you can do by clicking inside the search box at the left of the task bar here and typing command. Then opening the Command Prompt app that should pop up at the top. And here we can type Java Version. If it says that the Java command isn't recognized, you likely don't have the JDK installed. On the other hand, if it gives you a version number, you do have the JDK installed. However, it's important to know that as of the time of recording this video, LibGDx only supports JDK versions 1.1 through 1.8. So if you have a version of the JDK that is earlier than 1.1 or later than 1.8, you will have to download a different version. But don't worry. You can have multiple versions of the JDK installed at once. Okay, to download a compatible version of the JDK, if we do a search for Download JDK, one of the top results should be one from orcle.com entitled something Like Java Downloads. If we click this, that brings us to a page where we can download a version of the JDK. Currently, it says versions 19 and 17 are available. And by the way, sometimes they put a dot between the numbers and sometimes they don't. So these are the same as versions 1.9 and 1.7. All right, and as I mentioned earlier, LibGDx is only compatible with versions 1.1 through 1.8. So I'll go with Version 1.7 by choosing it here. And actually, if we go to the Java archive tab here, we can also choose other versions like 1.8 or 1.6. But anyway, I'll stick with 1.7 for now. Okay, with Java 17 chosen, we can now choose our operating system. I'm using Windows, so I'll click the Windows tab. We can then choose which type of file we want to download for the installer. I'll go to the X 64 installer here, which is an EXC file. When I click this link, the download will begin. After the installer has finished downloading, let's go ahead and open it up. All right, we can click Next here. Then we can choose where we want to install the JDK. You'll probably want to leave yours on the default, but I like to install things in the D drive, so I'll change the location for mine. All right? Now if we click next, the installation will begin. Once it's finished installing, we can close this out. Now if we open up the command prompt again and try the Java version command, if it didn't work previously, it should now show us the version of the JDK we just installed. And now we should be good to go with the JDK. See you in the next video. 3. Install IntelliJ IDEA: In this course, we're going to be using the Intellij Idea IDE for writing the code and running our projects. IntelliJ IDEA is an integrated development environment or IDE, which is a software application that is designed to help computer programmers write code more efficiently. An IDE typically consists of a source code editor, a compiler or interpreter, and a debugger. The source code editor usually provides features that help with writing code more accurately and efficiently, such as by displaying different parts of the code in different colors for easier reading, auto completing code so that the developer doesn't have to type out common code over and over and highlighting errors to make it much easier to fix mistakes as they occur. The compiler or interpreter converts the code into executable instructions that can be run on a computer or device, and the debugger allows the developer to go through the code step by step and find and fix any errors. In tele J idea developed by JetBrains, is the most popular IDE for the Java and CotlanPgramming languages and is widely known for its ease of use. There are other popular IDEs, such as Eclipse and Android Studio, which can be used to develop the projects in this course. However, unless you are already very familiar with one of these, I highly recommend using a tela J when following along with the course in order to avoid running into any issues. To install IntelliJ IDEA in our Internet browser, we need to head over tojrains.com slashA. On this page, we can learn much more about IntelliJ. If we click the download button here at the top, it will take us to a page where we can download IntellaJ. At the top here, we can choose a download for either Windows, MacOS or Linux. I'm using a Windows PC, so I'll download the Windows version. At the time of recording this video, the current version of IntelliJ is 2022 0.3 0.2 released in January of 2023. I recommend using this version or a later version throughout the course in order to avoid running into any bugs that might have existed in the previous versions. Okay? And over here, we can see that there are actually two different IntelliJ editions, Ultimate and community edition. Ultimate is paid software, and the community edition is completely free and open source. We can see the differences between the editions here. For this course, we'll only be needing the features of the community edition, so that's what I'll choose. Okay? And for each edition, we can also choose what type of file we want to download for the installer. I'll just go with the normal EXC version and click Download here, which brings us to this page, and the download starts automatically. All right, after the setup file has finished downloading, let's go ahead and open it up. And here we can click Next. Then we can choose an install location for Antilla J. Feel free to use whatever you want, but I'll change mind to use the Drive. Now we can click Next. And here we have various installation options. First, we can choose whether we want to create a desktop shortcut for the application. I'll check mine. Next, we have this ad open folder as project option. If we check this, we'll be able to right click a folder on our computer and get the option to import the folder into IntelliJ as a project. We won't be needing this for this course, but it's pretty convenient, so I'll check mine. Next, we can choose to associate certain files with IntelliJ. This will allow us to double click a file that has one of these extensions and have it open up in IntellaJ. This also wasn't necessary, but I'll check dot Java here. Finally, we can choose add IntelliJ as Bn folder to the path variable and our computers environment variables. This is useful if we want to be able to access IntellaJthrough the command line. We won't be needing to do so in this course, but I'll check mine as it might come in handy in the future. A so in order for this to take effect, we'll have to restart our computer. Okay? Now we can click next. Then we can choose to create a folder for IntellllaJ in the Start menu, which is fine. So finally, we can click Install and give it a few minutes to finish installing. All right, after IntellaJ is finished installing, if we had checked the Add to path option in the setup, it would now ask us if we want to restart our computer. This is only necessary. We want to be able to use IntellaJthrough the command line, so I'll choose to manually restart later, but feel free to restart yours now if you like. Okay, now I'll click Finish. Then we can run IntellaJ either by using the desktop shortcut or by going into the installation folder, then the bin folder, and opening up the application file here. It might first ask us, if you want to import settings from a previous version of IntellaJ. I don't want to do this, so I'll leave it on Do Not Import and click Okay. Then it brings us to the welcome screen. From here, we can create a new project by clicking New Project. In the new project dialogue, we can give the project a name. It doesn't matter what we use here as this is just for demonstration, so I'll just call it MPject. Next, we can choose a location for where we want to save the project. I'll just leave mine on the default. Then we can choose the programming language for the project. Let's keep it on Java. We next get some options for the build system. This isn't important right now, so let's leave it on ItellaJ. Finally, we can choose the JDK version that we want to use for developing the project. If we install the JDK correctly in the previous video or if we already had a JDK version installed, we should see it in this box here. If we click this, it should show us any other JDK versions we might have installed, as well as give us the option to download other versions. All right, so with the JDK chosen here, we can now create the project by clicking the create button down here. KanaoPject is up and running. Now we just need to wait for it to finish indexing the JDK. When that's finished, we can go into the source folder here and open up our main class file, which contains the public static void main method that all executable Java classes require. The default code inside this method simply prints out the text hello world. And just to make sure it works, we can right click the main class in here and choose runmindt Min. And after it builds the project, the run dialogue should pop up, and it should say hello world in here. Awesome. That means we have it correctly installed IntlaJ and the JDK. Alright, I'll go ahead and close up this project by going to File, Close project. I'll see you in the next video. 4. Set a Java Home Environment Variable: After installing IntlaJ and the JDK, one more thing we need to do is set a Java home environment variable on our system that points to the download location of the JDK. This step will make things much easier when we start creating LibGDX projects. To do this, we first need to get the download location of the JDK. In the ItellaJ welcome screen, if we have projects selected here on the left, we should now see the project here that we created in the previous video. Let's click the project to open it up again. Now we want to go up to file the project structure. And here on our platform settings on the left, we want to choose SDKs, and now we can see the download location of the JDK here. Let's select the location and press Control C to copy it. Now we can close this out and close out of Manta J. Okay, so setting environment variables is going to be different depending on your operating system. I currently only have access to a Windows system, so I unfortunately can't show how to do it on a Mac or Linux system. However, if you do a search on how to set the Java Home environment variable for your operating system, you should be able to easily find instructions for it, and it should be a pretty simple process. Okay, so if you're using Windows like I am to set a Java Home environment variable, we first want to click in the search box at the left of the task bar, and if we start typing environment at the top, we should see a result that says, Edit the system environment variables. If we click this, it brings up the system properties dialog. And now we want to click the Environment Variables button here at the bottom. In the system variables list here, we first want to check if we have a variable called Java underscore home in all caps. If we do, as long as the value is set to the location of a JDK between versions 11 and 18, we should be fine. If we don't already have a Java Home variable, we need to create one by first clicking the new button here. For the variable name, we want to put Java underscore home in all caps, and for the variable value, let's press Control V to paste the copy JDK download path. We can also browse to location if we want. All right, let's click Okay. Now we should see our new Java Home variable in the list here with the value set to the JDK location. Also, if you did already have a Java Home variable, but it wasn't set to a correct version, you could choose the variable on the list and click Edit here, then paste the copied location into the value box and press Okay. All right after adding the Java Home variable, we now need to find the path variable in the list here. When we find it, let's select it and click the edit button. If we already had a Java Home variable, we might also already have an item in here labeled percent sine Java underscore home, percent sine backslash Bn. If not, we'll need to create one by clicking New up here. Then typing percent sine Java underscore home in all caps, then percent sine backslash BIN and press Center. All right, now we can click Okay here and here and here, now we should be ready to go. See the next video. 5. What Is LibGDX?: LibGDX is a game development framework, which is a collection of libraries and tools that provide building blocks that developers can use to create their own games. This is different from a game engine like Unity three D or Unreal Engine, which provide a UI for dragging and dropping elements into a game and often come with a ton of extra functionalities that you may never need to use. Game development frameworks tend to be much more lightweight and flexible than game engines. LibGDX is also free and open source, meaning that you can make as many games as you want with it and never have to worry about purchasing a license. It also means that many developers have created and are still creating various extensions that can be added to LibGDx to make it just as functional as the most powerful game engines. Another feature of LibGDX is that it offers cross platform deployment, meaning that a game created with LibGDx can be run on multiple platforms, including Windows, Mac, Linux, Android, IOS, and web browsers. Although we'll be focusing on creating two D games in this course, LibGDX is also fully capable of handling three D game development, and you can find many examples online of amazing three D games that have been created with LibGDX. Finally, LibGDx is written in the Java programming language, which is what we'll be using in this course, but it's also possible to use it with other programming languages like Kotlin and Scala. Okay, now that we know what LibGDXs in the next video, we'll learn how to start using it. See you. 6. Setting Up LibGDX: To start using LibGDX, we first need to create a LBGDxPject then import the project into Antilla J. And to create a LBGDXPject, we use the LibGDxPject Generator. To find the project generator, you need to go to the LibGDX website, which is located at libgdx.com. On this page, we can see some examples of games that were created using LibGDX and we can read more about how it works. Here we can see that at the time of recording this video, the current version of LibGDX is 1.11 0.0. I recommend using at least this version throughout the course to avoid running into any issues. Lack up at the top of the page, if you click the Get Started button here, then click the Set Up a Project button on the next page. It takes us to a Wiki with information about how to get started, creating our first LBGDXPject. We already set up our Dev environment earlier, so we want to move over to the Generate aPject section by clicking this button. Then we can download the project generator by clicking the Download button here. The project generator file is called gdxsetup dot jar. It doesn't matter where we put the file, but just be sure to put in a location that you will easily remember, as we will need to use the file every time we want to create a new LBGDXPject. I simply created a folder in my Drive called LibGDx and put the file inside it. I also credit a folder in here called Projects, which is where I'll be placing all the projects that we create in this course. Anyway, as I mentioned before, this file has the dot Jar file extension, which stands for Java Archive. And to be more specific, this file is an executable jar file, which can be run directly through Java. This means that if we did everything correctly when installing the JDK earlier, we should be able to simply double click the file to run it. If double clicking it doesn't work for you, we can also run the file through the command line by using the command Java jar GDX setup dot jar. Okay, now we have the project generator up and running. And here we can give our game a name. We're just going to use this project as a test to make sure everything works. So let's just call it test game. Next, we can give the project a package name. It doesn't matter too much what we put here, but if you're creating a project for a particular company such as your own, the convention is to put the Internet, the main name of the company in reverse, followed by the project name. I'm just going to put com dot B Grant for my name the dot TestGame. And by the way, we can't use spaces or uppercase letters for the package name. Okay, next, we have game class. This will be the name used for the main Java class file of our game. I'll simply use the project name but with no spaces. Next, we can choose where to put all the project files. I'm going to browse to the projects folder that I created inside the LBGDXFolder. Now I'll right click in here and choose New Folder. I'll name the folder test game and click the open button. Okay, under the output folder, we have Android SDK. If you have the Android Studio development kit installed on your system, it will likely show the location of it here. However, we won't be using Android for this project, so we can ignore this for now. Next, we have options for which platforms you want our project to run on, including desktop, Android, IOS, and HTMO. For this project, we only want to run it on the desktop, so let's uncheck all but the desktop option. Finally, we have various extensions that we can use in our project. We'll talk about some of these later in the course, but as this is just a test project, we won't be needing any of them right now, so let's go ahead and click the Generate button here. This will generate all the project files for us. And if everything works correctly, it should only take about a minute. Okay, it says, Build successful and shows us how to import our project into various IDEs. We can close out the project generator now, and in the next video, we'll import the project into Antilla J and learn about the basics of LGDx. See there. 7. LibGDX Basics: Now that we have our test game project created, we can input it into IntellaJ from the welcome screen by clicking the open button at the top. Then browsing to the location of the project we just created, which in my case is D, Lib gDxPject TestGame. Now we want to have the project folder selected and click Okay. It warns us that the project may contain malicious code, but we're not worried about that, so we can click Trust Project. It will then start building the project, which may take a couple of minutes. As long as there are no problems with the project build, we have successfully created and imported our first LibGDX project. Let's check out what we have here. First, in the project panel at the top left, we want to have projects selected here, and now let's expand the test game folder. The folders that we're mainly concerned with in here are assets, core and desktop. And the assets folder is where we put all of the assets for our game, which includes things like images and sounds. By default, we get this bad logic dot JPG image included in the Assets folder. We can double click the image to view it. Bad logic is the name of the company that created LibGDX, and this is just an image they include in every default LibGDX project for testing purposes. We can close out the image. Let's skip the core folder for now and check out the desktop folder. The desktop folder is where we place all of the code that is only relevant to running the game on the desktop. If while generating the project, we had chosen any of the other platforms like Android or IOS, we would also have separate folders for those platforms. Anyway, if we expand the source folder here, we see the package name that we chosen the project generator, and inside it, we have a desktop launcher class file. Let's double click it and check out the source code. At the top of the desktop launcher file, we have the package name for our project and some imported classes and packages. And here we have the start of our desktop launcher class. It's quite a simple class consisting of only a main method and a few lines of code. The first line here instantiates an LWJGL three application configuration object called Config. LWJGL is a Java library that allows us to create a BGDX window on the desktop, this class here, which I won't try to say again, is used for configuring various settings of the window. For example, the next line, config dot set foreground FPS 60 sets the target frame rate of the application to 60 FPS or frames per second. The frame rate is how often the contents in the window are refreshed, and 60 fps is pretty much the standard for most video games. Next config dot set title is used to set the title of the window. The title is only displayed if the window isn't full screen, and by default, it's set to whatever we use as a name for our project and the project generator. We can change the title to something else if we want. Finally, the last line instance is an LWJGL three application object using an instance of our main game class and the Gafik object we created. The main game class, which in my case, is called Test Game is located in the core folder. But before we check it out, let's go ahead and run the desktop launcher. This is actually the only runable file in our project, and to run it, we can right click the file on the list here and choose run Desktop launcher dot Min. If it runs correctly, we get a small square window with a red background and the bad logic of JPG image at the bottom left. We can also see that the title of the window is whatever we put in the set title method for the C fake object. Okay, we can close this out now. Let's check out the core folder. The core folder is where we put all of the platform independent code, and it's where we'll be spending 99% of our time working on the project. In the source folder, we have a package name again, and in the package, we have the test Game class, which is our main game class, and what the final line of the desktop launcher creates an instance of and places inside the window. Let's double click the file and check out the code. At the top of the file, we again have our package name and some imports, then the start of our main game class. This class extends application adapter, which is required in order for the desktop launcher to run it. Inside the class, we first have two variables, a sprite batch object called batch and a texture object called IMG. It's important to have a sprite batch object in each of our LibGDx projects, as is basically what we use to draw all of our textures to the screen, and the texture class is used to decode an image file and load it into GPU memory. Next, we have three methods that any class which extends application adapter must override. These include create, render and dispose. Create is the first method that gets called when the class is loaded, so it's where we should instantiate all of our variables. So in here, we instantiate our sprite batch object and our texture object. As you can see with the texture object, we pass in the location of the image we want to decode, which in this case is bad logic dot JPG. As we saw earlier, this image is located in the assets folder, and it's the smiley face image we saw when we ran the desktop blancher. Also note that we don't have to use assetlash Badogic dot JPG for the location because LibidX already knows to look inside the assets folder for all assets. Okay, the next method is render. This method is called at the beginning of every frame in our game. Because of this, we don't want to create any objects inside this method, because the Java garbage collection will have to deal with possibly thousands of created objects at a time, which can cause the game to skip frames and become jumpy. The first line we have is screen utils dot Clear, followed by four float values. The screen utils class contains static helper methods for dealing with different aspects of the game screen, and the clear method is used to clear the screen of all textures and set it to a single color. The four float values are the RGBA values of the color that we want to set the screen to. RGBA stands for red, green, blue, and Alpha, with the minimum value for each one being zero and the max being one. So the values of one for red, zero for green, zero for blue, and one for Alpha, we get a fully opaque red screen. Alpha, by the way, deals with transparency. One is fully opaque and zero is fully transparent. Next, we have three lines that make calls to methods inside our sprite batch. The first one batch up again is required before we use the sprite batch to do any drawing, and it lets the GPU know that we're ready to start drawing. We then have a call to Batch Dot draw in which we pass in the texture that we want to draw, followed by two floats, which are the X and Y coordinates of the point on the game screen, where we want the texture to begin drawing. In Lib GDX, the 0.00 is located at the bottom left, which is why when we ran the desktop launcher earlier, we saw the image drawn at the bottom left of the window. And finally, we have Batch Dot end. Let's let the GPU know that we're finished drawing and it's required after all calls to Batch Dot draw. If we had any other textures that we wanted to draw, we would make a call to Batch Dot draw for each of them. The final method in the class is disposed. This method gets called when we close out our game, and it's where we release certain resources that our game uses so that they will be removed from the GPU memory. As you can see, we need to call the disposed method for the sprite batch and for any textures that we create. When we create our main project later, we'll use something called an asset manager to manage all of our assets. With an asset manager, we'll be able to dispose all of our textures at once, so we won't have to worry about calling the disposed method for every single one of them here. Okay, that's it for the test game class. But before we move on, let's run the desktop launcher again. And actually, when we ran the desktop launcher the first time, InteleJ automatically created a run configuration for us using the desktop launcher, which we can see up here. So now we can simply click the Run button next to this to run the desktop launcher. So as we saw before, we have a red background with the image at the bottom left. If we close this out, we can try changing the background color to something else. I'll change the red value to zero. Now with values of zero for red, green, and blue, it will give me a black background. If we use all ones, it will give us white. And we can use different combinations of values for various colors. We can also use different X and Y values in the code and Batch Dot draw to change the location of the image. We can use negative numbers as well, which would draw the image at least partially off screen. If you wanted to put the image at the top right of the screen, we'll need to get the width and height of the screen. To do this, we can delete the X and Y values. Let's type GDX with a G. We'll need to import this class by pressing Enter. Then we can type dot graphics dot G width to get the width of the screen. Then type a comma. And for the Y value, we can type GDX dot Graphics dog height. Now if we run this, we of course won't be able to see the image because it starts drawing from the bottom left pixel of the image, so it's actually up here just off screen. We can close this out and subtract some pixels from the X and Y values in order to see the image. If you wanted to start drawing the image at the center of the screen, we can divide these values by two. And we need to put an F after the twos so that the result remains afloat. If you wanted to center the image on the screen, we also need to subtract half of the image's width from the X coordinate and half of its height from the Y coordinate. To do this, for the X coordinate, we can do Gdx dot graphics dot Get Wi, divided by f minus mg dot get Wi, divided by f. And for the Y coordinate, GDX dot Graphics dot G height, divided by F minus ImgtGt height divided by two f. There we go. All right, and that's pretty much it for a very basic LBGDX project. In the next section, we're going to learn a whole lot more about LibGDX by creating our main projects. Stick figure showdown. See there. 8. Create & Import the Project: To create our main project, we first need to go back to the location where we saved our LBGDXPject generator and open it up again. I'm going to name the project stick figure Showdown. For the package name, I'll go with comb grant SFS. And for the game class, I'll go with SFS and all caps. For the output folder, I'll browse to my LBGDXPjects folder. Create a new folder in here called Stick Figure Showdown. And click Open. Now, to keep things simple, we're going to focus on creating games for the desktop in this course because I know not everyone has access to Android or IOS devices. However, at the end of the course, there will be an optional bonus section in which we will learn how to add Android functionality to our project. So feel free to check that out after finishing this section of the course. For now, though, let's uncheck it here. Unfortunately, in order to test our game on IOS, an IOS device is required, but I don't currently have access to one. Therefore, at least for the moment, I won't be including a section in this course regarding IOS. I may, however, be able to do so in the future. For now let's uncheck it. An HTML doesn't support everything that we'll be including in our project, so we can uncheck it, as well. Next, we have some official extensions, as well as some third party extensions, which we can see by clicking this button. These provide a whole lot of extra functionality that we can include in our games. For example, free type gives us a lot of control over the typography in our games compared to what we can do with the standard LGDX library. Box two D here gives us advanced collision detection functionality, and AI gives us advanced artificial intelligence functionality. The only extension we'll be needing for our project is free type. So let's go ahead and check it. I'm planning to make another LibGDX course in the near future, in which I will cover many if not all of these extensions. Okay, now that we have everything set up the way we want it, let's click Generate and let it do its thing. When the project has finished generating, we can open up and tell a J and import the project by going to file open, browsing to the stick figure showdown folder, and clicking Okay. Let's click Trust Project, and we don't need our test game project open anymore, so we can choose this window. When the project has finished building, let's go over to the desktop folder, then source. Then right, click Desktop Launcher and choose Rundsktop launcher dot M, just to make sure everything is working correctly so far. Alright, the demo game seems to be working fine, so we can close this out. And in the next video, we'll start replacing the demo code with our own code. See you there. 9. Import & Manage the Assets: In this video, we'll import all of the assets we'll need into our project. Then we'll set up an asset manager, which we can use to manage all of our assets in one place. First, if we unzip the resources dot zip file that I included with this course and take a look inside, one of the folders we have in here is this assets folder. This folder contains all the assets we'll need for our project, including audio files, fonts, and textures. To import them all into our project, we simply want to select all the folders in here, right click them and choose Copy. Then go to our project and Intel a J, right click the assets folder, choose paste. Then click Okay here. And now we have all of the assets imported into our project. We won't be needing the bad logic image anymore. So we can right, click it and choose Delete. Then click Okay here. Next, in order to start using the assets in our project, we need to load them all into memory. We'll do this with an asset manager. Let's first go into the core folder, then source. Then let's right click our project package here and go to new then package. At the end of this, let's type resources. Then Press Center. And this package is where we'll put all the resource classes that we'll create throughout the course. The resource classes will mostly consist of managers for things like audio, game settings, and assets. For the moment, we just want to create an asset manager. So let's right, click the Resources package and go to New Java class. Let's name the class assets with a A and Press Center. Okay, so the first thing we need to create in the assets class is the actual asset manager. I'm going to put a comment here that says asset manager. Now on the next line, let's type public final asset manager. Press Enter to import the class. Then manager equals new asset manager. Open and close parentheses. We want it to be public so that we can access it from classes outside of the package. Next, we want to create some static final string variables that point to the locations of all the assets in our project, so that we can easily access them throughout the project without having to memorize all the locations. First, let's create variables for all the textures that we'll use and the actual gameplay. If we go to the textures Assets folder, the gameplay assets include background dot png, blood dot Atlas, front ropes dot PNG, and gameplay buttons dot Atlas. We'll discuss what Atlas files are a bit later. The remaining gameplay assets are located in the Sprites folder. These are sprite sheets for all the animations that our fighters will perform, like the block animation, the hurt animation, and the idle animation. So back in the assets class, I'll go down a couple of lines here. Put a comment for gameplay assets. And first I'll do public static final string. Background underscored texture equals textures forglash background dot png. And because I capitalize the B in the name of the image file, I want to make sure I use at B here as well. Okay, I'm going to continue creating variables for the remaining gameplay assets. I'll skip through this so the video won't be ridiculously long. Okay, here are all of the variables for the gameplay assets. If you're following along, please pause the video now and copy these into your class file. Next I'll do the same for the font assets. If you open the fonts folder, we only have one file here, Roboto regular TTF. However, we're going to create several fonts in our project using this file. Some will be large, some small, and some will have borders around the letters. First, I'll put a comment here for fonts. Then I'll type public static final string, Roboto regular equals fonts Roboto regular TTF. On the next line, I'll do public static final string. Small font, equals small font dot TTF. I'm going to press Control D to duplicate this line and change small to medium. I'll duplicate again and change this one to Large. As we'll see later, it doesn't matter that these three files don't actually exist yet. For the next section, I'll create variables for the audio assets. These are all of the assets inside the audio folder. First I'll put a comment here for audio assets. Then I'll start with the block sound by typing public static final string block sound. Equals audios Block dot NP three. Then I'll continue creating variables for the remaining audio assets. Here are all of the variables for the audio assets. Be sure to pause the video now and copy these lines into your class file. And notice that for the music variable, I didn't add underscore sound to the name. That's because we'll be handling the music file a bit differently than the others. Also, be sure to use dot Ogg as the extension instead of dotNP three like the others. Finally, I'll create variables for the menu assets. We only need to create one variable for the menu assets. This menu items dot Atlas file here. So I type public static final string. Menu items Atlas. Equals textures slash Menu items dot AtlaS. You might have also noticed that we have a mobile i dot Alas file, which we didn't create a variable for. That's because we'll only need this asset for the bonus Android section at the end of the course. We'll create a variable for it in that section. Next, we're going to create some methods that will load all of the assets into memory. First, let's create a public void load method. We'll call this method from the main game class. Eventually, we're going to create methods for loading each of the categories of assets. But since we'll only be needing the gameplay assets for the next few videos, we'll just create a method for loading the gameplay assets for now. First I'll put a comment here that says Load all assets. Then let's call the method load gameplay assets. Now below the load method, let's spe private void load gameplay assets. In this method, we'll load each of our gameplay assets. To do this, we type manager, which is the asset manager we created earlier. Then dot load open parentheses, and now we type the location of the asset we want to load. So we can first use our background texture variable here, then and now we need to tell which type of asset we're loading. This is going to be a texture asset. So we can type texture with a T, press Enter to import the class, and it might add dot class here for us. If it doesn't, we'll need to type it ourselves. Now we can close off this line. I'm now going to do this for front ropes texture and all of the sprite sheets. And I'll stop at Gameplay Buttons Atlas. Okay, here's all the code to load the gameplay texture assets. For gameplaybtons dot Atlas here, let's type manager dot load, Gameplay Buttons Atlas. Because this is a texture atlas, not a normal texture, we need to type texture atlas, pres Enter, then dot class. Now let's do the same for blood Atlas. Manager dot Load, Blood Atlas, texture atlas dot class. Okay, that's all we need to load at the moment. The final thing we want to create in this class for now is the dispose method to dispose all of our assets, removing them from memory. Let's put the method at the bottom of the class. Public void dispose inside this method, we simply need to type manager dot dispose. With this, the asset manager will take care of all of the disposing for us. Now, we need to actually create an assets object in our main game class and called the load method. Let's head over to the core folder and double click the SFS class file to open it. We won't be needing the bad logic texture anymore, so we can delete the texture IMG line at the top, the initialization of the texture in here, the call to Batch dot draw here, and the mg dot dispose line here. I'm also going to press Control A to O to remove the unused texture class import. I'll press Control A to L to reformat all of the code so that everything looks cleaner. I'll be using these two shortcuts throughout the course, and it's a good idea to get into the habit of using them yourself after adding a lot of code or making a lot of changes. Okay, now back at the top under Sprite Batch batch. Let's type assets with a A, press Enter to import it, then assets with the lower KsA. And we actually want to make both the Sprite batch object and the assets object public, as we'll be using them from various classes. Next in the create method, after the batch initialization, let's put assets equals new assets. Now we have our asset manager initialized and ready to go. Now we want to call the assets manager load method to load all the assets. I'll go down a couple lines and put a comment that says load all assets. Then let's type assets dot Load. If you go back to the assets class really quick, when we make a call to manager dot Load, it actually puts the given asset in a queue and the assets are loaded asynchronously, meaning that other parts of our code are able to continue running as the assets are loading. We're also able to check the loading progress to see how many of the assets have actually been loaded. This is another advantage of using an asset manager, and we'll put this feature to use later in the course when we create a loading screen with a progress bar to show us how many of the assets have been loaded. Until then, however, we want to make sure that all of the assets have been loaded before we continue running the game. This will prevent us from trying to use an asset like a texture or audio file that hasn't yet been loaded, which will cause the game to crash. To make sure all of the assets are loaded before continuing, we can go back to our main game class. And after the assets Dow load line, we can type assets, Dot manager, dow finished loading. This method will block all other code in our game from running until all the assets have finished loading. This is just a temporary solution until we create the loading screen a bit later. The final thing we want to do in this video is go to the dispose method, and after batch start to dispose, that's called assets Dot dispose. Now, when we close out our game, all the assets will be disposed of and removed from memory so that no memory leaks will occur. Alright with all of that boring stuff out of the way, in the next video, we actually start putting some textures on the screen. See there. 10. Create the Game Screen: I in our game, we're going to have multiple different screens, including a loading screen, a setting screen, a main menu screen, and a game screen where we'll actually play the game. We're going to be able to switch back and forth between these screens. In this video, we're going to start creating the game screen so that we can actually start playing the game and we'll worry about the other screens later. To start, let's go over to our project package in the core folder, right click it and go to New package. Let's call it screens and press Enter. This package is where we'll place all of our screen classes. Let's right click the screens package and go to New Java class. Let's call it game screen. I press Center. Now, each screen class we create needs to implement a class called screen. So after public class game screen here, let's type Implements screen. And press Enter to import the screen class. Implementing the screen class requires that we implement a few methods. To do this easily, we can hover over this line until this dialogue pops up. Click Implement methods here. Then click Okay. Now we have overridden all of the methods that we need to implement. We don't have to actually put anything inside all of them. They just have to exist inside our class. We actually also need to create a constructor for our class, which will be called when we create a game screen object in our main game class later on. So at the top of the class, let's type public game screen, open parenthesis. And we want to pass an a pointer to our main game class here, let's type SFS, press Enter to import it, then game, and finish creating the method. Above this method, let's create a private final SFS game variable. And inside the constructor, let's type this dot Game equals game. We'll be using this variable throughout our game screen class. Next, we want to get our game area textures from the asset manager so that we can render them to the screen. If you look inside the textures folder here, the game area textures are background dot PNG and front ropes dot PNG. If you open front ropes dot PNG, it's basically just a copy of the front ropes of the fighting ring and background dot PNG. Just to clear up any possible confusion, the reason we have a separate image for the front rings is that the first texture we're going to draw is the background. Then we'll draw the fighters so that the fighters will appear on top of the background. However, if the fighters move down here, they're also going to appear on top of the front ropes, which won't look right. To fix this, we'll draw the front ropes image on top of the fighters. Anyway, that's not too important at the moment, so let's get back to getting the game area textures from the asset manager. First, after creating the game variable at the top of the class, let's go down a couple of lines, and I'll put a comment here that says background Ring. And this section is where we'll put all variables related to the background and fighting ring. Now let's type private texture. Press Ender to import it the background texture. Let's also create one for the front ropes. Private texture, front ropes texture. Next, under the constructor. Let's create a private void create game area method. And here we'll get the game area textures from the asset manager. First dot comment, get the ring textures from the asset manager. Then we can type background texture equals. And now we'll use our game variable, then dot assets dot manager dot Git. We now want to pass in the location of the background texture, which we can do easily by typing assets, pressing Enter to import it, then dot background texture. This is the public static final string variable we created earlier in the assets class, which points to the location of the background texture. Now let us do this for the front ropes texture. Front ropes texture equals game dot assets dot manager dot Git, assets dot Front ropes texture. Now back inside the constructor, I'll comment, create the game area. Then make a call to the create game area method. Next, we'll actually draw or render the background texture to the screen. We'll do this in the render method here. First, as we learned when we went through the demo project, we want to clear the screen of any previous drawings. This is important because when we start animating our fighters later, if we don't clear the screen every frame, all the previously rendered animation frames remain on the screen, causing everything to look like a jumbled mess. So to clear the screen, let's type screen Utils. Press Ener to import it, then dot Clear, open Perthess. Let's clear the screen to black by typing 000 com one. Now we'll render the background texture. If you recall from the demo project, we did this with a call to the draw method of the Sprite Batch, and all calls to draw must come between calls to the Sprite batches begin and end methods. It's a good practice to only use a single sprite batch for the entire game, so we're going to use the sprite batch we created in our main game class. First I'll comment, begin drawing. Then let's call game dot batch dot begin. Now I'll go down a couple lines and draw the background. Then let's call game dot batch dot draw. Now we want to pass in our background texture variable followed by the location where we want to start drawing the texture. Let's go to 00. Then let's go down a couple of lines, and I'll comment n drawing. Then type game dot batch dot end. Okay? Now, in order to start using our game screen class, we need to create a game screen object in our main game class then switch the screen to the game screen. So let's head back over to our main game class. First, before we can switch between screens using this class, instead of extending application adapter, this class needs to extend a class called game. So let's replace application adapter with game and press Center. We can press Control A O to remove the application adapter inpuort. The game class actually implements the application listener class, which is the same class the application adapter implements. This means that we still need to override the create, render, and dispose methods. However, we won't actually be doing any drawing in the render method of this class. Instead, we need to replace all of the code in the render method with superdtRnder. Super refers to the class we're extending from, which is game. So this calls the render method of the game class. I'm not sure exactly what the game classes render method does, but it's important that we make this call here. Now at the top, under the creation of the assets variable, I'll comment screens. Then let's type public game screen. Press Enter, then game screen. Now in the create method, after we load all of the assets, I'll type initialize the game screen and switch to it. Then this initialized game screen by typing game screen equals new game screen pass in this class by typing this. Then to switch to the game screen, we can type set screen and pass in our game screen variable. Set screen is a method that is provided by the game class. Okay, now we can go up here and run the desktop launcher. And if everything works correctly, we should see our background texture on the screen. But was see is the bottom left corner of it. So in the next video, we'll see how we can change the default size of our game window as well as how to show the entire background texture. See there. 11. Window Size, Camera & World Units: We saw in the previous video that when we run the desktop launcher, we see is a small portion of our background image. Also, if we change the aspect ratio of the window, the image becomes warped. Over these next couple of videos, we'll fix both of these issues. But first, the default window size is a bit too small, so let's make it bigger. To do this, let's go into the desktop folder and open up desktop launcher. And here, somewhere between the initialization of config and the last line, let's add another line. And let's call Config, that set windowed mode. Then pass in 804 80. Windowed mode is actually what we've been using already, as it puts our application inside a movable and resizable window that doesn't take up the entire computer screen. Later on, we'll learn how to switch to four screen mode. However, if we manually call this set Window and mode method, we're able to pass in pixel values for the width and height that we want the window to be instead of using LibGDx as default values. So now our window will start out as 800 pixels by 480 pixels. Let's go ahead and run desktop launcher again to see if it works. There we go. We still can't see all of the background image, though and to fix this issue, we're going to set up a camera. Cameras and LibGDX determine what the player can see in the game. They give us the ability to zoom in and out and follow the player as they move around in the game. In Lib GDX, we have two types of cameras perspective camera and orthographic camera. Perspective cameras more closely mimic how the human eye sees as they make objects appear bigger or smaller, depending on how far away they are from the viewer. Perspective cameras are used by most three D games. Orthographic cameras, on the other hand, don't take depth into account, so objects will appear the same size regardless of how far they are away. This is most common in two D games like side scrollers and top down games. Because the game we're making is two D, an orthographic camera is what we'll use. To set up an orthographic camera, let's go back over to our game screen class. At the top, after creating the SFS game variable, let's type private final orthographic camera. Press Enger to import it and camera. Now in the constructor, before creating the game area, I'll comment, set up the camera. Then let's initialize the camera by typing camera equals new orthographic camera. And now we need to pass in the width and height of the area that we want the camera to show. For now, let's use the width and height of the window by typing GDX, pressing Enter, then do graphics, do get width, GDX, DGraphics do get height. Then close off the line. We next have to tell the sprite batch to use our camera when rendering. To do this, let's go to the render method. And before the call to game Dow batch do begin, I'll comment set the sprite batch to use the camera. Then type game dot Batch D set projection matrix, and pass in camera dot combined. It's not important to know exactly what this method does, but it basically uses information from the camera to transform everything as it gets rendered to the screen. Okay, let's go ahead and run the desktop launcher and see what we get? So a problem we have now is that the texture starts at the center of the screen instead of at the bottom left. And the reason for this is that the camera is located at position 00, but the camera's lens is actually at the center of the camera. Therefore, when using a camera, 00 is now at the center of the window instead of at the bottom left. To go back to having 00 at the bottom left, we simply need to move the camera to the right by half the width of his viewport and up by half the height of his viewport. A camera's viewport, by the way, is all the area that the camera is showing. To move the camera after we initialize, we can make a call to camera dot translate the pass in camera D viewport width divided by two F, coma camera that viewport height divided by two F. And in order for this to go into effect, we also need to make a call to camera dot update. If we run Desktop launcher now, the texture starts at the bottom left again. However, we still have the problem of not displaying the entire texture. In fact, it's only showing the bottom left, quarter of the image. And the reason for this is that I actually created all the textures at twice the size that we need. I did this because it helps to minimize loss of quality on larger screens. If we go into the textures assets folder and open background dot PNG, we can see at the top right here that the image is 1,600 by 960 pixels. This is twice the size of our game windows default size, which we set in the desktop launcher to be 800 by 480 pixels. So if we want to show the entire background texture in the game window, you need to draw the texture at half its width and height. To do this, in the render method of the game screen class, where we draw the background texture, we can use another version of the draw method, which lets us pass in the width and height of the texture. So after the Y value, we can put background texture Get width. And we want to draw it at half the width, so we can put either divided by two F or times 0.5 F. Then we can do the same for the height. Now when we run the desk top blancher, we see the entire texture in the window, but we still have one problem. That's if we change the aspect ratio of the window, the image gets warped. We'll fix this in the next video, where we will create a viewport object to handle our camera's viewport and have more control over how things appear on the screen. But before we do that, let's talk about something called world units. So far, with our camera, we've been working in screen units or pixels by passing in the width and height of the window into the camera. This is okay right now, but using large numbers of pixels can get pretty confusing and tedious when we start moving things around in our game. Doing it this way also forces us to think about things for a particular screen resolution. However, we're planning to use our game across many different resolutions. Therefore, it's much better to think in terms of world units of some kind. We can think of world units as feet or meters or inches or any other unit we want to use. What I like to do is take the desired within height I want from my game, divide them by ten, and use those numbers as the world units. In this game, the desired within height is 800 by 480, which is exactly half the size of the background image. So I'll take those numbers and divide them by ten, which gives me 80 by 48. I could even divide by 100 if I wanted to, giving me eight by 4.8. However, going too low will make it difficult to accurately place things inside the game. So I like to stick with dividing by ten. To start using world units, we need to replace the screen units that we pass into the camera with the world units we want to use. However, instead of typing the numbers directly in here, we're going to make them public static final variables in a different class, so that we'll be able to access them easily across our entire project. To create this new class, let's go to the resources package that we created earlier inside the core folder. Let's right click it and go to New Java class. Let's name it global variables. And press center. Throughout the course, we'll be placing variables in here that we'll need to access from multiple classes in our project. First, inside this class, I'll comment world. Then let's type public static final float. Rolled width equals ADF. Next let's do public static final float, rolled height. Equals 48 F. Let's actually also put the default within height of the window at the top here. I'll comment Window, then type public static final int. Window width equals 800 and public static final int, window height, equals 480. Now on desktop launcher, let's replace the values we put in the set windowed mode method with the variables we just created by typing global variables and pressing Enter, then dot window width, como global variables, that window height. Can I go back to Game screen and start using our role units. First, let's delete these parameters. We pass into the initialization of the camera. Then put in the world units by typing global variables, pressing Enter, then dot world width, como global variables, dot world height. Now let's run the desktop launcher and see what happens. All right, so now the camera's view port is way too small to display the entire background image. We already scaled down the texture by half in the draw method, but now we need to scale it down further by a tenth. Therefore, in total, we need to multiply the width and height of the texture by 0.5 times 0.1, which is 0.05. But we don't want to keep typing this number in every time we draw a texture, so let's create a variable for it in global variables. After the world height variable, let's type public static final float. World scale equals 0.05 F. Now back in game screen, we can replace these 0.5 values with global variable world scale. Now if we run the game, everything should be back like it was. However, as I mentioned before, we still have the problem of the image getting warped if we change the size of the screen. So we'll fix that using viewports in the next video. See you there. 12. Viewports: As we learned in the previous video, a camera's viewport is all of the area the camera shows on the screen. In this video, we'll see how we can control a camera's viewport using a viewport object. Among other things, this will allow us to fix the warping of textures that changing the Windows aspect ratio causes. LibGDX actually provides several different viewport classes we can use extend Viewport, fill viewport, fit view port, stretch viewport, and screen view port. We can even create our own custom viewports if we want, but that's a much more advanced topic. The type of viewport we use will depend on how we want the objects in our game to appear on the screen for different screen resolutions. Let's first take a look at stretch viewport. At the top of the game screen, after creating the camera, let's type private final stretch viewport. Press Enter to import it, then Viewport. Now in the constructor, we actually won't be needing these camera dot translate and camera dot update calls anymore. We'll see why in a bit. For now, let's remove the lines. Then type viewport equals new stretch viewport. And now we need to pass in the world units, so we can type global variables do World Width, global variables do World height. Next we can put a comma then pass in our camera objects. This will tell the viewport object to apply its information to the viewport of the camera we created. We can also remove the world units from the initialization of the camera as we're now doing this and the initialization of the viewport. Now, in order for our viewport to work correctly, we first need to scroll down here to the resize method that we had to override. This method is called every time the game window is resized, including when the window is first created. The width and height parameters here will contain the new width and height values of the window. Inside this method, we need to update our viewport with the windows new width and height. First I'll comment, update the viewport with the new screen size. To do this, we call viewport dot update with height. And next we can pass in an optional Boolean value. If we pass in true, it will center the camera on the viewport. This is what we want, so let's type true. This is also why we no longer need to move the camera manually in order to get it centered like we did with the call to the translate method previously. Now we can go ahead and run it and see what happens. Re slidee the window, you're probably thinking this seems exactly the same as it was before we added the viewport. You would be correct. It seems orthographic cameras by default, do use something similar to stretch viewport. What stretch viewport does is it assumes that the windows size always remains the same as what we pass in when we create the viewport and the viewport stretches to always take up the entire window. This will result in the textures getting stretched and warped if we change the windows aspect ratio. This could be useful for certain types of games, but it's definitely not what we want in our game, so let's check out the other viewports. Next, let's try Fit viewport. We can simply change stretch viewport here at the top to Fit viewport and we'll have to press Enter to import it. We also need to change it and initialization at the viewpoint. Now let's run it. Fit Viewport looks the same as Stretch Viewport at the moment, but if we change the aspect ratio of the window, we start getting black bars either on the sides or in the top and bottom. This is because like Stretch Viewport, Fit Viewport assumes that the window size remains the same, so we'll try to scale everything as much as possible in order to fit the entire window. But unlike stretch Viewport, it will maintain the aspect ratio of everything. This prevents the textures from warping, but it results in black bars if we change the windows aspect ratio a lot. And the black bars we see are actually the color we chose when clearing the screen. So if we want, we can go to the render method and change the color to, for example, red, now we will see red bars. Okay, let's change it back to black for now. For our purposes, Fit Viewport is better than stretch Viewport because we don't want our textures to become warped. However, we also want our game to look good on many different screens, and we don't want black bars to be surrounding everything. So let's move on to the next viewport, Fil Viewport. First, let's replace Fit Viewport with FieldView port here. Press Enter, and replace it here. Let's run it. Like Fit Viewport, fill viewport will maintain the aspect ratio of everything. However, fill view port will always fill the entire window. This prevents the black bars from appearing, but it also results in parts of textures being cut off. This definitely isn't what we want, so let's try the next viewport type, screen view port. Let's first replace Field view port with screen view port. With screen viewport, however, the only perimeter we can pass in is the camera object. Now, let's run it. So unlike the other viewport types, screen viewport only works with pixels, not world units. That's why our texture appears so small, because we scaled it down to world units when drawing it. If you want to work in pixels and you don't mind that a player with a bigger screen will see more of the game than a player with a smaller screen, this might be the viewport type you want. However, it's not what we want for this game, so let's talk about what we do want. First, let's open up the background dot png image. Basically, what we want is for the entire width of the background to be shown regardless of the player's screen size. This is because we don't want any parts of the ring to get cut off. We also want the player to be able to see from the bottom of the ring up to no lower than about this point. This is because particularly for mobile versions, we're going to have buttons at the bottom of the screen, and we don't want the buttons to be covering up the fighters. And at the top of the screen, we're going to have a HUD display showing the fighters life bars and a timer, and we don't want these to cover up the fighters, either. So we don't want to cut off too much of the top of the background image. This type of control requires the final viewport type, extend view port, which we'll check out now. First, let's close up the background image and replace screen view port in here with Extend viewport. Press center and replace it here. With Extend Viewport, before passing in the camera object, we can pass in a minimum world width and a minimum world height. That will make sure at least that much area of the game will be visible regardless of the screen size. We want to show the entire width of the world on the screen. So for a minimum world width, let's just use global variables dot World Width. For the minimum world height, after testing all of this out, I found that about 85% of the world height works pretty well. So let's go over to the Global Variables class, and after the world height variable. It's like public static final float. Min world height equals world height times 0.85 F. Now back in game screen, that's at the Min world height variable here. Now let's run it. I if we increase the width of the window, we can see that it will start to cut off some of the top of the background until it gets below 85% of the background height. At that point, we'll start to get a black bar here on the right. This is actually okay because very few screens are going to have a width that is this much larger than the height. However, the image is no longer centered, which we'll need to fix. But first, if we decrease the width a lot, we start to see some black at the top of the screen. This is okay, as well, because our background image is black here. This is why it's important that we clear the screen too black for this game. Okay, so let's now fix the problem with the background not being centered anymore if the window is too long. To do this, we also need to pass in maximum world width and height values to the viewport. Again, we want the entire world width to always be visible, so let's use the world width variable for the maximum world width as well. And we actually don't need a maximum world height, but we have to put something. So if we put a zero, it's the equivalent of telling the viewport that we don't care about our maximum world height. Now, let's give it a try. Okay, now if we increase the width of the window, the texture is vertically centered again. We might occasionally get a user who has a screen with a weird resolution that causes these black bars on the sides to appear, but it's going to be such a rare occurrence that we don't have to worry about it. All right? And if we increase the height of the window, we can see that the texture remains at the bottom. This is because we didn't provide a maximum rolled height, and it's exactly what we want for our game. One more thing before we move on. We could continue passing in the camera that we created into the initialization of the viewport. However, if we don't pass in a camera, the viewport will actually create one for us. So if we want, we can remove these lines for creating the camera. And we'll also need to remove the camera parameter we passed in here. Now change the comment to set up the Viewport. Also in the render method and the call to game dot batch dot set projection matrix, we need to change camera dot combined to viewport dot camera dot combined. Finally, let's press Control A to o to remove all the unused imports. Alright, in the next video, we'll start having some fun by creating the fighters. See you there. 13. Create the Fighter Class: Before we start creating the fighters, let's go to our main package and the core folder, right click it and go to New Package. Let's call this package Objects and Press Center. And here as we we'll create any classes that we'll use multiple instances of in our game. This includes the fighters. So let's right click the Objects package, go to New Java class, call it fighter and Pre Center. All right, now let's set up some things we'll be needing for the fighters. First, if we go over to the assets and open the Sprites folder, we have these eight images with sprite sheet in their names. A sprite sheet is basically an image consisting of multiple smaller images arranged in a tiled grid formation and are often used to hold the frames for a game objects animation. Each of these sprite sheets holds the frames for a fighter's animation state, such as block, kick, punch, and walk. If we open, for example, punch sprite shehet dot PNG, you can see that it contains six frames for fighter's punch animation, arranged in a grid of two rows and three columns. And actually, all of these sprite sheets have the same two by three grid arrangement. Another thing to note that, even though it's hard to tell here, due to the backgrounds of the frames being transparent, all of the sprite sheet images are the exact same size. And each frame within them is the same size as all the other frames. This isn't entirely necessary, but it definitely helps when switching between the different textures and animation, as we'll see in a bit. Also, you might be wondering why all the fighter textures are colored white. The reason is that we can actually add color to our textures and GDX before we draw them. So we can reuse these textures for each of the fighters by simply changing the color. This saves us from having to use a whole bunch of different sprite sheets for each fighter. But of course, this is only possible because the fighters are simple, solid colour stick figures. Okay, let's go back to the fighter class now. Let's create some variables that will be needing for the fighters. First, we'll need a couple of variables for the number of rows and columns in each animation sprite sheet. I'll comment number of frame rows and columns in each animation sprite sheet. Then let's type private static final int, frame rows equals two, frame cause, equals three. Next, we're going to add some variables for things like a fighter's movement speed and the maximum amount of life they can have. First I'll comment how fast a fighter can move. Then type public static final float, movement speed equals ten F. We'll be able to change this later if we want to make the fighters walk faster or slower. Next I'll comment, maximum life a fighter can have. Then type public static final float. Max life equals 100 F. Next, amount of damage a fighter's hit will inflict. Then public static final float. Hit strength, equals five F. Each time a fighter gets hit, the fighter's life amount will be decreased by the hit strength amount. Next I'll comment, factor to decrease damage if a fighter gets hit while blocking. Then public static final float block damage factor equals 0.2 F. When a fighter blocks an attack, we want them to still get injured so that they won't just stand there and block the entire game. However, we don't want the fighter to get as injured as they would if they hadn't blocked the attack. So we'll multiply hit strength by block damage factor and decrease the fighter's life by the resulting number. So in this case, with a hitch strength of five and a block damage factor of 0.2, the blocking fighter's life will only be reduced by one. Okay, in the next section, we'll create variables for a fighter's distinguishing details. We only need two of these the fighter's name and the fighter's color, which we'll be able to set when we create the fighter. So first, let's type private string name. Next, let's type private color. We want to make sure to import the GDX do graphics version here by double clicking it. Then type color. Next, we need to add some variables relating to the fighters state, such as their animation state, position, current life amount, et cetera. I'll come it state. And now we're first going to create an Enum with values for each of the possible animation states. So let's type public Enum state open curly brace, then block, hurt, idle, co kick, lose, come a punch, come a walk, win. After this, we want to create a private state state, which will hold the fighter's current state. We also want to create a state time variable to correspond to how long the fighter has been in a particular state. This is important for the animations. So let's type private float state time. Another thing we want to do is create a separate render state and render state time for the fighter. This is because when we make it so we compose the game later, we want to freeze everything on the screen, including any fighter movements and animations. However, we still want the player to be able to change their fighters state such as to stop moving if they release a movement button. We just don't want this change to appear on the screen until they unpose the game. Therefore, we need to give the fighter an actual state and a separate render state. This makes sense a bit later. For now let's type private state render state. Then private float render state time. Next we'll add a variable for the fighter's position inside the ring by typing private final vector two, pressing Inger to import it, and position equals new vector two. A vector two object holds an X and Y value, which will be able to change within the game. We also need one of these for the fighters movement direction. So let's type private final vector two movement direction. I was new vector two. For the X and Y values of movement direction, we'll be using ones, zeros, and negative ones to indicate whether the fighter is moving up, down, left, right, or not moving at all. Let's now create a variable for the fighter's current life amount by typing private float life. Next, let's type private in facing. This variable will be either negative one or one and will indicate which direction the fighter is facing. We want the fighters to always be facing each other, so we'll be changing this value whenever the fighters walk past each other in the ring. Finally, let's type private Boolean made contact. This will indicate whether the fighter has made contact with their attack. As we'll see later, this is important in order to prevent a fighter single attack from continuously injuring the opponent. We only have one more section of variables to write, which are for the animations. To create animations and LibGDX, we use the animation class. With the animation class, we can create a list of objects that represent an animation sequence. So, for example, to create one for the block animation, we can first type private animation and make sure we import the graphics g2d version here. We didn't need to type a less than sign, let the animation know what type of object we want to create a list of. We'll typically use texture region objects for this. A texture region points to a particular region in the texture. For example, in one of our sprite sheets, each frame here will correspond to a separate texture region. So we can type Texture region here and press Inger to import it, then type the greater than sign. Now we can call the animation block animation. Now we simply need to do this for each animation. Okay, here are the objects for all eight animations. Next, we need to create a constructor for the fighter. So let's type public fighter. We want to pass in three parameters here. First, let's type SFS, press Inger to import it, then game, string name, color color. Inside the constructor, if you want to set this dot name equals name, and this dot color equals color. We're now going to create some methods for initializing all eight of the animations. First I'll comment, initialize the animations. Then type initialized block animation. We want to pass in game dot assets dot Manager. Of course, we're getting an error because we haven't yet created the method, let's go ahead and create calls like this for the remaining animations. Okay, with those calls typed out, let's actually create these methods below the constructor. First, let's do private, void, initialized block animation. Then type asset manager. Press Enter to import it, then asset manager. Inside the method, we first want to get the Block Sprite sheet texture from the asset manager. To do this, we can type texture, press Enter, Sprite Sheet, equals asset manager dot Git. Then we can pass in the pointer to the Blocksprthet dot png location that we created in the assets class a while back by typing assets, pressing Enter, then dot Block Sprite Sheet. Next, we need to get the individual frames or texture regions from the texture. To do this, we can type texture region, followed by open and closing brackets because this is going to be an array of texture regions. Then type frames, equals G animation frames and pass in sprite sheet. We're getting an error because we haven't yet created the Git animation frames method, which we'll do in a bit. But first, let's go down the line and initialize block animation by typing block animation equals new animation. Less than greater than open Perthess. And here we need to pass in a desired frame duration for the animation. This is the amount of time and seconds that we want the animation to last. If we open up block spritesheet dot P and G, each frame in here is actually exactly the same. This is because I didn't really want the fighter to move while blocking, but if I change my mind later, I can easily change the frames and the animation. So because the frames are exactly the same, it doesn't really matter what we put as the frame duration, but I like to default to 0.05 F. Now we also need to pass in the frames object that we created. Okay, now let's create this Get animation frames method. First, let's type private texture region, open and closing brackets, Get animation frames with the parameter texture sprite sheet. To get all of the texture regions from sprite sheet, we'll need to create a two D array of texture regions by typing texture region with two pairs of brackets. And this is just going to be a temporary variable, so we can call it TIP or TMP we'll set this equal to texture region dot split. Then passed in sprite sheet, sprite sheet, dot get width divided by frame calls. Co sprite sheet, Dogg heights. Divided by frame rows. What the split method of texture region does is it takes the given texture and splits it up into texture regions with each region having the size of the tile width and tile height values that we pass in. Dividing the sprite sheets width by frame calls, lets the method know that we want each texture region to be a third of the sprite sheets width. Similarly, dividing the height by frame rows, which is two makes each texture region half the height of the sprite sheet. We now need to take the two d array that the split method returns and turn it into a one D array. To do this, let's create a one D texture region array called frames. Let's set it equal to new texture region, open bracket. And for the size of the array, we want frame rows times frame calls. We now need to run through the two D temp array and put its texture regions into the one D frames array. To do this, we'll use nested four loops. First, let's create an int index variable and set it equal to zero. This will refer to the current index of the frames array. Now let's type four, int I equals zero, semicolon I less than frame rows, semicolon I plus plus. For the columns, let's type four, int J equals zero, semicolon J less than frame cause, semicolon J plus plus. We can now type frames brackets index equals TIP, brackets I, brackets J. We then need to increment index by one. We can either do it on the next line by typing index plus plus. Or we could simply put a plus plus after index and frames index here. We have to be careful to put the plus plus after index, not before it, so that it will increment the index variable. After doing frames index equals temp by J. Okay? And after these four loops, you need to return the frames variable. Now we just need to create these initialized methods for the remaining animations. I'm going to copy this one and paste it above the Get animation frames method. This will be for the next animation, rt. So I'll change the method to initialize Heurt animation and make it so it uses the Hertzprt sheet and the Hurt animation. For the frame duration, I find that 0.03 F works well here. Now, let's create a method for the idle animation. I'll use 0.1 F for the duration. Next up is kick. I'll leave the duration at 0.05 F. Next is lose. Also with a 0.05 F duration. Next we'll do punch. Again, I'll use 0.05 F. Next step is walk. For a walk, I'll use 0.08 F. Finally, we have win. Now I'll leave this one on 0.05 F. Alright? I think this is a good point to stop for this video. And the next one will actually draw the fighters and start animating them. See there. 14. Draw & Animate the Fighters: Now that we've set up the variables and animations for the fighters, let's actually get them onto the screen. First, after the fighter constructor, let's at a public void get ready method. And for the parameters, let's type float position X, come a float position Y. We'll be calling this method from the game screen class at the start of each round of a fight, and we'll use it to reset the fighter's state. First, let's set both the state and the render state to the idle animation state. We can do this easily by typing state, equals render state, equals state dot idle. We also want to do state, equals render state time, equals zero F. Next, we'll set the fighter's position to the position values that we pass into the method. Because the position variable is a vector two, we can do this by typing position dot set position next come a position Y. And we want to set the movement direction vector two variable to zero, zero, which indicates no movement. So let's site movement direction dot set 00. Next, we'll set life to max life so that the fighter starts each round with the maximum life amount. Next, we'll just set made contact to false since the fighter hasn't made contact yet. After this method, let's create a public void render method. For a parameter, we want to type sprite batch, price Enter, then batch. We'll leave this method empty for the moment. Let's also create a public void update method with a parameter of float Delta T. We'll leave this one empty for the moment as well. Now let's head over to our main game class, SFS. We're going to create the fighter objects in this class instead of the game screen class. And the reason is that when we add a main menu screen later, we're going to give the player the ability to choose their fighter. Therefore, we will need access to the fighter objects in both the main menu screen and the game screen. So at the top, after the creation of the game screen variables, I'll comment fighters. Then cite public fighter, presenter, then player opponent. Next in the create method, after the lines for loading the assets, I'll comment, initialize the fighters. Tennis type player equals new fighter. Now we need to pass in this, and we're going to eventually provide a list of fighters for the player to choose from, which will contain different names and colors for each fighter. But for now for the name, I'll go the string Slim Salon. And for the color, I'll type new color. Make sure to import the GDX dot Graphics version. Then I'll put F, 0.2 F, 0.2 f1f. This will make the player a reddish color. That will create the opponent. So opponent equals new fighter, this, and for the name, I'll go thin diesel. And for the color, I'll do new color, 0.25 F, 0.7 F 1f1f. This will be a bluish color. We've created the fighters, so let's work on rendering them in the game screen. First, at the top, we're going to add some variables for the starting positions of the fighters. I'll comment fighters here. Then let's type private static final float. Players start position X equals 16 F. Next, let's type private static final float. Opponents start position X. Equals 51 F. And we want both fighters to have the same Y starting position so that they will be lined up evenly across from each other in the ring. So for the Y position, let's type fighter, start position Y equals 15 F. I came up with these values mostly by using trial and error until I found something that looked good. I could have used some math here to get them positioned perfectly, but I found it wasn't necessary. And by the way, these values are in world units. Next, down here after the render method, Let's create a private void update method and give it the parameter float Delta T. And inside the method, type game dot player dot update, Pass in Delta T. Then let's do game dot opponent that update and pass in Delta T. We haven't yet filled in the update method of the fighter class, but we're going to use this method to update things like the fighters animation and movement. Now, this Delta T variable here will actually come from the float Delta parameter of the render method. Delta refers to how many seconds have passed between the previous call to render and the current one. B render is called many, many times per second in the game loop, Delta will be a very small number. This number lets us keep track of how much time has passed in the game, and it's very important for consistency and things like animations and movements of objects. This is because render might get called more often on a faster computer than a slower one. So if, for example, we tell an object to move a certain distance every time render is called, and we don't take into account how much time has passed since the previous call to render, then the object will move faster on the screen of a user with a fast computer than it would for someone with a slower computer. So with that in mind, after we clear the screen here, I'll comment update the game. Then let's make a call to the update method, passing in Delta. Later on, we're only going to pass in Delta here when the game is actually running. If it's not running, such as when the user has paused the game, we'll pass in zero instead. This will stop all animations and movements. Okay, next, after we draw the background Ring here, I'll draw the fighters. Let's make a call to render fighters, a method that we still need to create. Let's create the render fighters method, after the render method. By typing private void render fighters. We're going to be adding more to this method later, but for now, I'll comment draw player. Then type game dot player dot render, passing in game dot batch. Next, I'll comment draw opponent. Then type game dot opponent, dot render, and again, passing game dot batch. We now need to go back into the fighter class and fill in the render and update methods. First, in the update method, you need to increment the fighters state time by Delta T. I'll comment increment the state time by Delta T. Then's type state time plus equals Delta T. Next, we want to set the fighters render state to their actual state, but only if Delta time is greater than zero. If Delta time isn't greater than zero, the game may be paused, so we want the render state to remain the same, which will freeze all animations. So first, I'll comment, only update the render state if Delta time is greater than zero. Then's type I Delta time greater than zero. Then's type renders state, equals state. We also need to do render state time equals state time. Don't worry if this is confusing right now as it all makes sense later. Now in the render method, we're going to use the fighters render state and render state time to determine which frame of which animation to draw. First I'll comment, get the current animation frame. Then we'll need to create a texture region object for holding the current frame by typing texture region, current frame. Next, we need to create a switch statement using render state by typing switch, render state. Now we need to create a case for each type of animation state. So first, let's do case, block, colon. To get the current frame of the block animation, we can first type current frame equals block animation, DG key frame. We now need to pass in render state time so that the method can calculate which frame to get by using the frame duration that we set when initializing the animation earlier. Next, we can pass in a Boolean value to let the method know whether the animation should loop. If we put it true, then when the state time exceeds the frame duration, the animation will start over from the first frame. If we put it false, the animation will stop. Although, as we saw earlier, our block animations frames are all exactly the same. We want the animation to continue as long as the fighter is blocking, so let's start looping to true. Let's now add a break here so that I won't continue to the next case. Then let's do case hurt colon. Followed by current frame equals hurt animation, DG key frame. Render stay time, and we only want the hurt animation to play once. So let's put false for looping. That's do a break. Then case idle. For this one, we'll do current frame equals Idle animation, DG ki frame. Render state time, and true for looping then a break. Next is case kick. With current frame equals kick animation do GkiFrame Render state time false and a break. Next, we have case lose. I'm going to copy these two lines for the kick case, paste them down here and change kick animation to lose animation. Now I'll do case punch, paste the lines, and change it to punch animation. Lex is case walk. I'll paste the lines, change it to walk animation. And for a walk, I want looping to be true. Finally, for the wind state, because it's the final state, we can put default colon. Now we can paste the lines, change it to win animation. Let's start looping to true. Because this is the final case, we don't need a break here. Okay, after the switch statement, we'll draw the current frame to the screen. First, we'll have to make it so that the color of the frame corresponds to the fighter's color. To do this, we can simply type batch outside color, pass in the fighter's color variable. Then we can draw the current frame by typing batch, draw, current frame. Give the position by typing position dot X, position dot y, then and for the width, let's type current frame dot get Region width, which gets the width of the texture region in the frame. And we need to scale this by the world scale variable, so we can type times, global variables, press Enter, then dot world scale. And for the height, let's type current frame, dot get Region height. Times global variables world scale. After this, we need to reset the batch's color to white, which is the same as telling it to not add any color to the textures that might get drawn after this one. To do this, we can cite batch dot set color, then pass in all ones. And before we run this, we also need to call the Get ready method for each fighter in the game screen class. So let's go back to Gamescreen and for now, we can do this at the bottom of the constructor. I'll comment, get the fighters ready. Then type game dot player dot Get ready. I pass in players start position X. Come a fighter start position Y. Next I'll type game dot opponent dot get ready. Opponent start position X. Come a fighter start position Y. Now let's run it and see what happens. Okay, so our fighters are in their starting positions and their idle animations are running. Awesome. We do want the fighters to be facing each other, however, which we'll fix in a bit. But first, we can try some of the other animations to make sure they work. First, let's go to the get ready method and change state to state D block, then run it. Okay, that works. Now let's try Hurt. Hurt doesn't loop, so we just see it quickly run through the animation once at the beginning. Now, let's try. Kick. Looks good. Now, lose. Okay. Ponchs next. Now, walk. And finally, win. Perfect. Okay, let's set it back to idle. Al right in the next video, we'll fix the problem of the fighters not facing each other. See there. 15. Update the Fighters' Facing: As we saw when we ran the game in the previous video, the fighters are in their starting positions and their idle animations are running, but they're not facing each other. So let's go ahead and fix this. First, after the update method in the fighter class. Let's credit a couple methods for changing the fighter's facing. We can first create a method for making the fighter face left by typing public void face left. And in here, we can simply put facing equals negative one. Now we'll do the same for the face right method by typing public void, face right. And put facing equals one. Next, we want to compare the X coordinates of the fighters positions each frame. The fighter with the higher X should face left, and the fighter with the lower X should face right. We'll do this in the update method of game screen. However, we first need to be able to access the fighter's position from the game screen. To do this, we can create a Gitter method for the position variable. So up here, after the constructor, Let's type public vector two Git position. Then we just need to type return position. Now let's go over to GameScreen and in the update method. After calling the fighters update methods, I'll comment, Make sure the fighters are facing each other. Then let's type Igamedtplayer, dot git position dot X is less than or equal to game dot opponent dogiposition dot X. And if this is the case, you need to call game dot player dot face right and game dot opponent Da face left. If the fighters coordinates happen to be equal, it doesn't really matter which direction they face. So we'll just have the player face right and the opponent face left. Now we need to put an outs. Then type game dot player, dot face left, and game dot opponent, Da face right. Now, in order for the facing changes to actually work, we need to go into the render method of the fighter class. And where we draw the current frame, we're going to need to use a different version of the draw method. If I remove the open parenthesis and type it again, we can see all the versions of the draw method. We want the one where we can pass in texture region, region, float X, float Y, float origin, X, float origin Y, float width, float height, float scale X, float scale Y, and float rotation. Scale X, let's just shrink or grow the texture horizontally, and scale Y, let's just do so vertically. You can also rotate the texture using the rotation parameter. The default values for scale X and scale Y are one, which means no scaling. And for rotation, which is in degrees, the default is zero degrees, which means no rotation. We won't be rotating our texture, so we'll leave rotation on zero. For scale X and scale Y, if we use a negative number for either of them, the texture will essentially be flipped as well as scaled. So if we use negative one for scale x, the texture will remain the same size, but will be flipped horizontally. This is what we want whenever the fighter should be facing left. Also, origin X and origin Y here represent the point at which the texture will scale or rotate. This will make sense in a bit. For now, let's go ahead and add these new parameters into our draw line. First, before we pass in the width, we need to pass in origin X and origin Y. Let's just use the defaults of 00 for the moment. Next, after we pass in the height, we need to pass in scale X, scale Y, and rotation. Because we want to flip the texture horizontally when the fighter should be facing left and not flip it when the fighter should be facing right, we can simply pass in the facing value. For scale Y, we never want to flip the texture vertically, so let's just put a one. We don't want to rotate the texture, so we'll put a zero for rotation. Now let's go ahead and run it. So the fighters are now both facing in the correct direction, but the opponent fighter has moved to the left. This because we set the origin to 00, which is the bottom left of the texture. So the texture, which was originally in this area, got flipped horizontally over its bottom left corner. To fix this, we need to put origin X at the center of the texture. So back in the code, let's replace zero for origin X with current frame dot G region width times 0.5 F. And we also need to multiply it by world scale, so times global variables dot WorldScale. And because we're not flipping the texture vertically, we can just leave origin Y at zero. And I'll go ahead and clean this line up a bit. Now let's run it. Okay, the fighters are in their correct positions again, and they're facing each other. They will also continue facing each other if they pass each other in the ring. This leads us to the next video, where we will learn how to process keyboard input from the player so they can do things like make their fighter move around and attack. See there. 16. Handle Keyboard Input: In order to start handling input from the player, we need to go to our game screen class at the top, we need to implement another class called Input Processor. After implement screen, let's type Input processor and press Enter to import the class. This requires that we implement several methods. Let's hover over this line. Click Implement methods. Then click Okay. Now at the bottom of the class, we have eight new methods. All of these methods are used for dealing with certain types of input, such as keyboard input, touch input, and mouse input. In this video, we're going to focus on keyboard input, giving the player the ability to use different keys for things like making their fighter move and attack. The methods for dealing with keyboard input are keydown, key up, and key typed. Keydown is called whenever the player presses the key, key up is called when they release a key and key typed is called when the player types in a character. Key typed is mainly for processing some texts that a player might type into a text field, for example. We won't be needing this in our game, so we'll ignore the key typed method, but we'll be using both keydown and keyup. And we'll begin by using these methods to let the player move their fighter. But before we do this, we need to go to the fighter class and add a few methods. First, we need a method that we can call to change a fighter's animation state, such as from idle to walk. So after the face right method, let's type private void change state with a parameter of state new state. Inside the method, we first want to type state equals new state. We also want to reset the state time. So next we'll type state time equals zero F. That's it. Next, we need to create some methods for setting a fighter's movement. First, let's create a private void set movement method. With parameters float X and float Y. And here we need to set the movement direction variable to the X and Y values by typing movement direction dot set X Y. We now need to determine whether to put the fighter in the walk state or the idle state. If both X and Y are zero, meaning the fighter shouldn't move, and the fighter is currently in the walk state, we need to change the state to idle. If on the other hand, the fighter is currently in the idle state and either X or Y isn't equal to zero, the fighter needs to start moving. So we'll change the state to walk? So first let's type if state is equal to stake dot walk, and X is equal to zero, and Y is equal to zero, then change state and pass in state dot idle. Now let's do Asif. State is equal to state dot idle and open parenthesis, X is not equal to zero, or Y is not equal to zero, closed parenthesis. Change states dot walk. If neither of these is the case, we won't change the fighter's state. So, for example, if the player's fighter is currently in the punch state, we don't want the punch animation to get cut off if the player presses a movement key. However, we will have the fighter start walking after the punch animation is finished, which we'll take care of later in the update method. Next, we need to create some public methods that we can call from game screen to move the fighter in different directions. First, let's type public void, move left. And here, let's type set movement. Then pass in negative one for X, and for Y, let's pass in movement direction Y. Passing in negative one for X will cause the fighter to move left, and we don't want this method to affect the fighter's vertical movement, so we pass in their current vertical movement for Y. Next, let's do public void, move right. Set movement one movement direction Y. Now we need two methods for vertical movement. So let's type public void, move up. Set movement. This time we want to keep the horizontal movement the same, so we'll put movement direction dot X for X. Then to move up, we'll put a one for Y. Finally, let's do public void and move down. Set movement, movement direction dot X comma negative one. We now need to create some methods to stop the fighter from moving in each direction. First, let's type public void, stop moving left. And for these methods, we only want to stop the fighter from moving in a certain direction if they are currently moving in that direction. This is because, for example, the player might press the left movement key, then without releasing the left key, they might press the right key to start moving right. If the player then releases the left key while still pressing the right key, we don't want to stop the horizontal movement. This will make sense when we add some code to the key up and key down methods in a bit. For now, to check whether the fighter is currently moving left, let's type if movement direction dot X is equal to negative one. And if this is the case, you want to stop the horizontal movement by typing set movement, zero, and keep the vertical movement the same by typing movement direction dot Y. Next, we'll do public void, stop moving right. Check whether the fighter is currently moving right by typing I movement direction X is equal to one, then set movement, zero moving direction Y. For vertical movements, let's type public void, stop moving up. If movement direction Y is equal to one, set movement movement direction.x0. And finally, public void, stop moving down. If movement direction Y is equal to negative one, set movement moving direction.x0. A to actually put that movement direction into effect, let's go into the update method. At the bottom of the method, we want to check whether the fighter is in the walk state by typing if state is equal to state dot walk. And here I'll comment, if the fighter is walking, move in the direction of the movement direction vector. We now need to move the fighter's position in the direction of the movement direction variable, and we want to do so at the speed of movement speed, which we said earlier. However, movement speed is how many world units to move per second. So we'll need to multiply this value by the Delta time variable and the update method. We learned previously that this is the amount of time that has passed between the current game render and the previous one. So to change the fighter's position, let's go back in here and first type position dot X plus equals, moving direction dot X times movement speed times Delta time. So if moving direction dot X is negative one, position dot X will be decreased by this amount, causing the fighter to move left. If it's zero, position dot X won't change, and if it's one, position dot X will be increased by the amount causing the fighter to move right. And we also need to do this for position dot Y. So let's cite position dot Y plus equals, moving direction dot Y. Times movement speed, time stilt the time. Alright, with all of that out of the way, we can go back into the game screen class and handle the keyboard input to get the player's fighter moving. First, at the top of key down, I'll comment, check if player has pressed that movement key. Okay, for the movement, we're going to let the player use either the arrow keys or the WASD keys. This is pretty common in computer games. So first, we need to check whether the player has pressed one of these keys. When the key down method gets called, the key that gets pressed is passed into the keycode parameter. So we can use keycode to check whether the pressed key is one of the keys we're looking for. Let's start with the left movement, which the player can do by pressing either the left arrow key or the A key. So let's type if keycde is equal to input, press Enter to import it, dot keys, dot Left, or key code. Is equal to input dot keys A. Input dot keys here holds variables for all the keys on the keyboard, and we need to check keycode against those variables. Now in here, we can call game dot player dot move left. We now have to check for write movement by typing El SIV, Keycode is equal to input dot keys dot right. Or keycode is equal to, and for write movement, we'll use input dot keys dot D. Then in here, let's type game dot player Move right. Now for vertical movement, we want to use a separate I statement. This is because we want to let the player move both horizontally and vertically at the same time. After this I statement, let's first check for up movement by typing if key code is equal to input dot keys dot up or key code is equal to input dot ks dot W. Then put game dot player dot MOV U. Finally, for down movement, let's type SIV. Keycode is equal to input dot keys dot down. Or keycode is equal to input dot keys dots. Then game dot player dot move down. One more thing we want to do in this method is change return false to return true. Let's let the input processor know that we handled the key down events ourselves. Otherwise, it might do some extra stuff in the background which we don't want. We next need to use the key up method to determine if a movement key has been released, and if so, we should stop moving in that direction. First, let's change return false to return true. Then above it, I'll comment. I player has released a movement key, stop moving in that direction. Now, I'm actually going to copy the two F statements and key down and paste them and key up. Then I'll change all the move method calls to the respective stop moving calls. So stop moving left for this one. Stop moving right. Stop moving up and stop moving down. Okay, I think we should be ready to go now, so let's give it a try. It doesn't seem to be working yet, and that's because we forgot one important step, which is to tell LibGDX to use the game screen class as the input processor. And we need to do this in the game screen show method. This method gets called every time the game screen becomes the visible screen in our game. The reason we need to set Gamescreen as the input processor and the show method is that we're going to be switching between screens at various times in our game. So it's just by going to the main menu screen or the setting screen to change something. And each time we do so, we'll need to set the current screen as the input processor. Okay? So in the show method, I'll comment, process user input. Then we simply need to type GDX, press Enter to import it, then input dot set Input processor and pass in this. Now should work when we run it. Okay, cool. We can move our fighter around the ring. We do have a couple of problems, though. The fighter appears to be in front of the front ropes and we can walk outside of the ring. We'll fix both of these in the next video. See there. 17. Define the Ring Bounds: Let's first fix the problem of our fighter appearing in front of the rings front ropes. To do this, we simply need to draw our front rope stop PNG image after we draw the fighters. We actually already got this image from the asset manager earlier in the create game area method and put it in front ropes texture. So in the game screens render method, after the call to render fighters, I'll com it, draw the front ropes. Then we can draw the front ropes by first typing game dot batch dot draw front ropes texture. Now for the position, if you open the front ropes dot PNG image, it's hard to tell here, but I actually made the image the same width as the background image with transparent padding on the sides. Also, the bottom of the poles here reach the bottom of the screen. So like with the background texture, we can use 00 as the position and it will line up correctly on the screen. For the width, let's type front ropes texture dot Get W times global variables rolled scale. And for the height, front ropes texture, D get height times global variables, that roll scale. And if we run the game now, and move the fighter down here. The front ropes now appear on top of the fighter. We can still walk outside of the ring, though, so let's fix that next. First, we want to go to the top of game screen and set up a few variables to define the ring bounds. We'll do this in the background slash Ring section after the texture variables. First, we need to define the minimum and maximum X and Y values for the ring bounds. These need to be in world units, and I basically use trial and error to find values that work well. First is type private static final float. Ring Mn X equals seven F. Next, private static final float. Ring max X equals 60 F. Then private static final float. Ring men Y equals four F and private static final float. Ring max Y equals 22 F. Now, if you open the background image, the sides of the ring actually slope inward at the top to account for a perspective, and we need to take the slope into account when defining the ring bounds. By subtracting this point from this one, I found the slope to be about 3.16. So back in game screen, aside private static final float, ring slope equals 3.16 F. Next, in every frame of the game, we need to check whether either fighter's position has exceeded the ring bounds. We'll do this in the update method. First, at the bottom, I'll com it, keep the fighters within the bounds of the ring. Then we're going to call a method that we'll create called keep within ring bounds. And we'll pass in a fighter's position. So for this one, let's do game dot player dot get position. We also need to call this method for the opponent fighter, so keep within ring bounds. Game opponent, Dk get position. Now let's create the keep within ring bounds method after Update. Let's type private void, keep within ring bounds. Then vector two, press Enter to import it, then position. And here we'll have a couple if statements. Checking the position against the Y bounds is the easiest, so let's start with that. We can type if position dot Y less than ring men Y. Then position dot Y equals ring men Y. So if the fighter goes below the bottom of the ring, we'll move them back up to touch the bottom of the ring. Next, we'll do out Sif position dot Y, greater than ring max Y. Position dot Y equals ring max Y. Below this, we'll do an F statement for the X bounds, and we'll need to take into account the slope of the ring. So first, let's type I position dot X less than position dot Y divided by ring slope plus ringmX. The position dot X equals position dot Y divided by ring slope plus ring Min x. This expression here takes the Y coordinate of the fighter's position, divides it by the slope of the ring side, and adds the minimum X coordinate of the ring to determine the lowest X coordinate that the fighter is allowed to have. Now for the right side of the ring, let's do Sif position dot X greater than position dot Y divided by, and because the ring slopes in the opposite direction on this side, we need to put negative ring slope, then plus ring max X. And here, let's type position dot X equals position dot Y divided by negative ring slope plus Ring Maxx. If you run the game now and walk around, our fighter is now forced to stay within the bounds of the ring. Also, if we pass the opponent, we can see that they're facing updates correctly. However, if we go below the opponent, the opponent is still being drawn on top of the player. This, of course, isn't what we want, so let's fix that really quick. To fix this, we need to go into the render fighters method. And we need to use the Y coordinates of the fighters positions to determine which fighter to draw first. First, let's select all of these lines and cut them with Control X. I'll comment, use the Y coordinates of the fighters positions to determine which fighter to draw first. Then we need to compare the fighters Y coordinates using a NIF statement. So let's type if game dot player dot g position dot Y Greater than game dot opponent, dog position dot Y. Now let's paste the lines we cut. This would draw the player first, then the opponent, which is what we want because the player is higher up in the ring. Now we can do ts. Then paste again and switch the locations of these lines. If we run the game now, the fighters get drawn correctly. Okay, cool. Next, we'll continue with the keyboard input processing by allowing the player to press keys to attack and block. See there. 18. Attacking & Blocking: Before we give the player the ability to press keys to attack or block, we need to create a few methods in the fighter class for setting the fighters state to each of the attack and block states. So in the fighter class, after all of the movement methods, let's create a public void block method. We actually only want to change the fighters state to block if they are currently in either the idle state or the walk state. This is because if the fighter is in an attack state, we don't want to interrupt their attack animation. Also, if the fighter already happens to be in the block state, there's no point in changing the state. So this type of state is equal to state dot idle or state is equal to state dot Walk. Change state state dot block. We also want to create a method to make the fighter stop blocking. We'll call this method when the player releases the block button. For this method, let's type public void Stop blocking. And we only want to stop blocking if the player is actually blocking. So in here, let's first type if state is equal to state dot block, next, we want to determine whether the fighter should go into the idle state or the walk state. That's because the player might be pressing a movement key while blocking, and we want their fighter to immediately start walking when they stop blocking. To determine whether the fighter should start walking, we can simply check if either moving direction dot X or moving direction dot Y is not equal to zero. So first, I'll comment, if the movement direction is set, start walking, otherwise, go to idle. Then this type, I moving direction dot X is not equal to zero, or moving direction dot Y is not equal to zero. Change state state dot walk, else, change state state dot idle. Okay, one more block method we want to create is one that returns whether the fighter is currently blocking. This will be helpful when we start coding the opponent AI later on. For this method, we can type public bullying is blocking. Then return state is equal to state dot block. So if state is equal to block, it will return true, otherwise it will return false. Okay, now let's move on to the methods for attacking. Let's first create one for punching by typing public void punch. Like with blocking, we only want to change to an attack state if the fighter is currently in the idle or walk state. So in here, let's type if state is equal to state dot idle or state is equal to state dot walk, then change state state dot punch. Now let's create a method for kicking. That's called a public void kick. Now we can simply copy and paste the code from the punch method and change state dot punch to state dot kick. Like with blocking, we want a boolean method that returns whether the fighter is attacking. For this, we can type public boolean is attacking. Return state is equal to state dot punch. Or state is equal to state dot kick. Next, because the punch and kick animations are only supposed to happen once, when the animation is finished, we want to put the fighter in either the idle state or the walk state if their moving direction has been set. We can do this in the update method by adding an CIF to the F statement here, where we check if the fighter is in the walk state. What we want to do is check whether the fighters in the punch state and the punch animation is finished, or if they're in the kick state and the kick animation is finished. To check if an animation is finished, we call the is animation finish method of the animation class passing in the state time. So here we want to type open Parthess St is equal to state dot punch. And punch animation. Do I animation finished. State time. Closed parenthesis, closed parenthesis, or open parentheses. State is equal to stake Dot kick. And kick animation. I animation finished. State time. And here I'll comment. If the animation is finished and the movement direction is set, start walking, otherwise go to idle. And we want to check if moving direction has been set by typing, I moving direction dot X is not equal to zero, or moving direction dot Y is not equal to zero. Then change Stsatedt walk. Then else change staatesidl. Okay, so if the fighter is in an attack state and the animation has finished, we either put the fighter in the walk state if they're moving direction has been set or in the idle state. Now we can move on to the game screen class and add the keyboard processing for attacking and blocking. First in the key down method, we're going to create an I statement for the block and attack keys that is separate from the I statement for the movement keys. This is because later on, we're going to make it so that each fight can have up to three rounds. At the beginning of each round, we'll have a short delay where it first tells us the round number on the screen then says fight. During this delay, we don't want the fighters to be moving around in the ring. So we'll make it so that the movement keys will only be processed after the round has begun. However, just for fun, we'll let the player attack and block while waiting for the round to begin. Okay, so before the return statement, I'll comment, check if player has pressed a block or attack key. Now for blocking, we're going to use the B key. For punching, we'll use the F key and for kicking the V key. We'll use these keys because they're pretty close to the WASD keys, so they'll be easy to reach if the player is using the WASD keys to move. So first, let's type if keycde is equal to input dot keys dot B, then game dot player dot block. Lexus type Osif keycode is equal to input, do keys F. Game dot player dot punch. And finally, Osif keycode is equal to input, Da keys V. Game dot player dot kick. Now, because the attack animations only go through one time when the player presses an attack key, we can ignore when the player releases the key. For block, on the other hand, we want the fighter to continue blocking until the player releases the block key. Therefore, like with the movement keys, we need to deal with the block key and the key up method. Before the return statement, I'll comment, I player has released the block key, stop blocking. Then this type, I key code is equal to input dot keys dot B, game dot player dot Stop blocking. Okay, now we can run the game and give these keys a try. We can punch, kick, and if we hold down the B key, we can block. And when we release the B key, we go back to idle. Or if we hold B, then hold a movement key before releasing B, we immediately start walking. Awesome. Okay, in the next video, we'll make it so our punches and kicks actually have an effect on the opponent. See you there. 19. Getting Hit: In this video, we'll make it so when a fighter gets hit, they have a visible reaction and also lose some of their life amount. And to do so, we first need to add a few more things to the fighter class. First, when one of the attack methods has been called, we want to set the made contact variable here to false because if the fighter has just started an attack, of course, the attack hasn't yet made contact with the other fighter. We'll soon see why it's important to use this made contact variable. For now, let's go down to the punch method, and after the call to change state, I'll comment, just started attacking, so contact hasn't been made yet. Then type made contact equals false. And we can copy and paste this into the kick method. We also want to create a couple extra methods for the made contact variable, one for setting it to true and one for returning it. So first, let's type public void, make contact. And in here, let's set made contact to true. For the return method, we can type public BollyanH made contact. Return made contact. Another method we want to create is one for returning, whether the fighter is actively attacking. There are two conditions that must be met for the fighter to be actively attacking. First, made contact needs to be false because we only want the attack to hit the other fighter once. Otherwise, the other fighter will constantly get hit during the entire duration of the attack animation. And second, if we open one of the attack animation sprite sheets like the one for kick, we're going to make it so the attack is only active during the middle third of the animation instead of the entire animation. This isn't entirely necessary, but I find it looks better than having the attack hit the opponent the instant the animation starts or in this case, after the fighter has already put their leg back down. Also, having the attack only be active during the middle frames gives the other fighter the chance to move out of the way or block before getting hit. Okay, so with that in mind, let's go back to the fighter class. And for the method, let's cite public bullying is attack active. And here I'm going to comment, the attack is only active if the fighter has not yet made contact and the attack animation has not just started or is almost finished. Can was first check if the fighter has already made contact? We can do this using the has made contact method. So let's type I has made contact. And if so, we want to return false. Next, we want to check which attack state the fighter is in and determine if the animation for that state is within the middle third of the animation duration. Let's first check if the fighter is in the punch state by typing Osif state is equal to state Dot punch. Now to check how far into the punch animation the fighter currently is, we can compare the state time variable to the punch animation's duration. To get the duration of an animation, we call the G animation duration method of the animation class. So what we want to do here is return whether state time is greater than one third of the animation's duration and less than two thirds of it. So let's type return state time, greater than punch animation, dot G animation duration, times 0.33 F and state time, less than punch animation, DG animation duration time 0.66 F. So if state time is between the first third and the last third of the animation, it will return true. Otherwise, it'll return false. Now let's do the same for kick by typing Osif. State is equal to state dot kick. Return state time greater than kick animation, get animation duration time 0.33 F and state time, less than kick animation, do get animation duration. Times 0.66 F. Finally, we want to put outs at the bottom. Then return false because this means the fighter isn't in an attack state. Next, we want to create a method to call when the fighter gets hit. This method will reduce the life of the fighter by a certain amount. And if the fighter's life has fallen to zero or below, the method will make the fighter lose. So for this method, si public void, get hit. And we want to give it the parameter float damage, which we'll use to determine by how much to reduce the fighter's life. Now, there are a few animation states that the fighter could be in where we don't want the fighter to get hit. First, if the fighter is in the hurt state, they've already just been hit, so we don't want them to immediately get hit again. We also don't want them to get hit if they're in the win or lose state, because in either of these cases, the round or the game is already over, so there's no point in the fighter getting hit. We also don't want to interrupt their win or lose animation. Okay? So first, let's type if state is equal to state dot HRT, or state is equal to state dot win or state is equal to state dot lose. Return. This will cause the method to finish straightaway and not perform the next lines of code. Next, we want to reduce the fighter's life either by the full damaged amount or if the fighter is blocking, we'll only reduce their life by a fraction of the damage amount. To calculate this fraction, we'll multiply the damage value by the block damage factor variable that we created a while back. First I'll comment, reduce the fighter's life by the full damage amount or a fraction of it if the fighter is blocking. And we can do this with a ternary operator by typing life minus equals. State is equal to state do block. Question mark, damage times block damage factor, colon, damage. This is the equivalent of doing I state is equal to state do block, the reduce life by damage times bock damage factor, else reduce it by damage. Next, we want to check if the fighter's life has dropped to zero or below, and if so, we make the fighter lose. So let's type if life less than or equal to zero F, and I'll comment, I no life remains, lose. We're actually going to create a public method for making the fighter lose because we might want to make a fighter lose at some other point in the game. For example, when we add rounds to the game, we're going to make the rounds have a time limit. And if neither fighter's life has reached zero before the time runs out, the fighter with more life will win, and the fighter with less life will lose. Okay, so let's make a call to a lose method, which we'll create in a bit. And if the fighter does still have some life left, we want to start the fighter's hurt animation, but only if the fighter isn't currently blocking. So here let's put OutsiF State is not equal to state dot block. Now comment, if not blocking, go to Hurt state. Then let's type change state state dot HRT. Next, we need to create the Lose method. So down here, let's type public void Los. In this method, we first want to change the fighter state to the Los state, so let's type change state state dot Loos. We also want to set life equal to zero F in case it has dropped below zero. This will prevent any problems from occurring when we display the life amount for each fighter on the screen later. Before we move over to game screen and put these methods to work, we want to do one more thing in here. In the update method, where we check if the fighters punch or kick animation as finished, we also want to check if their hurt animation is finished. So before the final parenthesis of the I statement, we can put another or then open parenthesis, State is equal to stat HRT and RT animation. D is animation finished. State time. And that's it. Now let's move over to game screen. In the game screen class, we first want to create a method for checking whether the fighters are within contact distance. This basically means that the X and Y coordinates of the fighters positions are within certain limits in which if one of the fighters attacks, the other fighters should get hit. So first, let's go up to the top of the class and define these limits. We can put these in the fighters section. Like with the fighter starting positions, I came up with these values using trial and error, and they will be in world units. For the first variable, let's type private static final float, fighter contact distance X equals 7.5 F, and for the second one, private static final float, fighter contact distance Y, equals 1.5 F. So if the fighters X coordinates are within 7.5 rod units of each other and the Y coordinates are within 1.5 rod units, the fighters are in contact distance. Now we'll create a method to check if this is the case. To create the method, let's go down here below the keep within ring bounds method. Let's type private bullying or within contact distance. And for the parameters, let's type vector two, position one, vector two, position two, which will pass in the fighter's positions. And here I'll comment, Determine if the positions are then the distance and which contact is possible. Now, we want to credit a couple of variables to hold the distances between the X coordinates of both positions and the Y coordinates of both positions. So first, let's type float x distance equals position one dot X minus position two dot X. One problem we might have, though, is that if position one dot X is less than position two dot X, the result of this will be negative. All we care about is the distance between the points, not the direction. So we want the result to always be positive. To do this, we can put this calculation inside a call to the ABS method of the math class that Java provides for us. So just before the calculation, let's state math dot abs open parenthesis and put the closing parenthesis before the semicolon. Because the math class is in the Java dot Ling package, it's automatically included in our project, so we don't have to import it. Now we want to create the variable for the y distance by typing float y distance equals math dot abs, position one point Y minus position two point Y. Next, we simply want to type return, X distance, less than or equal to fighter contact distance X and Y distance, less than or equal to fighter contact distance Y. Okay, now let's go up to the update method. And I'll comment, check if the fighters are within contact distance. Now we want to type I within contact distance and pass in game dot player dog position. Ca game opponent docu position. Next, we want to check whether the player is actively attacking, which we can do by calling the I attack active method that we created. So let's type I game dot player dot I attack active. And here I'll comment, if the fighters are within contact distance and player is actively attacking, opponent gets hit. Now we can make the opponent get hit by calling game dot opponent dot get hit. And for the damage, we'll use the public static hit strength variable we created in the fighter class. So let's type fighter, press Inger to import it, then dot hit strength. Okay. Now, once we get the Hut on the screen, we'll be able to see how much life each fighter has. But for now, for testing purposes, we can just print the opponent's life. However, first, we need to add a method in the fighter class for getting the fighter's life amount. So let's go to the fighter class and near the top, after the Gibosition method. Let's tie public float, get life. Then return life. Now back in game screen, we can type system dot dot print LLNopponents Life. Colon space plusgamedt opponent dot GLIC. Now we can run the game and see what happens. Okay, so when we hit the opponent, they go into the Hertz state. However, as you can see in the output, sometimes it prints the opponent's life multiple times when we hit them once, and the only reason the opponent isn't losing life multiple times with each hit is because the duration of the hurt animation happens to be longer than the active part of the attack animations. If we were to go into the fighter class, then go down to the initialized Hurt animation method and change the duration to something lower like 0.0 03f, and maybe, for example, the punch animation to something higher like 0.2 F. Now if we run the game and punch the opponent, They lose life multiple times with each hit. The reason this happens is that we forgot to set the player's made contact variable to false by calling the make contact method. So, back in the game screen class, after these lines, I'll comment, deactivate players attack. Then it's called game dot player dot make contact. Now if we run the game and hit the opponent, They only get hit once per attack. And if we keep hitting the opponent until all their life is gone, they go into the lose animation. When one of the fighters goes into the lose animation, though, we want the other fighter to go into the win animation. For this, we'll need a method for determining whether a fighter has lost and a method for putting a fighter in the win state. So let's go back to the fighter class and after the lose method, let's type Public Bolling has lost. Then return state is equal to stake lose. Next, for the win method, let's type public void win. Then simply called change States dot win. Now back in game screen. After we deactivate the player's attack, I'll comment, check if opponent has lost. Then it's type I game dot opponent that has lost. Now I'll comment, I opponent has lost, player wins. Then let's type game dot player dot win. Okay, now if we run the game and hit the opponent until they lose, We go into the wind state. We can also temporarily put the opponent in the block state at the start of the game in order to test whether hitting them will only reduce their life by 20% of the hit strength. To do this, let's go into the show method. And somewhere in here, let's type game that opponent, that block. Now when we run the game and hit the opponent, Instead of reducing their life by five, it only reduces it by one. It also doesn't put the opponent in the hurt state, which is good. All right, we can close this out and remove the line for putting the opponent in the block state. We can also go ahead and remove the line in the update method that prints the opponent's life. Let's not forget to go to the fighter class and set the HR animation duration back to 0.03 F and the punch animation back to 0.05 F. Okay, in the next video, we'll work toward displaying the Hud so that we can see some useful information on the screen, including the amount of life each fighter has. See you there. 20. Generate the Fonts: The Hud or heads up display is the part of the screen where some important information about the game is displayed to the player. In our game, we're going to put the health bars of each fighter at the top of the screen with the player's health bar at the left and the opponents at the right. We'll also put the names of the fighters inside the health bars. Some other information we'll display are a ratio of how many rounds the player has won versus how many they've lost. We'll display the current difficulty of the game, which the player will be able to change, and in the center of the Hud, we'll display the amount of time remaining in the current round. So we're going to be using a lot of text. Therefore, the first thing we need to do is set up the fonts we'll use. The standalone LibGDX library comes with a class for creating fonts called Bitmap font. However, bitmap fonts rely on images, and the default bitmap font images and LibGDX look very pixelated when scaled a lot. We could create our own Bitmap font images, but that would be a very time consuming process. Instead, we're going to use the free type extension, which we included as an add on when creating our project. With free type, we can provide a font file and a desired font size, and it will generate bitmap font images for us straightaway, using the font file and size. This means we can use pretty much any font we want. And our game will be using the Roboto regular font. We can replace this with a different font if we want. It just has to be a TTF or true type font file. If you go to the assets class, when we created variables for the font assets up here, we learned that we're going to use our font file to generate three different fonts, small font Da TTF, medium font Da TTF, and large font D TTF. We'll do this using a load fonts method. So after the load gameplay assets method, let's type private void Load fonts. The first thing we need to do in here is tell the asset manager how to work with free type and TTF files. It's not important that we know exactly how this works because it will be the same for any project in which we generate fonts using free type. So first, we need to create something called a file handle resolver by typing file handle resolver, pressing Enter to import it. Then resolver equals new internal file handle resolver. Press Enter to import it. Next, we'll set an asset loader for free type in our asset manager by typing manager, dot set loader, free type font generator. Presenter, then Do class, new free type font generator loader Presenter, and pass in the resolver variable here. We also need to set an asset loader for loading bitmaps from TTF files. So let's type manager dot set loader, Bitmap font, presenter, dot class, TTF New free type font loader, presenter and pass in resolver. Okay, with all of that complicated stuff out of the way, let's generate and load some fonts. First, let's do the small font. I'll comment, Load the small font. We first need to create a free type font loader parameter variable. This variable will let us set things like the font file we want to generate the font from the size of the font, and add a border around the letters if we want. So to do this, let's type free type font loader parameter. Press Enter to import it. Let's call it small font. Then equals new free type. And instead of typing all of this out, we can just press Enter. Next, we want to give small font the name of the font to generate from, what would be the Roboto regular file. To do this, we can type small font font file name equals Roboto regular. Next, we'll give the font a size by typing small font dot font parameters, dot size equals, let's make it 32. We can always change this later if we want. We'll mainly use the small font for displaying the names of the fighters. We don't want a border for this font, so we don't need to change any other settings at the moment. All we need to do now is have the asset manager use the small font settings to generate bitmap font images into the small font dot TTF file and add the file to the asset loading queue. This sounds complicated, but to do it, you just have to call manager dot Load, then pass in the small font static variable. Then Bitmap font dot class. Then the small font perimeter variable. And the asset loaders we set up earlier will take care of everything for us. Lets us load the medium font. I'll comment Load the medium font. Then we can first copy these four lines for the small font and paste them for the medium font, then change the small font variables to medium font ones. For the size of the medium font, let's go with 106. Among other things, we'll use the medium font to display the time remaining for the current round. We want to put a border around the characters in this font. To do this, after setting the size, let's site medium font dot font parameters. D border width equals. And for the width, I found that four looks pretty good. We can also set the border color by using medium font, dot font parameters, that border color. But the default color is black, which is what we want, so we don't need to change it. Finally, let's load the large font. I'll comment, Load the large font. Then let's copy and paste the medium font lines. And change the variables for the large font. Let's make the size of the large font 150 and the border width six. Okay, now that we've created the load fonts method, we need to go up to the load method and add a call to the load fonts method so that the fonts will actually get loaded when we run the game. All right, in the next video, we'll put our new fonts to use by displaying the Hood. See. 21. HUD (Heads-Up Display) 1: Wins/Loss Ratio and Difficulty Text: Before we display the Hud, we'll need to create a few variables related to the information that we want to display. So first, let's go into the global variables class, and at the bottom, let's add a section for variables related to colors. I'm going to create a gold color here that we'll use for different things throughout the game, including the background color for the fighters health bars in the Hud. Let's create the variable by typing public static final color, making sure to import the GDX graphics version. Then gold equals new color, 0.94 F, 0.85 F, 0.32 f1f. Next, let's create a section for game variables. And here we're going to create an Enum for the possible difficulty levels of the game. And the reason we're doing it in global variables instead of game screen is that we want the player to be able to change the default difficulty setting through the setting screen, which we'll create later. So we'll need to access the difficulty Enum in both the game screen class and the setting screen class. Okay, so to create the Enum, let's type public Enum difficulty. For the difficulty levels, we want easy, medium, and hard. Next, let's go to the top of the game screen class. And after we create the Viewport, let's set another variable section here labeled game. In this section, we want to create a variable for the difficulty by typing private global variables difficulty difficulty. And by default, let's set it equal to global variables dot difficulty dot easy. Now let's create another section for rounds. And here we want to create a couple of variables to store how many rounds the player has won or lost, and we want to initialize them both to zero. So let's type private and rounds one equal zero. Rounds lost equals zero. We also need a round timer, which we'll display to show how much time remains in the current round. But first, let's create a static final variable to hold the maximum round time by typing private static final float. Max round time equals. Let's set it to 99.99 F. We'll use this to reset the round timer each round. And the reason we're using 99.99 instead of 100 is that when we display the timer, we're going to cut off the decimal part and always show two numbers from 99 down to 00. This isn't completely necessary, but I find it looks better and fits more nicely between the fighter health bars and the Hud. Okay, for the round timer, let's type private float Round timer equals and initialize it to MaxoundT. Next, we'll need to create some variables for getting each of the fonts we created from the asset manager. So first, let's create a section here for fonts. The small, medium and large fonts that we'll get from the asset manager are of the bitmap font type. So let's type private bitmap font. Press Enter to import it, then small font, medium font, large font. We're also going to create a default font color, which we want to be white. For this variable, let's type private static final color. Make sure to import the GDX style graphics version, then default font color. Equals. Now for this, we could do new color, then type in the RGBA values like we've done before. However, the color class actually provides some colors for us. To access them, instead of doing new color, we can type color dot. Then either choose the color we want or type it in. I'll type white. Okay, let's now create a section for a HUD. In this section, we're simply going to add a couple variables for the colors of the health bar and the health bar backgrounds. For the health bar color, let's type private static final color. Health bar color equals let's make it red by typing colour dot Red. For the Health Bar background color, let's type private static final Color. Health Bar Background color. Equals. Let's make it the gold color we created in global variables by typing global variables dot Gold. Now we need to get our fonts from the asset manager and set up some things for them. We'll do this in the game screen constructor. After we create the game area here, I'll comment, set up the fonts. Let's make a call to a setup fonts method that we'll create next. Let's create the setup fonts method after the create game area method by typing private void setup fonts. And here let's first get the small font from the asset manager by typing small font equals game dot assets dot Manager, dot get assets dot Small font. Now, like with our textures, we need to scale the font by the world scale variable. We created in global variables. To do this, we can type small font dot Git data dot set scale, global variables WorldScale. Next, we can set the color of the font to the default font color variable we created by typing small font dot side color, default font color. One more thing we want to do is call small font dot set U integer positions and Pass in False. I'm not sure exactly what this does, and it might not be necessary for all fonts, but for our fonts, if used integer positions is set to true, which it is by default, the letters get all jumbled up. Okay, now we just need to do all of this for the medium and large fonts. So we can simply copy and paste these lines and change the variables to the medium font. Then we could do the same for the large bot. Okay. Now we're ready to display the Hud. We'll do this in a render Hud method that we'll call from the render method. So first, let's go into the render method. After drawing the front ropes, I'll comment, draw the Hud. Then let's call Render HUD. Now we'll create the render Hud method after the render fighters method by typing private void render Hud. Let's first define a Hud margin, which will refer to the space between the hood and the sides and top of the screen. For this, let's type float, Hood margin equals. Let's set it to one F, which will mean one world unit. Next, we'll draw the ratio of rounds one to rounds lost, and we'll do so using the small font. To draw text with Bitmap font, we simply call the Bitmap font objects draw method. So first I'll comment, draw the rounds one to loss ratio. Then let's type small font dot draw. And in this method, we first need to pass in the sprite batch by typing game dot batch. Next, we need to pass in the string of texts we want to draw. So let's type quote wins, colon space, quote, plus rounds one, plus space, space, quote, plus rounds lost. After that, we need to pass in the starting position of the text. When drawing with a bitmap font, the starting position refers to the top left point of the text. Therefore, the position of the text should be height margin. For the Y position, we need to take the height of the viewport and subtract HUD margin from it. Because we're working in world units, we need to use the viewports world height. To do this, we can type viewport Get world height minus HUD margin. Now we can go ahead and run the game to make sure this works. Okay, at the top, it says wins zero to zero. Good. Next, we're going to display the difficulty setting at the top right of the screen, and we'll also use the small font for this. First, I'm going to comment, draw the difficulty setting. Now let's create a string variable called text. Let's set it equal to difficulty, colon space. We're now either going to add the word easy, the word medium, or the word hard to the text variable depending on what the difficulty setting is. Let's do this with a switch statement by typing switch difficulty. A us write case, easy, text plus equals, easy. Then break. Next case medium, text plus equals, medium. Then break. Finally, since hard is the final case, we can just do default. Text plus equals quote hard. Now we can draw the text by first typing small font dot draw game dot batch text Now for the draw position, we want to align the text to the right of the screen with HUD margin spacing between the right side of the text and the right side of the screen. To align text, we'll use a longer version of the draw method. Also, when we align text to the right, the starting position becomes the top right of the text instead of the top left. So for the position, let's type viewport dot Get world width minus HUD Margin. We use the same Y position as the win loss ratio text so that the tops will be aligned horizontally. So let's type viewport dot Get roll height minus Ht margin. Now for the longer version of draw, we first need to pass in a target width value. Target width refers to how wide we want the line of text to be. We don't want to restrict the width, so we can pass in a zero, which will cause target width to be ignored. Next, we need to pass in how we want to align the text. To do this, we type a line, press Ender to import it, then dot and choose the alignment. We, of course, want to align the text to the right so let's choose right. Finally, we need to pass a boolean value for whether the text should wrap. If we set target width to something other than zero, if the width of the text exceeds target width, the text will wrap or move down one or more lines in order to keep the width from exceeding target width. We don't need this in our case, so let's put false. That's it. Now we can run the game and see if it works. Okay, we get difficulty easy aligned to the top right. Okay, in the next video, we'll continue working on the Hud by displaying the fighters health bars. See. 22. HUD (Heads-Up Display) 2: Fighter Health Bars: For the health bars, we first need to set up a bunch of variables for the sizes and positions of the parts of the health bars. These parts include each fighter's name, red rectangles for the health bars, and gold rectangles for the health bar backgrounds. The way we're going to do the layout here is only one way of displaying things like HUD's and UI and Lib GDX. When we create the main menu screen and setting screen later, we'll look at another and usually more efficient way of doing it. Okay, I'm going to comment here, set up the layout sizes and positioning. Well first create a variable for the health bar padding. This will refer to the spacing between the fighter name and the red rectangle in each health bar. So for this, let's type float, health bar padding and set it equal to 0.5 F. Next, we want to set the health bar height or the height of the red rectangle. We want this to be the height of the fighter name text, plus the health bar padding doubled. To get the height of a font, we use the Get cap height method. So let's type float health bar height equals small font dot Get cap height. Plus health bar padding, times two F. Doing it this way will allow us to change the small font size later and have it also change the height of the health bar. Next, we want to set the maximum width of the health bar. This will refer to the width of the red rectangle when the fighter's life is at full capacity. We'll make it so the red rectangle gets shorter as the fighter loses life for a nice visual display of the fighter's current life. For the max width, I found that 32 works pretty well. Type float, health bar, max width. Equals 32 F. Next, let's define the padding of the health bar background, which refer to the amount of spacing between the red rectangle and all sides of the gold rectangle. For this, let's type float, health bar background padding. Equals 0.2 F. Next, for the height of the health bar background, you want to use the health bar height plus the health bar background padding doubled. So let's type float, health bar background height equals health bar height plus health bar background padding. Times two F. Unlike the health bar, we want the health bar background's width to remain the same, regardless of how much life the fighter has. And we want it to be the health bar max width, plus the health bar background padding doubled. So let's type float, health bar background width equals health bar max width plus health bar background padding, times two F. We also want to set a margin between the health bar background and anything above it, like the wins loss and difficulty text. For this, let's do float, health bar background margin top. Equals 0.8 F. Now we just want to set up the Y positioning of everything. First, for the Y position of the health bar background, we want to take the world height of the viewport, then subtract it by HUD margin, then the height of the small font, then the health bar background top margin. And like textures, shapes are drawn from the bottom left, so we also need to subtract the height of the health bar background. Okay, so with all of that in mind, let's type float, health bar background, position Y. Equals a view port D G road height, minus Hud margin, minus small font, DG cap height, minus health bar background margin top. Minus health bar background height. For the Y position of the health bar, we can simply take the Y position of the health bar background and add the health bar background padding. So let's type float health bar position Y equals health bar background position Y plus health bar background padding. Finally, we need to set the Y position for the fighters names. Because text is drawn from the top left, we can take the Y position of the health bar, add the health bar height to it, and subtract the health bar padding. Okay, so let's type float, fighter name position Y equals health bar position Y plus health bar height minus health bar padding. Now in order to draw rectangles and Lib GDX, we have to use something called a shape renderer. Shape renderer is similar to sprite batch, except we use it to draw shapes. And like sprite batch, we only want to use a single shape render in our project. So let's create one in our main game class. At the top, after creating the sprite batch, let's type public shape render. Press Enter to import it, then shape render. Now in the create method, after we initialize batch, let's initialize shape render by typing shape render equals new shape render. I also like with sprite batch, we need to dispose the shape render. So let's go down to the disposed method, after we dispose the batch, that's call shape render dot disposed. Okay, now we can head back over to the game screen class. All right, so another similarity with Sprite Batch is that we have to do all of the shape render or drawing between calls to the begin and end methods. And because we call the render Hud method, in between calls to Batch Dop again and batch Dot end, before we begin drawing with shape render, we have to call Batch Dot end and side render Hood. And after drawing with shape render, we need to call Batch DoP again again because we'll later be adding more rendering here after rendering the hood. Okay? So at the bottom of the render Hood method. Let's type game dot batch dot end. Then game dot shape render dot begin. Now in here, we can pass in a shape type. Shape type can either be filled or line. Filled will fill in the entire shapes we draw with color, and line will just draw a colored border around the shapes, leaving the inner part transparent. Line is the default, but we want our shapes to be filled. So to pass in the filled shape type, we can type shape type precenter dot field. Let's also go ahead and call game dot shape render dot end. Then game dot batch dot begin. Let's go in between the shaped render begin and end calls. C every ready to start drawing with the shaped render? Because the health bar backgrounds will appear at the bottom, let's start by drawing them first. How common here, draw the fighter health Brground rectangles. We first want to set the color of the shaped render to the health bar background color static variable that we created before. To do this, we call game dot shape render dot set color, health bar background color. Now if we type game dot shape render, dot, we can see that we have methods for drawing things like arcs, circles, ellipses, lines, polygons, and rectangles. We, of course, want to draw a rectangle. So let's use the rect method. And here we first want to pass in the X and Y positions of the rectangle. This one will be for the player's fighter. So for X, we simply want to put HUD margin. For Y, we can use the health bar, background position, Y variable that we created. Next, we need to pass in the width and height of the rectangle. We also created variables for these. Let's type health bar background width, C Health bar, background height. That's it. Now we need to draw the one for the opponent spider. Let's type game, do shape render, direct. To get the exposition of this one, we need to take the world width of the viewport and subtract both the HUD margin and the health bar background width. So let's type viewport dot Get rolled width minus Hud margin, minus health bar background width. The remaining values are the same as the other rectangle. So let's type health bar, background position Y. Come Health bar background width, Come a health bar background height. Now let's run the game and make sure it works. Okay, so it appears the rectangles have been drawn really small way down at the bottom left. The reason for this is that like with sprite batch, we need to set the shape renders projection matrix to the viewport cameras projection matrix so that I would know what units to use. We do this up in the main render method. Okay, so here, where we set the sprite batch as projection matrix, I'll change the comment to set the sprite batch and the shape render to use the viewports camera. Then, after setting the BatchsPjection matrix, let's type game, dot shape render, dot set projection matrix. Viewport dot get camera dot combined. Now let's run it. Awesome. Next, we'll draw the red rectangles for the health bars. First I'll comment, draw the fighter health bar rectangles. Then a such shape renders color to the health bar color variable by typing game, dot shape render, dots color, health bar color. Now, when we draw the health bars, the width of the rectangles will depend on how much life the fighters have. To find the width, they can take the maximum width of the health bar and multiply it by the fighter's current life, then divide by the maximum life a fighter can have. Instead of typing all of this out inside the rect method, let's create a variable for it by typing float, health bar width equals health bar max width. Times we'll first create the players health bar. Let's get the players life by calling game dot player dot GET Life then divide by fighter dot MaxLif. Now we can draw the player's health bar rectangle by first typing game dot Shape Render direct. For the position, we want it to be the HUD margin plus the padding of the health bar background. Let's type HUD Margin plus health bar background padding. Then, health bar position Y, C health bar width, co health bar height. Now, we want to set health bar width for the opponent. So let's type Health bar width equals health bar max width times game dot opponent dot get Life. Divided by fighter dot Max Life. Now with the player's health bar, as the player loses life, the health bar rectangle will shorten from the center to the left side. With the opponent, we want it to shorten from the center to the right side. So to get the position, we'll take the world width of the viewport and subtract the HUD margin, then subtract the health bar background padding, then the health bar width. So as the health bar width shrinks, the rectangle will move toward the right. Okay, so let's draw the rectangle by typing game, dot shape render, Direct, viewport dot G world Width. Minus HUD margin, minus health bar background padding, minus health bar width. Then, health bar position Y. Come at health bar width, come at health bar height. Okay, let's give it a try. Nice. And as we hit the opponent, their health bar gets shorter and moves to the right. Perfect. Next, we'll draw the fighter names. This means that we're going to need a Getter method in the fighter class for the fighter's name. So let's head over to the fighter class, and at the top, before the gitter for the position, let's type public string, G name, return name. Let's also go ahead and create a setter for the name by typing public void, set name, string name. This name equals name. Let's also create some Gitter and setter methods for the color. We'll be needing all of these methods when we create the main menu screen later. So let's type public color, get color, return color. In public void set color, color color. This color equals color. Okay, back in game screen, because we use the sprite batch to draw text, we need to draw the names after we call game dot batch dot begin here. First I'll comment, draw the fighter names. Now let's use the small font to draw the player's name by first typing small font dot draw game dot batch game dot player dot G Name. For the exposition, we'll use the HUD margin plus the health bar background padding, plus the health bar padding. So let's type HUD margin plus health bar background padding. Plus health bar padding. And for the Y, we'll use the fighter name position Y variable that we created. Now let's draw the opponent's name by first typing small font dot draw game dot batch game.opponent.gn For the opponent name, we're going to align it to the right of the health bar. This means that the starting position will be at the top right. So for X, we'll take the world width of the viewport, subtract the HUD margin, then the health bar background padding, then the health bar padding. Okay, so let's type viewport dot GetR Width. Minus Hood margin, minus health bar background padding. Minus health bar padding. For Y, let's type fighter name position Y. Next, we'll put a zero for target width, line dot right for the alignment, and false for rap. Let's give it a try. Cowsom. Finally, we just seem to draw the round timer at the center of the hood. First do comment, draw the round timer. We're going to use the medium font for this. Let's site medium font dot draw game dot batch. Now, when we draw the value of round timer, we want to turn it into a string and we don't want to show the decimal part of it. To do this, we can call integer two string, open parenthesis, open parenthesis, int, closed parenthesis round timer. We're doing here is we're first casting the float value of round timer into an integer, which removes the decimal part. Then we're calling the integer two string method on the resulting integer, which will turn it into a string. Next, we want to center the text horizontally in the viewport, and to help with this, we're going to use a line dot center for the text alignment. When we use a line dot center, the starting position of the text becomes the top center point. This means that to center the text horizontally in the viewport, we simply need to set the position to viewport dot Get Rod width divided by two F. F Y, let's use viewport dot G Rod height minus Hut margin. This will align the top of it with the tops of the wind loss ratio and difficulty text. Now let's type zero for target width, align dot center for the alignment, and false for rap. Let's give it a run. Okay, we have our round timer in the center of the Hud with a nice black border around the numbers. One thing to note, though, is that if we go up here and set round timer to something less than ten, like five, then run it. It just displays a five. To make it look nicer, we can make it so it puts a zero before numbers less than ten. To do this, when we draw the text, we need to use the format method of the string class. So let's go back down to render Hud. Let's replace the integer dot two stream call here with string dot format. The first thing we need to pass in here is a local object, which represents a specific geographical, political, or cultural region. There are many things we can do with the format method, and depending on the locale we set for it, the result may be different. For our purposes here, though, the local doesn't really matter, so we can just use the default one by typing locale, pressing nter to import it, then dot get default. Next we need to pass in something called a format string that represents the format we want to use for the text. What we want to do is check if the text is two numbers, and if it's only one number, we want to pad it with a zero on the left. To do this, for the format string, we can type percent sign 02d quote. Percent sine D represents numbers or digits. The two before the D represents how many digits we're looking for, and the zero after the percent sine tells us that we want to pad the text with zeros at the left if we don't have the quantity of digits we're looking for. Passing in a five to this will give us a string of 05. If we were to change the two here to a three, it would give us zero, 05. Like I said, there are a lot of things we can do with the format method and many different format strings we can use here. I highly recommend looking more into the method when you get a chance. Okay. And the final thing we need to pass in here is what we want to format. We want to format the value of round timer, but without the decimal part. So we can pass in round timer, cast it to an integer by typing open parenthesis, int closed parenthesis, round timer. Now, let's run it. All right, we now have a 05 for the timer. And if we set round timer back to Max round time, We get 99. Alright, that's it for the Hood. In the next video, we'll set up our game to start using rounds so that we can see our round timer in action. S 23. Set Up the Rounds: In order to start using rounds in our game, we need to set up a few more variables in the game screen class. Let's start at the top of the game section. And here, we're going to need a variable to indicate what state our game is currently in. The game can be in three possible states, running, paused and finished or game over. So first, let's create an E NUM for these states by typing private Enum game state Running, paused, and game over. Now we need to create a variable for the current game state by typing private, game state, game state. Next, let's go to the top of the round section. A round can also be in three possible states, including starting in progress and ending. So let's create an Enum for the round states by typing private, Enum, round state. Starting in progress and ending. As credit variable to hold the current round state by typing private, round state, round state. We actually also need to keep track of the amount of time around has been in a certain state. This will be used for things like the round timer and for the delays that we put at the start and end of a round. So for this, let's type private float Round state time. Let's also go ahead and create a couple of variables for the starting and ending round delays. First, let's type private static final float. Start round delay equals. Let's set it to F, which will mean a two second delay. Now let's type private static final float, end round delay, and also set it to two F. Next, we need a variable to indicate what round the game is currently in. So let's type private end current round. And finally, let's create a static final variable for the maximum number of rounds a fight can have by typing private static final nt, Max rounds. Let's set it to three. Okay, next, we need to create some methods for starting the game and for starting, ending, winning and losing a round. We'll call the start game method from the show method, so that it will get called each time the game screen becomes the active screen. So let's go down to the show method. And at the bottom, I'll comment, Start the game. Then let's call Start Game. Now let's create the Start game method after the show method by typing private, void, start game. And here we want to set the game state to running and reset both rounds one and rounds lost to zero. So let's site game State equals gamese dot running. Rounds one equals rounds lost equals zero. Next, we want to start Round one. First outle comment, start Round one. Then we want to set the current round to one. By typing current round, equals one. Then we'll start the round by calling the start Round method. Let's create the start Round method next by typing private, void, start Round. The first thing we'll do in here is get the fighters ready for the round by calling their get ready methods. We're actually currently doing this in the constructor. So let's select these lines here and cut them with Control X. Then let's paste them into the start Round method. Next, we want to set the round state to starting, the round state time to zero and the round timer to the maximum round time. First out comment, start the round. Then let's type round state equals round state dot starting. Round state time equals zero F, and round timer equals max round time. Next, we need to create an end round method by typing private, void, end round. Here we simply want to set the round state to ending and reset the round state time. I'll come it end the round. Then type round state equals round state dot ending. Round state time equals zero f. We next need to create a method to call whenever the player wins a round. So let's type private void, win round. In this method, we want to put the player in the win state, the opponent in the lose state, and add one to the rounds one variable. We also want to end the current round. So first I'll comment, player wins the round, and opponent loses. Then let's type game dot player dot win. Game dot opponent dot lose and rounds one plus plus. Next I'll comment in the round. Then let's call round. We now need to create a method for the opposite situation when the player loses the round. So let's type private, void, lose round. And here I'll comment. Player loses the round and opponent wins. Then's type game dot player dot lose. Game opponent dot win. And this time, we want to increment the rounds lost variable by typing rounds lost plus plus. Next, I'll comment in the round. Then let's call in Round. Next, let's go down to the update method. At the top of this method, we want to check a couple of things related to the round state. First, if the round state is on starting, we want to check if the round state time has reached the start round delay. If so, we want to move the round state into the in progress state so the fighters can start fighting. So let's do this by typing if round state is equal to round state dot starting and round state time greater than or equal to start round delay. And here I'll comment. If the start round delay has been reached, start the fight. Then type round state equals round state in progress. And round state time equals zero F. We also want to check if the round state is on ending and the round state time has reached the end round delay. We can do this with an SIF here by typing LCIF. Round state is equal to round state ending and round state time greater than or equal to round delay. If this is the case, we need to decide whether we should move to the next round or put the game in the game over state. With Max rounds set to three, the fight could possibly have three total rounds. However, if one of the fighters has already won two rounds, then there's no point in going to the third round because even if the other fighter wins the third round, they will still lose the game overall. If we were to set Max rounds to five, we would only want to go to the next round if fewer than three rounds have been won by a single fighter. In other words, we only want to go to the next round if fewer than half of Max rounds have been won by a single fighter. Otherwise, the game is finished, so we put it in the game over state. So first I'm going to comment all of this by typing, if the end round delay has been reached and player has won or lost more than half of the max number of rounds in the game, otherwise, start the next round. Now let's check whether the player has won or lost more than half the rounds by typing. I rounds one greater than Max rounds, divided by two, or rounds lost, greater than Max rounds, divided by two. And if this is the case, we'll end the game by typing. Game state equals gamete Game O. Otherwise, we need to start the next round. So let's put outs. Current round plus plus, start round. Alright, now, if neither of these situations is the case, we want to increment the round state time by Delta T. So here let's put outs. And I'll comment, increment the round state time by Delta time. Then let's type round state time plus equals Delta time. Can let's go down to where we check if the fighters or the contact distance. Above this, we want to check if the round state is in progress, and if so, we want to decrease the round timer by Delta time. So let's type if round state is equal to round state in progress. And here I'll comment, if the round is in progress, decrease the round timer by Delta time. Then let's type round timer minus equals Delta T. Another thing we want to do in here is check if the round timer has finished. If so, neither of the fighters has won the fight yet. We'll make it so the fighter with the most life automatically wins, and the other fighter automatically loses. If both fighters have the same amount of life, we'll just let the player win. So first, let's type I round timer less than or equal to zero F, then I'll comment. If the round timer has finished and player has the same or more life than opponent, player wins the round. Otherwise, player loses the round. Ellis type, if game dot player dot G Life, greater than or equal to game that opponent dot get Life. When round, else, lose Round. Also, because we only want the fighters to be able to hit each other when the round is in progress, we can cut all of these lines of code here. I paste them into the I statement where we check if the round is in progress. I and what we call game dot player dot win here, we want to change it to win Round. So we'll also increment rounds one and start the next round. I'll add to the comment here. Player wins the round. All right now we can finally run the game and see what happens. After a two second delay, the round timer starts counting down. It actually appears to be a three second delay, and that's because we start the round timer at 99.99. A three second start delay will actually be good because in a minute, we're going to display some text to tell the player what the current round is, then let them know when they can start fighting. Okay? And if we attack the opponent until they lose, we get a two second delay, then it starts round two. We can also see that round one has increased by one. If we win again, because we won more than half of the Max rounds, it doesn't go to round three. We'll later add an overlay here with buttons to let the player either restart the game or go to the main menu. But first, if we run the game again, we're able to walk around during the start delay. We want to make it so we can only walk around when the round is in progress. To do this, let's head down to the key down method. Let's cut all of the lines for checking if the player has pressed a Momi key. Now let's type I round state is equal to round state in progress, Then pace the lines. We'll still let the player press the attack and block buttons during the delays, though. Okay, now if we run the game? We can't walk around during the start delay. Okay, to finish up this video, let's add some text at the start of the round. We'll do this in a new render method called Render Start Round Text. So let's go up here to below the render Hud method. Let's type private void renders Start Round text. During the first half of the start round delay, we're going to draw some text stating what the current round is. And during the second half, we'll just draw the word fight. So first, let's create a variable for the text by typing string text. Next, we want to check if the round state time is less than half of the start round delay, and if so, we'll draw the round number text. Otherwise, we'll draw the fight text. So let's type I round state time, less than start round delay. Times 0.5 F, text equals round space quote plus current round. Then, text equals fight. We're going to use the medium font to draw the text. We want to center both horizontally and vertically on the screen. So let's call it medium font dot draw game dot batch, text, Comma viewport dot get rolled Width, divided by F, C viewport, DGRd height, divided by F. We want to use center alignment. So let's put a zero for target width, align dot center for the alignment, and false for rap. Okay, now we want to go up to the main render method. And after rendering the HUD, we'll check if the round state is on starting, and if so, we'll draw the start round text. So first I'll comment if the round is starting, draw the start round text. Then this type, I round state is equal to round state dot Starting render Start Round text. Now if we run the game, first says Round one, then fight. And if we win the game, it says Round two, fight. Awesome. Alright, now, one thing you might have noticed is that the text for the round timer sometimes shifts slightly when it changes. In the next video, we'll learn how to fix this. We'll also make it so the text changes color when the round timer reaches a certain critical time. See there. 24. Improve the Round Timer: As we saw in the previous video, as the round timer counts down in the Hud, at certain times, the numbers seem to shift a bit. The reason for this is that the numbers in the Roboto font family aren't exactly the same width. So the width of the text is fluctuating slightly. And because we have its alignment state centered, it appears that the text is shifting. One solution to this is to use a monospace font family, which use characters that all have the exact same width. However, depending on the font, this might leave a bunch of extra space between the numbers, which won't look very good. A better solution is to use left alignment on the text with this left edge at the center of the viewport, then shift the text to the left by the width of one number. This means that it won't be perfectly centered at all times, but it will only be off centered by a very small amount, so it won't be noticeable. Okay, so to do this, let's go into the game screen's render Hut method, where we draw the round timer at the bottom. Because we want to use left alignment, which is the default, we can just remove these last three parameters. Now for the position, we want to take viewport Get Rod height divided by two F here and subtract it by the width of a number. However, because every character and a font can have a different width, we can't just get the width of any character we want. And actually, in Lib GDX, we only have a single method that lets us get the width of a character in a font, and that's the G SpaceX advanced method. So here let's type minus medium font GespaceX advance. This method returns the width of the space character in the font, and in Roboto regular, the space character is less than half the width of a number character. So we need to bootuply this number by something. I found that doing times 2.3 F here works pretty well. If you decide to use a different font family in your game, you will likely have to use a different number here. Okay, let's give it a go. Alright, so the round timer isn't shifting anymore. And even though it isn't always perfectly centered, it's very difficult to tell. Okay? So another thing we can do to improve the round timer is when it gets down to a certain critical time, say 10 seconds, we can change the color of the text, like maybe to red. This will help to get the attention of the player, letting them know the round is about to end. Okay, so to do this, let's first go up to the top of the class and create a couple of static final variables to define the critical round time and the critical round time color. Let's put them at the bottom of the round section. First type private static final float, critical round time. Let's set it to ten F for 10 seconds. Let's us type private static final color. Critical round time color equals and we can set it to red by typing color dot red. Now, let's go back down to the render Hud method. Alright, so before we draw the round timer, we want to check whether the round timer has dropped below the critical round time, and if so, we want to change the color of medium font to the critical round time color. So let's type if round timer, less than critical round time then medium font dot set color, critical round time color. Now, when we change the color of a font, it will remain that color until we change it again. So medium font will continue to be red for all subsequent renders in the game. Therefore, after drawing the text, we need to set medium fonts color, back to the default color that we defined earlier by typing medium font, dot side color, default font color. Can I just go back up to the top and set Max round time to something like 13 F for now so that we don't have to wait too long to see if it works. Then let's run the game. Okay. When the timer drops below ten, it turns red. And when it reaches zero, because the fighters had the same amount of life, the player automatically wins. Okay, let's set Max round time back to 99.99 F. And in the next video, we'll learn how to handle touch input from the player so that they can press buttons on the screen, as well as skip the start and end round delays by touching the screen. See there. 25. Handle Touch Input: It's nice to have a short delay at the beginning of a round, letting the player get ready for the fight. But sometimes the player might just want to skip the delay and get straight to the action. We're going to let the player do this by touching or clicking the screen during the delay. Therefore, we need to learn how to handle touch and mouse input from the player. Below all of the keyboard handling methods at the bottom of game screen, we have five methods for handling touch and mouse input. Touchdown, touch up, touch, dragged, mouse move, and scrolled. Touchdown is called when the player either touches the screen with their finger or clicks it with the mouse. Touch up is called when the player either removes their finger from the screen or releases the mouse button. Touch dragged is called when the player drags the finger across the screen. Mouse moved is called when the player moves their mouse across the screen, and scrolled is called when the player scrolls their mouse well up or down. In the desktop version of our game, we only need to use the touchdown method. The touchdown method contains four integer parameters, screen X, screen Y, pointer and button. Screen X and screen Y refer to the X and Y coordinates of where the touch or click occurred. This is in pixels, and the origin of the screen is at the top left. So in order to get the location where the touch occurred in the viewport, we need to convert these coordinates into world units with the origin at the bottom left. Fortunately, LibGDX makes this pretty simple to do. Next, the pointer parameter refers to which finger or pointer the player used when touching the screen. This allows the player to use multiple fingers on the screen at once and is really only useful for touchscreen devices like mobile phones. On a desktop, the player will likely be using a mouse to touch the screen, so we don't need to worry about handling multiple pointers. Finally, if the player is using a mouse, the button parameter will refer to which mouse button they clicked. This lets us perform separate functions for left clicks and right clicks. This won't be necessary in our game, so we'll just handle all mouse buttons the same way. Okay, so let's see how we can use the touchdown method to let the player skip the round delays. The first thing we're going to do is convert the screen coordinates of the touch into world coordinates. We'll do this with the unproject method of the camera class. The unproject method actually works with both two D and three D, so it requires that we pass in a vector three variable containing the coordinates we want to convert. Therefore, we first need to put screen X and screen Y into a vector three variable. I'm going to comment here, convert the screen coordinates of the touch click into world coordinates. Then it's type vector three, press Enter to import it. Then position equals new vector three, screen X, screen Y. And because our game is two D, we won't be needing a Z coordinate, so we can just put a zero. Next, we can call the unproject method on the viewport camera by typing viewport get camera to Unproject. And here we need to pass in the position variable. Then, viewport dot get screen X. Ca viewport dot get screen Y. Come a viewport that gets screened with. Come a viewport that gets screened height. This method will actually replace the screen coordinates in the position variable with the world coordinates. So now we can use the position variable to check exactly where in the viewport the player touched or clicked. We won't actually be needing to do this until we add buttons to the screen a bit later, but I figured we should go ahead and get it out of the way now. All right, so when we check if a screen press should skip around delay, we only want to do so when the game is in the running state. If it's in the pause or game over state, we'll have some buttons on the screen for the player to click. So we'll handle touch input differently in those cases. So let's first check if the game is in the running state by typing if Game State is equal to gamete dot running. Now, if the current round is in the starting state, we want to set the round state time to the start round delay. Similarly, if it's in the ending state, we want to set the state time to the end round delay. So let's first check if it's in the starting state by typing if round state is equal to round state dot starting. I'm going to comment here if the round is starting and the screen has been touched, skip the start round delay. Tins type, Round state time equals start round delay. As two out Sif. Round state is equal to round state do ending. I'll comment if the round is ending and the screen has been touched, skip the end round delay. Tennis type Round state time equals end round delay. Finally, like with the keyboard methods, we want to change return false down here to return true, to let LibGDX know that we handled the touch event ourselves. Okay? Before we run the game and give this a try, let's go to the top of the fighter class. And temporarily set Max's life to five F, so we only have to hit the opponent once to beat them. Now if we run the game, we can click the screen to skip straight to the start of the fight. If we beat the opponent, we can click to skip the end round delay and click again to skip the start round delay. If we beat the opponent again, though, the game is finished and we can't do anything else. Now that we know how to handle touch events, we'll soon be adding some buttons on the screen to let the player choose what to do next. But for the moment, let's make it so we can press the space bar to restart the game. We'll also make it so we can press the spacebar to skip the round delays, since having to click the screen on the desktop can sometimes be a pain. Let's first go into the key down method of the game screen class. At the top of the method, a check of the space bar has been pressed. We can do this by typing if key code is equal to input dot keys dot space. Now we need to decide what to do, depending on the state of the game. If it's in the running state, we'll check if the round state is in either the starting or ending state, and if so, we'll skip the delay. If the game is in the game over state, we'll restart the game, which we can do by simply calling the start game method that we created earlier. When we make it so the player can pause the game later, we'll also make it so pressing the space bar, we'll resume the game, but we'll leave that for another video. Let's first check if the game is in the running state by typing, I Game state is equal to gamestate dot running. And here I'll comment, if the game is running and the space key has been pressed, skip any round delays. Now let's check if the round is in the starting state by typing, I round state is equal to round state dot starting. And if so, let's set round state time equals start round delay. Now let's do out Sif. Round state is equal to round state dot ending. Round state time equals round delay. Okay, now, after checking if the game is running, let's check if the game is over by putting Out SIF. Game statate is equal to Gametate Game O. I'll comment. If the game is over and the space key has been pressed, we start the game. Then let's call Start game. And actually, if the space bar has been pressed, there's no point in checking if all of these other keys have been pressed. So let's put an outs here. Then let's cut all of these other lines before the return true line and paste them into the outs part. Et's give it a run. If we press the space bar, we can skip the delays. At the end of the game, we can press the space bar once to skip the delay and again to go back to round one. Cool. One thing you might have noticed, especially if we maximize the window, is that the edges of the textures and the text look pretty jagged. In the next video, we'll see how we can fix this by changing something called texture filters. See there. 26. Texture Filters: Texture filters and Lib GDX basically define the algorithm used for scaling a texture up or down when rendering it to the screen. Each texture we create has two changeable texture filter settings, Mag filter and Min filter. Min filter or minification filter defines the algorithm used when scaling down the texture, and Mag filter or magnification filter defines the algorithm used when the texture is scaled up or magnified. For both Min filter and Mg filter, there are several texture filters we can choose from nearest, linear, and some options for MIT Maps. MID maps are mainly used in three D, because we're focusing on two D in this course, we'll just talk about the nearest and linear texture filters in this video. The nearest texture filter is the default filter that LibGDX uses. When a texture is scaled using the nearest filter, for each pixel in the scaled image that gets rendered, it will simply choose the color of the pixel nearest to that point in the original image. This is the least CPU intensive filter. However, using it can lead to a loss in quality because some of the colors from the original image might not be used in the rendered image. The textures in our game are relatively large, so for most screens, they'll be scaled down. This is why when we run the game, the edges of the textures appear jagged. This could be what we want if we were using pixel art or didn't care much about image quality, but if we want smoother edges, we need to use the linear texture filter. When choosing the color of a pixel for a scaled image using the linear filter, instead of just using the single nearest pixel in the original image, like the nearest filter does, linear takes the nearest four pixels and blends them together, then uses the resulting color in the scaled image. This tends to produce a better looking result, especially around the edges of the textures, because the blending makes the edges look smoother. Now if we want to set the texture filters in our game, there are two different ways we can do it. First, we can change the filters of a particular texture by using the set filter method of the texture class. Second, we can change the filters for all of the textures at once when we load them with the asset manager. Let's first see how we can change the filters of a single texture by changing them for the background texture. To do this, let's go into the create game area method of the game screen class. After we get the textures from the asset manager, let's call background texture. Got set filter. First, we can set the Min filter, and to do so, we can type texture filter, press Enter, then dot. Now we can see all of the available texture filters. Let's go with linear. Next, we can set the Mg filter. Unless we test this on a very large screen, we won't be able to tell the difference when changing Mg filter because the textures are all going to be scaled down. But in any case, we can go ahead and set it to linear as well. I'll just type linear and press Enter. Now if we run the game, we can see that the edges of the background image appear smoother, especially along the ropes. If we comment this line out and run it, the edges go back to being jagged. Okay. Now let's see how we can set the texture filters for all of our textures. First, I'll delete this line of code. Now let's head over to the assets class. Okay? So when we load textures with the asset manager, like we're doing here in the load gameplay assets method, we can actually pass in another variable at the end of the load method for various parameters we want to use when loading the asset. The type of object we pass in here depends on the type of asset we're loading. For texture assets, we use a texture parameter object, and we can share it with all of the texture assets that we load. To create a texture parameter object, let's go to the top of this method. Let's type texture parameter. Press Enter to import it, then parameter equals new. Now just press Enter here to fill in the code. Now if we type parameter dot, we can see a few parameters that we can set, including Mg filter and Min filter. Let's set Min filter to linear by typing Min filter, equals texture filter, pressing Enter, then dot linear. We can also go ahead and set Mag filter by typing parameter, dot Mag filter, equals linear and pressing Enter to fill it in. Okay, now we can add the parameter object to the end of each of the lines where we load a texture asset. I'll copy this part here and paste it into the other lines. Now, we don't want to put this in the lines for loading texture atlases, because texture atlases are a bit different. With texture atlases, we can actually set the texture filters when we create the atlases. We'll learn all about texture atlases in the next video. But for now, let's run the game and see how it looks. Okay, so all of our textures are looking pretty smooth now. Nice. The text, however, still does not look very smooth, so let's fix that. If we go down to the load fonts method, when we create the fonts here, it actually generates textures for them, and we can set their texture filters, and we can do it using the free type font loader parameter object that we've already created for each font. So first, before we load the small font, let's type small font, D font parameters, dot Min filter, equals linear and Presenter. Then let's type Small font, D font parameters. Dot Mag filter equals linear and pre center. Now we can copy these two lines and paste them for the medium font. And change small font to medium font. Then do the same for the large font. Alice run the game. Alright, even our text is looking pretty smooth, now. Okay, in the next video, we'll learn what texture atlases are and how to create them. See there. 27. Texture Atlases & Packing Textures: If we go into the textures assets folder, we have a few assets with the dot Atlas extension. And for each one, we have an image file that shares the same file name. Each of these pairs of files form what's called a texture Atlas. If you open up one of the image files, for example, gameplay buttons dot PNG, we can see that it's basically a single large image file consisting of multiple smaller images. In this case, the textures for the buttons we'll use in our gameplay. If you open up the gameplay buttons dot Atlas file, we can see that this file holds some information about the gameplay buttons dot PNG image, including its size and format, as well as the texture filters to use when loading the textures. We then have the names of all the textures inside the image file, along with whether or not they should be rotated, the XY coordinates of the texture inside the image file and their size. Now the reason it's a good idea to combine multiple texture images into a single image file like this is mainly that it reduces the number of textures that need to be loaded into memory. This reduces memory usage and improves performance. Putting textures together into a texture atlas is called packing textures, and LibGDX provides us with a texture packer class containing methods we can use to pack textures. Packing textures from within the IDE, however, can get pretty tedious. Fortunately, the guys over at crashmbders.com created a utility called GDX Texture Packer, which is a visual wrapper over the LibGDx Texture Packer class. To find the GDX texture Packer, we can go to github.com slashASHEbdRS, slash GDXTextPacker GUI. You can also just Google GDX Texture Packer, and this page should pop up in the top couple of results. Okay, here, we can go through all of the code for the GDX texture Packer if we want. However, if we just want to get straight into packing textures, we can go down here to the using the app section and click the releases page ink. We next get a list of the current and previous versions of GDX Texture Packer. At the time of recording this video, the current version is 4.11 0.0, and to download it, we have some links down here. I'll choose the EXE version. After the file finishes downloading, we can run it to begin the setup. We might get a warning that running the app might put our PC at risk, but GDX Texture Packer is officially supported by the creators of LBDX so we don't have to worry about this. I'll click More Info, then click Run Away. We can agree to the license agreement, choose whether we want to create shortcuts in the start menu or desktop. Then choose an install location and install it. I already have it installed on my computer, so I'll just click Cancel. But once you get it installed, let's head over to the installation folder. In the installation folder, we have a GDX texture Packer file. This is an executable jar file. So like with the Lib GDX project generator, we can simply double click it to open it. We can also use the command Java jar, GDX,texturepacker, dot jar. All right, so at the top left of the application, we have an atlas section. The first thing we want to do is create a new atlas by clicking this button that says create Atlas. And here we can give the Atlas a name. We're going to be recreating the gameplay buttons Atlas in this video, so let's call it gameplay buttons. Click Okay. Okay, we now see our gameplay buttons Atlas in the list here, and we get a whole bunch of settings that we can change. Before we talk about the settings, let's go ahead and add all of the gameplay button images to the source file section here. Within the downloadable resources that I provided with the course, we have this packing Textures folder. In this folder are all of the separated images that we saw combined together to form the gameplay buttons textualis. We'll use these images to recreate the gameplay buttons textualis and we need to import them into the GDX texture Packer. To do so, we can either click the ad input files button in here or we just simply select all the images and drag and drop them into the blank space down here. Also in the export section, we want to go ahead and set the output directory, which is where the texture atlas files will be saved. As this is just for testing purposes, it doesn't really matter what we choose for the output directory. I'll just use the installation folder of the GDX sextupator. Okay. Now let's talk about the settings. If you would like to see what any of them do, you can hover over it and some information about it will pop up. Fortunately, though, there are only a few settings that we need to focus on. First, we have a Min filter and MAG filter. As we learned in the previous video, these are for setting the texture filters. The default for both is nearest, but for better results, we should use linear. Next, we have padding X and padding Y. If we take a look at the gameplay buttons image in our project again, we can see that there's actually a small amount of spacing between each of the textures. This is called padding, and it's a good idea to use it, especially when using the linear filter. This is because if the textures are right up against each other, the colors from the edges of the textures will start to bleed into each other when the textures are scaled. I find that the default of two pixels for both padding X and padding Y works pretty well. Next, we have a bunch of checkboxes. The edge padding option here puts half of the padding X and padding Y settings around the edges of the entire pack texture. This is good because it allows for anti aliasing around the outside edges of the textures, which keeps the edges looking smooth. We should also make sure to have the bleeding option here checked because otherwise, no anti aliasing will be used between the edges and the transparent pixels of the padding. Another option we have here is force POT. POT stands for power of two. Power of two textures are textures in which both the width and the height are a power of two, such as four by four, eight by eight, 128 by 256, et cetera. In the past, due to GPU limitations, many devices were only capable of rendering power of two textures. The vast majority of devices nowadays can handle non power of two textures, but it's still a common practice to make game textures power of two. The POT option here simply forces the outputed packed image to be power of two. As we can see in the project, the gameplay button image is 1024 by 512 pixels, which is power of two. If the total width and height of the textures don't add up to powers of two, it will simply add in some transparent spacing in the image. Now that we understand the most important settings in the GDX texture packer, and we have everything set up the way we want it, let's go ahead and pack the textures by pressing the pack button here at the right of the atlases section. Once it's finished processing, we can close out this dialog, and now we see a preview of the packed image here. If we hover over one of the textures, we can see that it uses the name of the source image file without the extension as the name of the texture. And it also tells us the size of the texture and pixels. If we go into the output directory, we can see that it created both a Gameplaybtons dot PNG file and a gameplaybtons dot Atlas file, which contains all the information we need for rendering the various textures to the screen. Okay, we can go ahead and close out the GDX texture packer now. And by the way, we can also save the current texture packer project. In case we want to change some settings later or add more textures, I'll just choose no. Alright, in the next video, we'll learn how to put the gameplay buttons texture atlas to use by creating a game over overlay at the end of the game with buttons that the player can click. See there. 28. Game Over: All right, so at the end of a game, instead of just having the winning fighters win animation run on for all eternity, we want to put some kind of text on the screen to let the player know that the game is over, as well as give them some buttons that they can press to either restart the game from the beginning or return to the main menu. So in this video, we'll do just that. First, we need to go to the top of the game screen class and create a few variables. Let's add a section after the fighters section called buttons. For the game over overlay, we'll need two buttons, a play again button to restart the game and a main menu button to go to the main menu screen. We, of course, don't have a main menu screen yet, so the main menu button won't do anything, but we can still draw it on the screen. For the buttons, we're going to create Sprite objects. A sprite object's main purpose is to hold a texture, but it also holds information about the texture that we can set, including its draw position, draw size, and draw rotation. More importantly, for our purposes, however, is that it has a method that we can call to check if the player has clicked inside the Sprite. Okay, let's first create the continue button Sprite by typing private Sprite pressing Enter to import it, then play again button Sprite. Let's do the same for the main menu button sprite. Private Sprite, main Menu button Sprite. Next, we need to set up the sprites by getting their textures from the asset manager. We'll do this in a create buttons method that we'll call from inside the constructor. So after the line, we we call the setup fonts method, I'll comment, create the buttons. Then let's call Create buttons. And below setup fonts. Let's create the method by typing private void, create buttons. Now the textures that we need for the buttons are inside the gameplay buttons texture atlas. So we first need to get the texture atlas from the asset manager. I'll comment here, get the gameplay button texture atlas from the asset manager. Then let's type texture atlas, press Enter to import it. Then button texture Atlas equals game dot assets dot manager dot Get assets dot Gameplay Buttons Atlas. Now, when we initialize a sprite, we need to pass in either a texture or a particular texture region from a texture atlas. Because we're using a texture atlas, we need to get texture regions from it, which we can do by calling it Fine region method and passing in the name of the region we want. If you open up the gameplay buttons dot Atlas file, we have four texture region names in here. Continue button, main menu button, pause button, and play again button. For the moment, we want to get the play again button and main menu button regions. So back in game screen, let's first initialize the play again button Sprite. I'll comment, create the play again button then let's start by typing. Play Again button Sprite equals new Sprite. And now we need to pass in the Play Again Buttons texture region by typing button texture Atlas dot Fine Region. Play Again button. We can also go ahead and set the size of the sprite, scaling it down by world scale so that we don't have to do it in the render method. To do this, we call Play Again button Spriteset size. And for the width, let's type play again button Sprite, dot get width, times global variables, that world scale. And for the height, play again button Sprite, dog height times global variables that world scale. We now just need to do all of this for the main menu button. So I'll comment, create the main menu button. Then type Main Menu button Sprite equals new Sprite. Button texture atlas dot Fine Region. Main Menu button. And main menu button sprite that sets size, main menu button Sprite dot get width, times global variables, dot world scale. Come main menu button Sprite dot get height. Times global variables, world scale. Okay, now we need to actually draw these buttons along with some text when the game is over. We're going to do this in a render game overlay method that we'll call from the main render method. So let's first go to the main render method. Near at the bottom, where we check if the round is in the starting state, we first want to check if the game is in the game overstate. If so, we'll draw the game over overlay. If not, we'll then check if the round is in the starting state. So we can select all of these lines here and cut them with Control X. Now comment. If the game is over, draw the game over overlay. Now let's check if the game is in the game over state by typing I GameSte is equal to Gameste GameOver if so, let's call Render GameOver overlay. Now we can put a notes here, then paste the lines that we cut. Okay? Now below all of the other render methods. Let's create the render game over overlay method by typing private void render game over overlay. The first thing we'll do in here is make the game area and the hud darker so that the buttons and texts that we overlay on top of them will be easier to see. To do this, we can draw a rectangle that's the same size as the screen. And when we set the color of the rectangle, we'll make it black with an Alpha value that is less than one, which will make it partially transparent. So first I'll comment, cover the game area with a partially transparent black rectangle to darken the screen. Now to draw a rectangle, we of course need to use the shape render. This means that we first need to call the end method of the sprite batch, then called the begin method of the shape render. So let's type game dot batch, dot end game dot shape render, do begin. Remember that by default, shape renderer draws only an outline around the shapes. So let's make it draw filled in shapes by typing filled and pressing Enter to auto complete. And so we don't forget to do so later. Let's also go ahead and call game dot shape render, do end and game dot batch, do begin. Now let's go in between these lines of code. Then let's set the color of shaped render to black by typing game dot shape render, dot set color. 00 comma zero. And for the Alpha channel, let's go with 0.7 F. This will make it 70% opaque. Now let's draw the rectangle by first typing game dot shape render direct. We want to put it at 00, and for the width and height, let's do viewport do get rolled width, viewport, do Gerold height. So that covers the entire game screen. Run the game right now, however, and beat the game, The screen just turns black. This is because the blending of textures in LibGDX is disabled by default. To enable it, we have to do so before the call to the shape renders begin method by typing GDX dog dog Enable GO, pressing Enter to import it, then typing GL Blend. And there are actually many different ways to blend textures in LibGDX, we also need to tell it how to blend the textures. We do this by calling the GDX dotg dot G Blend Funk method. Here we have to pass in two integer values, one for the S factor or source blending factor, and one for the D factor or destination blending factor. These tell LibGDX how to blend the source or incoming RGBA values with the destination RGBA values, which are basically the colors that the source colors will be drawn on top of. To see all of the blending options, they can type Go 20 dot. Now, these aren't all blending options, but a lot of them are. Such as the ones that start with GODST that start with Go one and the ones that start with GOSRC. When you get a chance, I recommend trying different combinations of these to see where you can get. But for now, what we want to choose for the S factor is GoSRC Alpha. For the D factor, you want go 20 G one minus SRC Alpha. Now, after the call to Shape Render dot n, you want to Disable blending again by typing GDX dog dot G disable got GO blend. Okay, if you run the game now and be All of the textures become darker. Right now, let's work on getting the text and the buttons on the screen. First, let's set up the dimensions for the entire layout in which will place the text and buttons. I'll comment here, calculate the layout dimensions. Then this type float, text margin bottom equals two F. This will refer to the spacing between the text and the button below it. Lexus type float button spacing, equals 0.5 F. Which will refer to the spacing between the two buttons. We can now calculate the height of the layout. We're going to use the large font to draw the text, so the layout height will be the cap height of the large font, plus the text margin bottom, plus the height of the play again button sprite, plus the button spacing, plus the height of the main menu button sprite. So let's type float, layout height. Equals large font, do Git cap height, plus text margin bottom, plus play again button sprite. Do get height, plus button spacing, plus main menu button sprite. Dog height. Finally, we want to calculate the Y position of the layout. We're going to center it vertically in the viewport. So for the Y position, we'll take half the world height of the viewport and subtract half the layout height from it. So let's type float, layout position Y equals viewport, DGRd height. Divided by F minus layout height divided by two F. Alright, so when we set the positions of the parts of the layout, we're going to start from the bottom and move up. So first the main menu button, then the play again button, then the text. This way, we can set the main menu buttons y position to be the layouts wy position. Then use the main menu buttons position to help position, the play again button and so on. So first I'll comment, draw the buttons. Then I'll set the main menu buttons position by typing main menu buttons sprite, do set position. For X, we want to center it vertically in the viewport, which we can do by typing viewport dot G roll with, divided by F minus main menu button Sprite, dot G W, divided by F. And for Y, we can just use layout position Y. Now, to draw a Sprite, we simply have to call the Sprites draw method and pass in the sprite batch. So main menu button Sprite dot draw, game dot batch. Now let's set the play again buttons position by typing play again button Sprite. Do set position. Again we want to center vertically, so let's put viewport dot Get world with divided by F minus play again button Sprite Dt get Width, divided by two F. For Y, we can use layout position Y plus the height of the main menu button, plus the button spacing. So main menu button, sprite, do get Y, plus main menu button sprite Dog height plus button spacing. Then we can draw the play again button by typing play again, button Sprite dot draw game dot Batch. This is why it's convenient to use Sprites when dealing with things like UI. Now we just need to draw the text. First I'll comment, draw the text. The text that we draw will depend on whether the player has won or lost the game, which we can determine by comparing rounds one to rounds lost. Let's do this with a ternary statement by typing string text equals rounds one greater than rounds lost. Question mark, quote one. You lost. So if the player has won more rounds than they lost, the text will be one. Otherwise it will be lost. Now we can draw the text using the large font by first typing large font dot draw game dot batch, text. We're going to center align the text and also center it vertically in the viewport. So for X, we can simply put viewport Get world width divided by two F. For Y, we need to take the Y position of the play again button, add the height of the button, then add text margin bottom. And because text is drawn starting from the top, we also need to add the large fonts cap height. So it's like play again button, sprite, Doll get Y, plus play again button sprite. Dull get height, plus text margin bottom. Plus large font, thou get cap height. Now we'll put a zero for Target width, Align dot Center for the alignment, and phosphor RAP. Okay, I think we're ready to give this a try. Alright, we got our UO text and our two buttons. Of course, the buttons don't do anything yet, though. So let's see how we can make the play again button. We start the game when we click it. Let's set down to the touchdown method. After we check if the game is running here, we can add an outs to this statement. And here we want to check whether the game is in the game over state and whether the player has clicked or touched inside the play again button sprite. So first, let's type, I GameSte is equal to Gameste dot GameOver and to check off a point is inside the bounds of the play again button Sprite, we can call play again button Sprite, D Git bounding rectangle. That contains. We can then pass in the X and Y coordinates of the point we want to check. We want to use the point where the player pressed the screen after the point has been converted to world coordinates. We converted the point to world coordinates at the top of this method by calling the unproject method on a viewpoards camera. Unproject puts the converted coordinates inside the position variable that we passed into it. So in here we can type position dot X, come on position dot Y. And if all of this is true, we can restart the game by calling the start game method. First I'll comment. If the game is over and the play again button has been touched, start the game from the beginning. Then let's call it Start Game. Now, let's give it a try. Okay, if we beat the game, we can now click the play again button to restart the game. In the next video, we'll see how we can pause the game and add a pause overlay to the screen. See. 29. Pausing the Game: I the player pauses the game, we want to stop all action within the game. Like with the game overstate, when the game is in the pause state, we'll display an overlay on the screen with some texts and buttons. The buttons will consist of a continue button, which will let the player resume the action immediately where they left off and the main menu button for returning to the main menu screen, once we create one later in the course. We'll also, of course, need a pause button, which we'll display at the bottom right of the screen at all times. All right, so let's begin by creating the variables we'll need at the top of the game screen class. First, we'll need two more sprites for the continue button and the pause button. So in the button section, let's type Private Sprite, Continue button Sprite. Then private sprites, Pause button Sprite. Let's also set a variable for the margin of the pause button, which will represent how much space to leave between the pause button and the bottom and right side of the screen. For this, let's type private static final float. Pause button margin equals 1.5 F. Okay? Now, like we did with the other buttons, we need to get the continue button and pause button texture regions from the gameplay buttons textualis. So let's go down to the create buttons method. We can copy the lines here for creating the main menu button. Then let's paste them down here. This will be for the continue button, so I'll change the comment. Change the region name to Continue button. And make it so all of these methods are called in the Continue button Sprite. Now let's do the same for the Pause button. Next, let's go to the render method. Near the bottom, after we draw the Hud, but before we check if we should draw the game over overlay, we're going to draw the pause button. We'll create a method for this called Render Pause button. So first I'll comment, draw the pause button. Then let's call Render Pause button. Now, inside this s part of the If statement, where we check if the game is in the game over state, we'll check if the game is in the pause state, and if so, we'll draw the pause overlay. If we want to do this after the If statement for checking if we should draw the start round text, because if we do draw the start round text, we want the pause overlay to be drawn on top of it. So after this I statement, I'll comment, if the game is paused, draw the pause overlay. In this type, if GameState is equal to gamete dot pas, let's call Render pause overlay, which we still need to create. Okay, now let's go down here before the Render GameOver overlay method. Let's create the Render Pause button method by typing private void render pause button. First, we want to set the position of the pause button sprite. So let's type Pause button Sprite, D set position. For X, we want to take the world width of the viewport and subtract both the pause button margin and the width of the pause button sprite. So let's type viewport, Dot get rolled width minus pause button margin, minus pause button sprite Doll get width. For Y, we can simply put Pause button margin. Now let's draw the Pause button sprite by typing Pause button, Sprite, Da drawGame Da Batch. Okay, let's now go below the render GameOver overlay method. And create the render pause overlay method by typing private void render Pause overlay. This method is going to be almost the same as render game over overlay, so we can first copy and paste all of the lines from that method. We can leave all of the stuff for darkening the screen the same. For the layout dimensions part, we just need to change play again button Sprite here to continue button Sprite. When we draw the buttons, we, of course, want to draw the continue button sprite instead of the play again button Sprite. And for the part where we draw the text, we can get rid of the string text line here. And inside the large font dot draw call, we can change text here to quote game pause. We also want to change the play again button spray instances to continue button spray. Okay, if we run the game now, We get the pause button at the bottom right, but we haven't yet set it to do anything when the player clicks it. So let's do that now inside the touchdown method. We only need to check if the pause button has been touched when the game is running. So we'll check inside the If statement here. However, we also want touching the pause button to take precedent over skipping the start and round delays. So above the inner if statement, let's type, I pause button sprite Git bounding rectangle. Do contains position dot X come position dot Y. I'll first comment, if the pause button has been touched, pause the game. For pausing the game, we'll create a method called pause game. Let's type pause game here. And we actually want to let the player pause the game even during round delays without skipping the delays. Therefore, we need to combine these if statements together by moving this line up to the closing curly brace above it and adding s here. Before we create the Pause game method, let's also go ahead and set up the Continue button to resume the game when it's clicked. We only want to check if the continued button has been clicked, if the game is currently in the pause state because that's the only time the continued button will be visible. So we can add on to this IF statement where we check if the game is over, and the play again button has been clicked. Let's do so by adding CIF here. Then typing GameState is equal to gamestate dot Pause and Continue button Sprite Git bounding rectangle that contains position dot X, come on position dot Y. I'll comment. If the game is paused and the continued button has been touched, resume the game. For resuming the game, we'll create a resume game method. So let's type Resume Game. Now let's go up here below the Start game method. Let's create the pause game method by typing private void pause game. We'll be adding a few things to this method later, but for now, all we need to do is set gamete equal to gamesate dot pas. Similarly, let's create the resume game method by typing private void resume game. And for now, we just need to put Gametate equals gamest dot running. Okay, if we run the game now, we can click the Pause button, which will bring up the pause overlay and we can click the Continue button to resume the game. However, if we click the Pause button again, we can see that it doesn't actually pause the game. The round timer is still going and we can still move around. To fix this, let's take a look at the update method. The update method gets called every time the render method gets called, which is every frame of the game. Inside this method, we do things like called the update method for the fighters, which updates things like their movement and animations. And when we make these calls, we pass in Delta T, which as we learned before, is the amount of time that has passed between the previous render and the current render of the game. We also use Delta T to change things like the round state time and the round time each frame. Therefore, in order to essentially freeze the game, all we need to do is pass in a zero for Delta time when we call update at the top of the render method. We mainly want to freeze the game when the game is paused, and it's not completely necessary, but we can also do so when the game is over. Therefore, we can make it so that if the game is running, we'll pass in Delta to the update call. Otherwise, we'll pass in zero. Let's do this for the ternary statement by typing Gamesate is equal to gamestate dot Run Question mark Delta Colon zero F. I'll also add to the comment here, Delta time should be zero if the game isn't running to freeze the game. Now if we run the game and click the pause button, the round timer and animations freeze. Also, if we beat the game, once the game over overlay pops up, the fighters when animation freezes. Let's also make it so that the player can press the P key to either pause the game if it's running or resume it if it's paused. To do this, let's go to the key down method. Before the s here, we can add SIF to check if the game is in either the running or the pause state and the P key has been pressed. So let's type ECIF, open parenthesis, open parenthesis, Gamese is equal to Gamestate dot running. Or game State is equal to games state dot paused. Closed parenthesis, and key code is equal to input dot keys dot P. I'll comment if the game is running or paused and the P key has been pressed pause or resume the game. Now we'll check if the game is running, and if so, we'll call the pase game method. Otherwise, we'll call the resume game method. So let's type I Gameste is equal to Gamestate dot running, Pause game Outs, resume game. And for fun, let's also make it so we can press the space bar to resume the game when it's paused. We're already checking up here if the space bar has been pressed, and inside here, we're already checking if the game is either running or over, so we can simply add an outs here for when the game is paused. I'll comment, if the game is paused and the space key has been pressed, resume the game. Now, let's call Resume Game. Okay, now if we run the game, we can press P to pause and P again to resume it. And if we pause again, we can also press the space bar to resume. One more thing we can do is make it so when we minimize the game window, it pauses the game. Right now, if we minimize the window and bring it back up, we can see that the game just continued on like normal. To handle what happens when the window gets minimized, we use the pause method of the screen class. And here we want to check if the game is currently running, and if so will post. First I'll comment, if the game is running, pose. Then this type, I GameState is equal to Gamestate dot Run, Pause game. And when we bring a minimized window back up, the resume method is called. So we could call the resume game method in here if we wanted to, but I think it would be better to let the player resume the game when they're ready, so I'll leave mine empty. Okay, now if we run the game, minimize it and bring it back up. We can see it has been paused. Alright, in the next video, we'll liven our game up a bit by adding some music and sounds. See. 30. Add Sounds & Music: Before we can add sounds and music to our game, we of course need to first load the sound and music assets. So let's head over to the assets class. After the load fonts method, we're going to create a method for loading all of the audio assets that we'll need. Let's call it load audio and create it by typing private void load audio. If we take a look at the audio assets folder, we have six files. Five are MP three files and consist of sounds like cheer, click, and hit. The last one is an OGG file called music dot gg. This file is actually just a very short audio clip. If we loop this clip, it will play seamlessly and actually sound like music. And the reason we're using an OGG file instead of an MP three file, like for the sounds, is that when we encode audio data to MP three, it actually adds a very short pause at the beginning of the file. This isn't good if we want the sound to be a seamless loop. Therefore, we need to use something other than MP three, like OGG or WAV. Okay And when we load audio assets with the asset manager, for sound assets, we want to use the sound class, and for music assets, we want to use the music class. So in here, let's start with block.p3 by typing manager dot LOG and since we already created variables, pointing to the audio file locations, we can pass in Block sound. Next, we need to pass in the type of asset we're loading. So let's type sound, press Enter to import it, then Dow class. Next, we'll load the Bo file the same way. Then click. Then here. And finally hit. To load the music file, we can type manager, dot load, Music. And for the type, let's type music, press center, then dot class. Okay, now we just need to go up to the load method and add a call to the load audio method. We're next going to create an audio manager class for handling all of our audio. Let's do this in the resources package by right clicking the package, going to new Java class, calling it audio Manager and pressing Iter. The first thing we want to do in here is create a bunch of variables for all of the audio assets and audio settings. Let's start with the settings, our first comment settings. We only need two settings, one for indicating whether the music is enabled, and one for whether the sounds are enabled. So first, let's type private Boolean music enabled. When we add a setting screen later in the course, we'll let the player enable or disable the music and sounds and save those settings for future runs of the game. But for now, let's just set this to true. Now let's do the same for the sounds by typing private bullying, sounds enabled, equals true. Next, we'll create variables for holding all of the audio assets that we'll get from the asset manager. We're going to separate these into three categories, music, UI sounds and game sounds, first doll comment music. And the only music asset we have is music dot GG, which will be a music object. So let's type private final music, press Enter, then music. Nextll comment UI sounds. We also only have one UI sound, click Do MP three. We'll play this sound whenever the player presses a button. This will be a sound object. So let's type private, final sound, pre center, then click Sound. All of the remaining sound assets will be game sounds. So let's type private, final sound, block sound, private final sound, boo sound, private final sound, cheer sound, and private final sound Hit sound. We're getting errors here right now because these are final variables, and we haven't initialized them yet, but we'll take care of that soon. Another variable we want to put in game sounds is an array list to hold all of the game sounds. That's will allow us to easily pause, resume and stop all game sounds at once, which will be important later. So let's type private final array list. Presenter, less than sign, sound, greater than sign, all game sounds. All right, next, we're going to create a constructor for the audio manager, which will be called when we initialize an audio manager object inside the main game class in a bit. For this, let's type Public Audio Manager. And we'll need access to the asset manager. So for parameter, let's type asset manager, presenter, then asset manager. Now we need to get all the audio assets from the asset manager and store them in the variables we created. Our first comment, get all audio assets from the asset manager. Let's start with the music by typing music. Equals asset manager, dot Git Assets precenter dot Music. Now let's get click sound by typing click sound, equals asset manager, dot Git Assets dot click sound. Then let's do the same for all of the game sounds. Next, we want to initialize the A Game sounds array list and add all of the game sounds to it. First I'll comment, create an array list of all game sounds for easily pausing, resuming and stopping them all at once. Then let's initialize the array list by typing all game sounds. Equals new array list. Less than greater than open and closed parentheses. Now let's add all of the game sounds to the array list, starting with block sound by typing all game sounds. Add block sound. Now let's do the same for the others. Finally, because music objects don't loop by default, we need to make it so that our music variable loops. First I'll comment set music to loop. To set whether a music object should loop, be called the set looping method and pass in either true or false. So set Music dot set looping. True. We next need to create some methods for handling things like enabling, disabling, playing, and pausing the music and sounds. Let's start with the method for enabling music by typing public void, enable music. And here we'll enable the music by setting the music enabled variable to true. I'll first comment enable music. In this type music enabled equals true. We're also going to start playing the music in this method if it isn't already playing. I'll comment here, I music isn't already playing, play it. To check whether a music object is playing, we call the I playing method. You want to check if the music isn't already playing, so let's type I not music dot is playing. And to play the music, we simply type music dot play. Now, we need to create the opposite method for disabling the music. So let's type public void disabled Music. Holl Comment disabled music. Tennis Type music enabled, equals false. We also want to stop the music if it's currently playing. I'll comment I music is playing, stop it. Tennis Type I music dot is playing, and to stop the music, we call music dot stop. Next, we want to create a method for toggling the music on or off. This will allow the player to press a key inside the game to enable or disable the music whenever they want. For this method, let's type public void Toggle Music. I'll come it enable or disable music. If the music is enabled, we want to disable it. Otherwise, we want to enable it. So let's type I music enabled, and to disable it, we can simply call the disabled music method we just created. Then let's put Os enable music. We now want to create a method for playing the music, but only if the music is enabled. For this, let's type public void play music. And here we want to check if the music is enabled and isn't already playing. If these conditions are true, we'll play the music. First I'll comment, I music is enabled and isn't playing, play it. Now, let's type I music enabled and not music that is playing music dot play. The final music method we need to create is one for pausing the music. For this, let's type public void pause music. And here we want to check if the music is enabled and is currently playing, and if so we'll pause it. Okay, so I'll comment, I music is enabled and is playing posit. Then let's type if music enabled and music dot is playing. To pause the music, we call music dot Paz. When we call the play method after calling paz, it will start the music exactly where it was when pause was called. This isn't too important with our music because it's a very short clip. But if we had something longer like an entire song playing, this would be important. Next, we want to also create a couple methods for enabling and disabling the sounds. For the enable sounds method, let's type public void Enable sounds. Because the sounds don't loop, we don't have to bother with stopping them. So in here, we can just put sounds enabled, equals true. Similarly, for the disabled sounds method, we can type public void disabled sounds. Sounds enabled equals false. We're also going to need a method for playing a particular sound. For this, let's type public void, play sound. For a parameter, we'll use a string that refers to the location of the sound asset to play. So let's type string sound asset. We only want to play a sound if the sounds are enabled, so we need to check that first. I'll comment here, I sounds are enabled, play requested sound. Then let's type if sounds enabled. Next, we'll use a switch statement to check which sound asset the past in sound asset variable refers to, then we'll play that sound. So let's type switch sound asset. First, let's do case assets, dot, click sound. And like with music objects, we can play sound objects by calling the play method. So let's type clicksund dot play. Then break. Let's continue for the remaining sounds. Okay, now we just need to create three more methods for pausing all of the game sounds, resuming all of them, and stopping all of them. Let's start with pausing by typing public void pause game sounds. Our first comment, pause any instances of game sounds. Now we need to loop through the all game sounds array list and pause each one. We can do this with a four loop by typing four sound sound, calling all game sounds. There's no method for checking if a sound object is playing, but it's not really necessary. And to pause a sound, we call the Pase method, let's type sound dot pase. Next for resuming the game sounds, let's type public void Resume Game sounds. I'll come, resume any instances of pause game sounds. The foreloop for this will be similar to the one in pause game sounds, so let's copy and paste it from there. To resume a sound objects, we use the resume method. So let's change pause here to resume. This we'll continue playing this sound from where it left off if and when we called pause. Finally, let's create the stop game sounds method by typing public void stop game sounds. I'll stop any instances of game sounds. We can paste the four loop in here and to stop a sound object, we call the stop method. And by the way, with sound objects, we can actually play the same sound asset multiple times using a single object. Calling the pause resume or stop methods with no parameters like we're doing in these methods will pause resume or stop all instances of the sound asset stored in the object. If you wanted to call one of these methods on a particular instance of the sound, the play method actually returns a long value that represents the current instances ID, and we can pass in this ID when we call pause resume or stop to perform the action on just the sound instance with that ID. This isn't really necessary in our game, though, as we just want to handle the sounds all at once. All right, now we can head over to our main game class and create an audio manager object. First, let's go to the top of the class. And after we create the assets object, let's create an audio manager object by typing public Audio Manager, pressing Enter to import it, then audio Manager. Now in the create method, we want to initialize the audio manager after the assets have been loaded so that all of the audio assets will be available. So after calling assets dow manager dot finished loading, I'll comment, initialize the audio manager. Then let's type audio Manager, equals New audio Manager. And here we need to pass in Assets dot manager. Like I mentioned before, we're eventually going to have settings that we'll check to determine whether to enable the music and sounds. We already enabled them by default, but the music won't start playing until we tell it to. So let's go ahead and call audio manager dot play Music. And now if we run the game, the music immediately starts playing on loop. It's definitely not great music, and will probably get annoying after a while, but it's not too bad for demonstration purposes. Alright, now we can go to the game screen class and start putting our sounds to use. But first, because we're already at the pause method, let's make it so that minimizing the window, we also pause the music. If we run the game again and minimize it right now, the music keeps playing. So in the pause method, after we check if we should pause the game, I'll comment pause music, then it's called game dot audio Manager dot pas Music. And to make the music start playing again, when we bring the window back up, we can resume it in the rezoom method here. I'll comment resume music. And remember that we made it so calling the play music method will only play the music if it's enabled. So I'll also put if it's enabled. Ellis type game audio Manager play music. Okay, let's give it a try. Minimizing the window, pauses the music, and bringing the window back up, resumes the music. Perfect. All right, now let's make it so when we press a button, it will play the click sound. To do this, let's head down to the touchdown method. First, inside this IF statement, where we check if the pause button has been touched. After we call pause game, I'll comment, play click sound. Tennis type game dot audio Manager Play Sound. We need to pass in assets do click Sound. Okay, I'm going to copy these two lines. And down here where we check if the Play Again button has been touched. Let's past the lines after calling Start Game. Let's do the same for the continue button here after calling resume game. Okay, let's give it a try. Clicking the pause button plays the sound, clicking Continue plays it. And if we hoops, it looks like we have a bug because pressing an arrow key pauses the game. If we go up to the key down method really quick, I accidentally put the inner closing parenthesis here at the end of the If statement. So let's delete it here. And instead, we need to put it here after Gamestate dot paused. Okay, now if we run the game again, the arrow keys work like normal. And if we beat the game, clicking the play again button plays a click sound. Cool. Okay? Now, let's make it so when we hit the opponent, it will either play the hit sound or if they're blocking, it will play the block sound. For this, let's go into the update method. Near the bottom of the method, after we call the get hit method on the opponent, we can determine which sound to play based on whether the opponent is blocking. To do this as type, if game dot opponent dot is blocking. I'll comment I opponent is blocking, play block sound. Then let's call game dot audio manager dot play sound. Assets dot BlocksunD. Now let's do ts, and I'll comment, I opponent isn't blocking, play it sound. Then's type game dot audio manager, dot play sound. Assets dot Hit sound. Okay, if we run the game and hit the opponent, I place the hits sound. And if we go up to the start Round method, after getting the fighters ready, we can put the opponent in the block state by typing game dot opponent, do block. Now if we run the game and hit the opponent, Place the block sound. Okay, I'll delete this line. The final two sounds that we need to play are the cheer sound and the Bo sound. We'll play the cheer sound when the player wins around, and we'll play the boo sound when they lose. So let's head down to the Winound and Lose Round methods. Before calling end Round and the Win Round method here, I'll comment play Cheer sound. Tennis type game audio Manager dot Play Sound. Assets dot Cheer sound. And before calling end Round and the ose Round method, I'll comment play Boo sound. Dennis type game dot audio Manager, dot play sound Assets Dabo sound. Now if we run the game and win the round plays the cheer sound. The player, of course, can't lose a round yet since we haven't set up the opponent's AI, but just to test it, we can change cheer sound here to boo sound. Now if we run the game and win around, pace. Plays the Boot sound. But of course, we don't want the player to feel like the bad guy, so let's set this back to cheer sound. Another thing we want to do is make it so pausing the game we also pause all the game sounds and resuming the game, we'll resume the sounds. So let's go to the pase game method. At the bottom of the method, I'll comment pause game sounds. It might also be a good idea to pause the music, so I'll add music. Now we can call game the audio Manager, DPAs game sounds. Then game the audio Manager, D pas music. Okay. Now on the resume game method, I'll comment, resume game sounds and music if it's enabled. Tennis type game the audio manager, the resume game sounds. And game the audio manager that play music. Okay, if we run the game and win the round, Then pause it. We'll also pause the game sounds and the music. If we press Continue, it will resume the audio. Okay. One final thing we'll do in this video is make it so the player can press a key to talk about the music on or off. So let's head down to the key down method. Let's do this by adding another Out sift to our statement here before the outs part. For the music toggle key, we'll use. So let's type key code is equal to input dot keys dot M. And here I'll comment, Toggle music on or off. Then we can simply call game dot audio manager doTogO Music. Now if we run the game, we can press to toggle the music on or off. Excellent. Alright, in the next video, we'll finally make it so the opponent can fight back by setting up their AI. It's there. 31. Add Opponent AI: In this video, we'll get our game mostly fully working by programming the AI for the opponent fighter. And we're going to make it so that the harder the difficulty the player chooses for the game, the harder the opponent will be. The things we'll cover in this video will probably seem a bit confusing at first, mainly because I came up with a lot of it through trial and error, but I'll do my best to explain everything as we go. It's also not going to be perfect AI, so I encourage you to improve it or change it completely to suit your needs. Okay, with that said, let's begin. We'll first need to go to the top of the game screen class and create some variables. After the buttons section here, let's create a section for opponent AI. First, we're going to need a timer for the opponent AI, which we'll use to determine if they should perform the next action. Let's create this by typing private float opponent AI timer. Another variable we'll need is a boolean for indicating whether the opponent is currently making a contact decision. The opponent will make a contact decision when the fighters are within contact distance from each other, and there will basically be three possible decisions. Attack the player, block the player's attack, or move away from the player. Okay? For this variable, let's type private Boolean, opponent AI making contact decision. Next, we need to set up a delay time between the opponents previous contact decision and the next one. We actually have three separate variables for this, one for each difficulty setting. The harder the difficulty, the shorter the opponent's contact decision delay will be. So for easy, let's spe private static final float. Opponent AI, contact decision delay easy equals 0.1 F. So for the easy difficulty, there will be a tenth of a second delay between the opponents contact decisions. For medium, I'll duplicate this line with Control D, change es to medium. Instead it to 0.07 F. And for hard, I'll duplicate again, change it to hard, instead it to 0.01 F. It's only 100th of a second for hard. Next, we need to set up a couple chance factors. One for determining whether the opponent should block the player's attack, and one for determining if the opponent should attack. We can also create separate variables for these depending on the difficulty, but to keep things simple, we'll just use the same chance factors for all difficulty levels. For the block chance, let's type private static final float. Opponent AI block chance equals 0.4 F. This basically means that the opponent will block a player's attack four times out of ten. And for the attack chance, let's type private static final float, opponent AI, attack chance equals 0.8 F. So if the fighters are within contact distance, the opponent will try to attack the player eight times out of ten. Next, we also need a delay between opponent decisions when the fighters are not within contact distance. We'll also just create a single variable for this. Let's type private static final float. Opponent AI, non contact decision delay equals 0.5 F. So when the fighters aren't within contact distance, the opponent will make a decision every half second. The non contact decisions will be either to walk in a random direction or to pursue the player. Finally, we'll create some variables for pursuing the player. First, we need a boolean to indicate whether the opponent is currently pursuing the player. For this, let's type private boolean, opponent AI pursuing player. Now we just need to pursue chance factor to determine whether the opponent should pursue the player. We'll use three different values depending on the difficulty. The harder the difficulty, the higher the chance the opponent will pursue the player. So for easy, let's type private static final float, opponent AI, pursue player chance EZ. Equals 0.2 F. This means that on EZ, the opponent will pursue the player 20% of the time. For medium, I'll duplicate this line, change it to medium. Let's set it to 0.5 F or 50% of the time. Finally, for hard, I'll duplicate again, change it to hard and set it to one F. This means that on hard, the opponent will pursue the player 100% of the time, so the player will have no time to rest on hard mode. All right, now we're going to credit a few methods to start using these variables. For these, let's go down near the bottom of the class after the R within contact distance method. The first method will be for performing the opponent's AI, and we'll call it from inside the update method. Okay, let's create the method by typing private, void, perform opponent AI. We actually want to pass in the Delta time to this method, so let's say the parameter float Delta T. The first thing we want to do is check if the opponent is currently making a contact decision. I'll comment, check if opponent is making a contact decision. Attack block, et cetera. Then let's type I opponent AI, making contact decision. We then want to check if the opponent is currently blocking, and if so, we need to decide whether to make them stop blocking. So first, this type, I game opponent is blocking. There are several conditions for when we want to make the opponent stop blocking. When the fighters are no longer within contact distance, when the player isn't currently attacking or when the player's attack has already made contact with the opponent. I'll first put all of this in a comment. If opponent is blocking, stop blocking if the fighters are not within contact distance, or player isn't attacking, or player has attacked and made contact. Okay, to check all of this as type if not are within contact distance, game dot player dot G position, game opponent dot get position. On game dot player that is attacking or game dot player that has made contact. Now, we want to make the opponent stop blocking by calling game dot opponent dot stop blocking. Okay, after checking whether the opponent is blocking, we next want to check if the opponent isn't currently attacking. Let's do this by putting an OCIP down here, the not game dot opponent that is attacking. Next, we'll check if the fighters are within contact distance. First I'll comment, I opponent isn't currently attacking, check if the fighters are within contact distance. Then's type, I within contact distance, game dot player dot get position. Cat opponent, do get position. If the fighters are within contact distance, we'll check if the opponent's AI timer has finished. If so, we'll make another contact decision. If not, we'll just decrease the AI timer by Delta T. So first, let's check if the AI timer has finished by typing, I opponent AI timer less than or equal to zero F. And I'll comment, I the fighters are within contact distance and the opponent AI timer has finished, make a contact decision. To break this method up, we'll create a separate method for making a contact decision called opponent AI make contact decision. Now we need to put an s here. And I'll comment, decrease the opponent AI timer by Delta time. Then this type, opponent AI timer minus equals Delta time. Okay? And if the fighters are not within contact distance, the opponent shouldn't make a contact decision, so we need to set opponent AI making contact decision to false. Let's do this by putting an s down here and I'll comment. If the fighters aren't within contact distance, opponents shouldn't make a contact decision. Then let's type opponent AI, making contact decision. Equals false. All right, so all of this takes place when the opponent is making a contact decision. We next need to decide what to do when this isn't the case. So let's add an te to the main statement, where we check if opponent AI making contact decision is true. First, we want to check if the fighters are within contact distance, and if so, we'll make a contact decision. So let's type if R within contact distance. Game dot player dot get position. Coma game opponent, do get position. I'll comment, I opponent isn't currently making a contact decision and the fighters are within contact distance, make a contact decision. And again, let's call the opponent AI make contact decision method that we haven't yet created. If the fighters are not within contact distance, we want to check if the AI timer has finished, and if so, we'll determine if the opponent should pursue the player or simply move in a random direction. So let's put notes here. Then type, I opponent AI timer, less than or equal to zero f. I'll comment. If the fighters are not within contact distance and the opponent AI timer has finished, either pursue the player or move in a random direction. To determine if the opponent should pursue the player, we'll get a random float 0-1 and check it against the pursue player chance variable that we created. But if you recall, we separated this into three values depending on the difficulty setting. So we'll first create a temporary float variable and set it to the correct pursue player chance value using a ternary statement. To do this, let's type float pursue chance equals difficulty is equal to global variables, D difficulty, D easy. Question mark, opponent AI, pursue player chance easy. Then Colon, for this part, we could put another ternary statement by typing difficulty is equal to global variables do difficulty dot medium. Question mark, opponent AI, pursue player chance medium. Colon, opponent AI, pursue player chance hard. To get a random float 0-1, we can use the public static random method of the Math Utils class, which is a class that LibGDX provides for us for doing many different math functions. And we want to check that the float is less than or equal to the pursue chance variable, so let's type I Math Utils press Ender to import it dot random, less than or equal to pursue Chance. If this is the case, we want to set the opponent AI pursuing player variable to true and make the opponent move toward the player. I'll comment opponent is pursuing player. Then type opponent AI pursuing player, equals true. Next, I'll comment, move in the direction of player. And for this, we'll create another method called opponent AI move toowardPlayer. Okay. Now if the random float is greater than pursue chance, you want to set opponent AI pursuing player to false and move in a random direction. So let's put an s here, and I'll comment, opponent is not pursuing player. Then type opponent AI pursuing player equals false. Next, I'll comment, move in a random direction. And we'll also create a method for this called opponent AI move randomly. Okay, so after we make one of these non contact decisions, we want to set the AI timer to the non contact decision delay so that there will be a pause between the non contact decisions. So after this if statement in here, I'll comment, set the opponent AI timer to the non contact decision delay. Then type, opponent AI timer equals opponent AI, non contact decision delay. Finally, we want to do a couple of things when the AI timer has not yet finished. First, if the opponent is currently pursuing the player, you want to keep calling the opponent AI move toward player method to update the opponent's movement direction in case the player has moved in a different direction. Lastly, we want to decrease the AI timer by Delta time. So let's add an e to the I statement where we check if the timer is finished. I'll comment, I opponent is pursuing player, moving the direction of player. Then this type, I opponent AI pursuing player, opponent AI move toward player. Next below this SIF statement, I'll come it, decrease the opponent AI timer by Delta T. Then let's type opponent AI timer minus equals Delta T. Now we need to create the opponent AI make contact decision method. The opponent AI move toward player method, and the opponent AI move randomly method. Let's start with the make contact decision method down here by typing private void, opponent AI make contact decision. The first thing we want to do in here is set opponent AI making contact decision to true. Next, we're going to make a contact decision that will depend on whether the player is currently attacking. If so, we'll decide whether to block the attack or move away from the player. If not, we'll either attack the player or move away from the player. First I'll comment here, make a contact decision. Then this type, I game dot player that is attacking. Now, we only want to bother blocking or moving away if the player hasn't yet made contact. If they have made contact, it's too late. So I'll comment, I player is attacking and hasn't yet made contact, determine whether to block players attack or move away from player. Then let's type, I not game player dot has made contact. To determine whether the opponent should block or move away, watch like a random float against the block chance variable that we created, like we did with the pursue chance variable. So let's type I MatuTils dot random, less than or equal to opponent AI block chance. If so, we want to block the player's attack. So I'll comment block players attack. Then let's type game that opponent do block. Otherwise, we want to move away from the player. So let's type LTs and I'll comment move away from player. And for this, we'll create a method called opponent AI move away from player. Okay, we next want to decide what to do when the player isn't attacking. So let's not an s to the I statement here where we check if the player is attacking. Here we're going to determine whether to attack the player or move away from the player. And similarly to blocking, we'll do so by checking a random float against the attack chance variable. So first I'll comment, I player isn't attacking, determine whether to attack player or move away from player. Then let's type if Matthew tills dot random. Less than are equal to opponent AI attack chance. And with attacking, we of course have two options punching or kicking. Let's make it so 50% of the time the opponent will punch, and the other 50% of the time they will kick. To do this, we can generate a random integer of either zero or one with zero meaning punch and one meaning kick. We can actually also use the Matthew tills dot random method for this. If we pass in a number to the method, it will generate a random number from zero up to and including the pass in number. Also, the value it returns will be of the same type as the type of number we passed in. Therefore, if we pass in a one, which is an integer, the method will randomly return either a zero or a one. So I'll comment here, attack player. Equal chance of punching or kicking. Then's type I Matthew tills dot random, one is equal to zero. Game that opponent do punch. Else, game that opponent do kick. Okay, if the random float we generated up here is greater than the attack chance, we want to move away from the player. So let's at a notes this SIF statement. And I'll comment, move away from player. Then's call opponent AI, move away from player. Finally, below the Minif statement, we need to set the opponent AI timer to a contact decision delay. And because we created a separate delay value for each difficulty, we'll use a switch statement to determine which delay to use. So first I'll comment, set the opponent AI timer to a difficulty based contact decision delay. Then it's type switch difficulty. Let's first do case, easy. Opponent AI timer equals opponent AI, contact decision delay easy. Break. In case medium, opponent AI timer, equals opponent AI, contact decision delay, medium, break. And for hard, we can just do default, opponent AI timer, equals opponent AI, contact decision delay hard. Next below this method. Let's create the opponent AI move toward player method by typing private, void, opponent AI, move toward player. And I'll comment move in the direction of players position. To do this, we need to get the position of both fighters and compare them. Basically, if the opponent's exposition is greater than the player's exposition, the opponent needs to move left, and if the opponent's exposition is less than the players, they need to move right. Similarly, if the opponent's y position is greater than the players, they need to move down, and if it's less than the players, they need to move up. If their X positions match, the opponent doesn't need to move horizontally, and if their Y positions match, the opponent doesn't need to move vertically. However, the X and Y positions don't need to match exactly. The opponent just needs to get within contact distance of the player. Therefore, when checking the distance between the fighters, we can factor in the fighter contact distance X and fighter contact distance Y values that we created back near the beginning of the course. Okay, so let's begin by setting a couple vector two objects to point to the fighters positions so that we don't have to keep calling the Git position method on the fighters. For the player's position, let's type vector two, player position equals game player dot get position. And for the opponent, let's type vector two, opponent position equals game dot opponent dot get position. Next, we want to check if the opponent's exposition is greater than the player's exposition with the value of fighter contact distance X added to it, and if so, the opponent needs to move left. So let's type I opponent position dot X greater than player position dot X plus fighter contact distance X. Game opponent move left. If this isn't the case, we want to check if the opponent's exposition is less than the player's exposition with fighter contact distance X subtracted from it, and if so, we'll move the opponent to the right. Let's put Osif opponent position dot X. Less than player position dot X minus fighter contact distance X. The game that opponent that move right. If neither of these cases is true, we don't want the opponent to move horizontally, so we can put s, then stop the opponent's left and right movements by calling game that opponent thou stop moving left. And game that opponent thou stop moving right. We now want to create a separative statement for comparing the Y positions. Let's start by putting if opponent position dot Y. Less than player position dot Y minus fighter contact distance Y. Game that opponent that move up. Then let's do Osif. Opponent position dot Y. Greater than player position dot Y plus fighter contact distance Y. Game that opponent that move down. And finally, else, game that opponent does stop moving up. Game that opponent does stop moving down. Okay, below this method, let's do private void, opponent AI, move randomly. In this method, we're going to randomly set both the horizontal and vertical movement of the opponent. For horizontal movement, we'll decide whether to move left, right, or not move horizontally at all. Similarly, for vertical movement, we'll decide whether to move up, down or not move vertically. This means that for both horizontal and vertical movement, there will be three possibilities that we'll give equal chance to all three. Let's start with the horizontal movement. I'll first comment, randomly set opponent's horizontal movement. And we want to generate a random integer 0-2, which we can do by calling Matthew tills dot random and passing in a two as a parameter. Let's do this in a switch statement by typing switch Matthew tills dot random two. That's where we turn either a zero or one or two. So let's first check if it's a zero by typing case zero. If so, let's call game that opponent, that'll move left. Then break. Let us do case one game that opponent, that move right. Break. For two, we can put default, and we want to stop all horizontal movement by calling game that opponent stop moving left. And game that opponent that stop moving right. For the vertical movement, we can copy and paste all these lines here. And I'll change horizontal here to vertical. For case zero, let's call move up. For case one, let's call move down. And for default, let's call stop moving up and stop moving down. That's it for this method. Finally, let's create the move away from player method below this one by typing private, void, opponent AI, move away from player. This would be kind of like the opposite of the method for moving toward the player. But fortunately, it will be a bit simpler. Like with moving toward the player, we could first set some vector two objects to point to the fighters positions. First I'll comment, move away from player's position. And I'm going to go up to the move toward player method and copy the first two lines of code here. Then paste them in the move away method. In this method, we just want to move the opponent in a direction that will get them away from the player as efficiently as possible. Basically, this means that if the opponent is currently to the right of the player, the opponent should move even more to the right. Otherwise, they should move to the left. Likewise, if the opponent is currently above the player, the opponent should move up even more. Otherwise, they should move down. Let's start with a horizontal movement by typing if opponent position dot x greater than player position, dot x. Game dot opponent dot move right. ELTsGame that opponent that move left. For the vertical movement, let's type if opponent position dot Y, greater than player position dot Y. Game that opponent that move up, EltsGame that opponent that move down. That's it. We're finally done with the opponent AI methods. Now let's put them to work. Let's first go up to the update method. To get the opponent AI working, we simply need to add a call to the perform opponent AI method somewhere in here before we check if the fighters are within contact distance. I'll comment perform the AI for opponent. Then let's call perform opponent AI. We need to pass in Delta T here. Now let's run the game. Okay, after the round begins, the opponent starts moving around in the ring. And if we get close to them, they start attacking us. However, their attacks aren't hurting us. That's because inside the update method, where we check if the player is attacking, we forgot to also check if the opponent is attacking. Basically, we can copy this whole if game player that is attack active statement here, add an outs to the bottom of the statement and pace the lines. Then switch all the player and opponent stuff around. So game dot opponent that is attack active here. Now change the comment to opponent is actively attacking player gets hit. Change this to game dot player do get hit. Change this one to game dot player is blocking. Now change opponent in these comments to player. Deactivate opponent's attack here. Game dot opponent that make contact. Here I'll change the comment to check if player has lost and change this to game dot player that has lost. For this comment, I'll change it to if player has lost, player loses the round. We need to change win round here to lose round. We also never set the Max Life variable in the fighter class back to 100, so let's go over to the fighter class and change Mxlife equals five F here to MXLpe equals 100 F. Now let's run the game. I Right now we can actually fight the opponent. It's pretty easy right now, though. That's because the difficulty is set to easy by default. The player will be able to set the difficulty in the setting screen later, but for now, we can make it so they can change it by pressing a key. So let's go down to the key down method of the game screen class. After we check if the key has been pressed to tackle the music on or off here, we can add another Sif. For the difficulty, let's go with the key by typing keycde is equal to input dot keys dot L. Our first comment change the difficulty. Each time the player presses the key, we want the difficulty to move sequentially from easy to medium to hard then back to easy. Let's do this with a switch statement by typing switch difficulty. Case easy. Difficulty equals global variables. Do difficulty do medium. Break case medium. Difficulty equals global variables, the difficulty, the hard. Break. Default. Difficulty equals global variables do difficulty do easy. Now, let's give it a run. Alright, we can press the key to change the difficulty to medium. Press it again to change it to hard. Now the opponent is much harder. If we press L again, it goes back to easy. Cool. Okay? In the next video, we'll add some blood to the game. See. 32. Blood 1: Blood Splatters: Each time a fighter gets hit in the game, we'll display two types of blood, a blood splatter that appears briefly on a fighter's body and a blood pool that appears on the ring under the fighter and slowly fades away. We're going to create two different classes for the two types of blood and use these classes to create multiple blood objects that will draw to the screen throughout the game. We'll add both of these classes to the objects package. So first, let's right click the Objects package, go to New Java class. Let's start with the blood splatter class by typing blood splatter and pressing Enter. If we go into the textures Assets folder, we have blood dot atlas and blood dot PNG. If we open blood dot PNG, we can see all the textures we'll use for both the blood splatters and the blood pools. The blood pool textures are these three here that look like a bunch of red dots in an elliptical shape. When we create the blood pools in the game, we'll use a random blood pool texture for each one. The blood splatter textures, however, form an animation. Therefore, in the blood splatter class, we need to set some variables to hold things like the blood splatter state time and its animation. Let's start with the state variables. I'll come it state. And let's first create a variable for the state time by typing private float state time. Okay, we're actually going to create an array of blood splatter objects in the game screen class later, and we'll reuse the objects as needed. In order to do this, we need to keep track of which blood splatter objects are active and only draw those to the screen. So let's create a variable for indicating whether the blood splatter is active by typing private boolean active. One more state variable we'll need is the position of the blood splatter. So let's type private final vector two, press Enter to import it. The position equals new vector two. Can now we just need a variable to hold the animation. I'll comment here animation. Let us first type private animation and make sure to import the graphics GTD version here. The animation will be using texture regions from the Blood texture atlas, so let's put less than texture region. Presenter, then greater than, let's call it splatter animation. Next, we want to create a constructor for the class. So down here, let's type public Blood splatter. And we need to pass in the main game class to the constructor. So let's type SFS, precenter, then game. The first thing we're doing here is initialize the state time to zero and set active to false. I'll comet initialize state. And let's put state time equals zero F, active equals false. Now we need to initialize the animation. I'll comet initialize the splatter animation. And for this, let's call it a method that we'll create called initialized Spider animation. As a parameter, we'll need to pass in the asset manager. So let's p game dot assets dot manager. Can always create the initialized Spider animation method below the constructor by typing private, void, initialized Spider animation. Asset manager, press enter to import it, then asset manager. First, we need to get the blood atlas from the asset manager. We created a blood atlas variable for this in the assets class. So back in the blood splatter class, I'll comment, get the blood texture atlas from the asset manager. Then it's type texture Atlas, presenter. Blood Atlas equals asset manager dot Git Assets, presenter dot Blood Atlas. Now we just need to create the animation. I'll comment, create the animation. This type spider animation equals new animation. Less than texture region, greater than open parthess. For the frame duration, I found that 0.03 F works pretty well. For the texture reasons of the blood splatter animation, if we open the blood Atlas file and the assets folder, the blood splatter regions are all labeled blood splatter. So in the blood splatter class, we can put blood atlas, dot Fine regions, blood splatter. And we need to make sure to put fine regions with S here, as we're looking for multiple regions labeled blood splatter. Next, we need to create a couple of methods for activating and deactivating the blood splatter from the game screen class. First, for the activate method, let's type public void activate. And when we activate it, we also want to set its position. So for parameters, let's put float position X, come a float position Y. Okay. And here, I'll comment, activate the blood splatter. Now let's set active to true, state time to zero F, and set the position by typing position dot set, position X, common position Y. Okay, for the deactivate method, let's type public void deactivate. And we don't need any parameters for this. I'll comment deactivate the blood splatter. And all we need to do in here is set active to false. Now we need an update method to update the state time and a render method to draw the current animation frame. For update, let's type public void Update. And we need to pass in the Delta T. So for parameter, let's put float Delta T. All right, first, if the blood splatter isn't currently active, we don't want to do anything. So I'll comment, I not active, don't update. Then let's type, I not active, return. Next, we need to increment the state time by the Delta time. So I'll comment, increment the state time by Delta T. Then this type, state time plus equals Delta T. Finally, we want to check if the animation has finished, and if so, we'll deactivate the blood splatter. First I'll comment. If the splatter animation has finished, deactivate the blood splatter. Then type, I splatter animation is animation finished, state time. I think it's something called the deactivate method. Under the update method, let's create the render method by typing public void render. We need to pass in the sprite batch, so let's type Sprite Batch. Press Enter to import it, and Batch. Like with update, we don't want to do anything if the blood spider is inactive. So I'll comment, I not active, don't render. Then this type, if not active, return. Now we need to get the current frame of the animation, then draw it. First I'll comment, draw the current animation frame. And the animation frames are texture regions. So to get the current frame using the state time, we can type texture region, current frame equals splatter animation, Get key frame, state time. And to draw it, we can type batch, dot draw, current frame, common position dot X, common position dot Y, co current frame, dot get region with Times global variables. Press Enter to import it, then dot rolled scale. Come a current frame DG region height. Times global variables, dot rolled scale. And that's it for the blood splatter class. Now let's head over to the top of the game screen class. After the opponent AI variable section, let's set a section for blood. We're actually going to make it so the player can turn the blood on and off through the setting screen later. So before drawing any blood, we'll need to check if showing blood is enabled. For this variable, let's type private bullying showing blood. Let's set it to true for now. Next, we'll create a separate array of blood splatter objects for each of the fighters. So let's type private blood splatter. Press Enter to import it, then open enclosed brackets, then player blood splatters. After that, let's do private blood splatter, open closed brackets, opponent blood spiders. We also need a couple of variables that point to the current index of the two blood spltr arrays. So let's type private nt current player blood splider index, and private nt, current opponent blood splider index. Next, we need to create a static final variable to indicate how many blood splatter objects to create for each blood splatter array. Actually, due to things like the duration times that we set for the different animations, it's very unlikely that there will ever be more than one blood splatter on the screen at once. But just in case we might want to make the animations faster or something later, we'll set the blood splatter amount to five. Let's type private static final nt blood splatter amount. Equals five. Finally, we'll need a couple of static final variables for the positioning of the blood splatters. This will be an offset that will add to the current position of the fighter who owns the blood splatter. For these, let's type private static final float. Blood splatter offset X, equals 2.8 F and private static final float, blood splatter offset Y equals 11 F. To come up with these values, I use trial and error, and they'll put the blood splatter up near the fighter's head. Okay, we next need to create a method for creating the blood splatters. We'll actually use the same method for creating the blood splatters, as well as the blood pools in the next video, and we'll name it create blood. We'll be calling this method from the game screen constructor. So first, at the bottom of the constructor, I'll com it, create the blood splatters and pools. Then let's call create Blood. Alright, now let's create the method below the create buttons method here by typing private void create Blood. And here we first need to initialize the blood splatter arrays and objects. I'll comment, initialize the blood spltrs. Then let's type platter blood splatters. Equals new blood splatter, open bracket blood splatter amounts, close bracket, and opponent blood splatters equals new blood splatter, bracket, blood splatter amounts. Now we need to loop through the arrays and initialize the blood splatter objects. Let's do this with a four loop by typing four inch I equals zero, semicolon I less than blood splatter amount. Semicolon I plus plus. Then player blood splatters I equals new blood splatter. We need to pass in the game variable here. Then let's do opponent blood splatters I equals new blood splatter game. Finally, we need to set both of the current blood splatter indexes to zero. I'll come it, set the current blood splatter indexes to the start of the arrays. Then let's type current player blood splatter index equals zero and current opponent blood splatter index equals zero. And that's all we need to do in this method until we create the blood pools in the next video. Okay, now we need to create a method for drawing the blood splatters, which we'll call render blood splatters. Let's create it below the render fighters method. By typing private void render blood splatters. And we want to pass in which blood splatter array to draw. So for a parameter, let's put blood splatter brackets, blood splatters. And here we want to check if showing blood is allowed, and if so, we'll loop through the array and call the render method for each blood splatter object. And remember that we made it so that the render method will only draw blood splatter if it's active. So first I'll comment, I showing blood, draw all active blood splatters in the given array. Then this type if showing blood for blood splatter, blood splatter, colon blood splatters. Blood splatter, dot render, and we need to pass in game dot Batch. Okay. Now when we draw a blood splatter, we want to draw it after we draw the fighter that owns it and before we draw the other fighter, so that the blood splatter will appear on the correct fighter. Therefore, we need to draw the blood splatters inside the render fighters method here. First, after the line near the top or we draw the player, I'll comment, draw player's blood splatters if enabled. Then let's call Render Blood splatters and pass in player blood splatters. Now I'll copy these two lines and paste them below where we draw the player in the outs part here. Next, I'll paste the lines up here after we draw the opponent. And in the comment, I'll change players to opponents and change player blood splatters to opponent blood splatters. Then I'll copy and paste these lines after we draw the opponent in the Os part. Okay, we now need to do a few things down here in the update method. First, after we update the fighters, I'll comment, update the blood splatters. Then we want to loop through the blood splatter arrays and call the update method for each one. So let's type four int I equals zero, semicolon I less than blood splatter amount. Semicolon I plus plus, platter blood splatters I the update and pass in Delta time. Then opponent blood splatters I the Update Delta Time. Can let's go down here to where we check if the fighters have hit each other. Whenever a fighter gets hit, we'll call a method that we'll create called spill blood. This method will take a fighter object as a parameter and will activate a blood splatter for the given fighter, as well as a blood pool in the next video. Now, if we want, we can call the spill blood method, regardless of whether the fighter that gets hit is blocking. However, I think it will be better to just call it when the fighter isn't blocking and not draw any blood if the fighter is blocking. So in the ts part of this If statement, where we check if the opponent is blocking, after playing the hit sound, I'll comment, spill some blood. Then let's call it spill blood and pass in game opponent. Now, I'll copy and paste these lines into the Os part of the O statement down here where we check if the player is blocking. Now change opponent here to player. Alright, for the spill blood method, let's credit below the update method by typing private void spilled blood with a fighter fighter as a parameter. First, we need to use the given fighter to determine which blood splatter array to use, along with the correct current blood splatter index. I'll comment here, use the given fighter to get the correct blood splatter array and current index. Then let's type blood splatter brackets, blood splatters and int current blood splatter index. We set these two variables to the correct variables for the given fighter. To check which fighter the given fighter is, we can use the equals method of the object class, which every class in Java extends. The equals method takes an object as a parameter and will return a boolean value indicating whether the parameter object is the same as the object calling the method. If you want to check if the fighter is the player, for example, we can type a fighter dot equals and passing game dot player. If so, we can set blood splatters to player blood splatters and current blood splatter index to current player blood splatter index. And if the fighter isn't the player, then it must be the opponent. So we can put outs. Blood splatters equals opponent blood splatters. Current blood splatter index equals current opponent blood splatter index. Okay, now that we've gotten the correct variables, we want to go outside of this I statement and activate the blood splatter at the current blood splatter index in the array. First I'll comment, activate the current blood splatter in the array. Finns type blood spiders, open bracket, current blood splider index, closed bracket activate. We need to pass in the position of the blood splatter. For this, we'll use the given fighter's position with the blood splatter offset values added to it. Let's type fighter, docu position dot X plus blood splatter offset X. Coma fighter, docu position dot Y, plus blood splatter offset Y. All right now we just need to increment the current blood splatter index so that if the previous blood splatter is still animating on the screen and we want to activate another one, it won't replace the previous one. And we want to increment the actual current index for the given fighter, not the local variable here, because that won't have any effect outside of this method. So we first need to check which fighter the given fighter is again, then increment the correct index. Also, if the current index has reached the end of the array, we need to set it back to zero. Okay, I'll comment here, increment the correct current blood splatter index, or return to the first if the end of the blood splatter array has been reached. Then let's check if the given fighter is the player by typing if fighter dot equals game dot player. Okay, because we set the size of the blood splatter arrays to be the blood splatter amount variable that we created earlier, the final index in the arrays is blood splatter amount minus one. So we can check if the player's current blood splatter index hasn't reached the end of the array by typing if current player blood splatter index, Less than blood spider amount minus one. And if so, we'll increment the index by typing current player blood spider index plus plus. If this isn't the case, the index has reached the end of the array, so we'll set it back to zero by putting s, current player blood spider index. Equals zero. Okay, now we just need to do all of this for the opponent. So let's add an outs to the If statement where we check if the fighter is the player. Then let's copy this inner If statement here, paste it in the outs part, and change current player blood splatter index to current opponent blood splatter index in all of these lines. That should do it. Now we can give the game a run and see if it works. Alright, as the fighters hit each other, blood splatters appear near their heads. If we get hit while blocking, no blood appears. Perfect. Okay, in the next video, we'll add some blood pools as well. See there. 33. Blood 2: Blood Pools: For the blood pools, which we'll draw on the ring under the fighters when they get hit, we'll need to create another class. Like with blood splatter, we'll create the class in the objects package. So let's right click the package and go to New Java class. Let's call it Blood Pool and press censor. Okay, unlike blood splatters, which are animations that only appear on the screen for a short duration, a blood pool consists of a single texture that gets drawn to the screen and slowly fades away over time. And similar to blood splatters, we'll have an array of blood pools in the game screen class, which will go through and activate the blood pools as needed. So to begin, we'll need to create some state variables for the blood pool that include things like a state time, whether it's active or not, its position on the screen, the amount of time it takes to fade away. And in order to make the blood pool fade away, we'll need to lower its opacity or Alpha channel over time as we draw the blood pool. Okay, so our first comment states then let's start with the state time by typing private float state time. Next, let's do private Boolean active. And to hold its current Alpha value, let's type private float Alpha. Next for its position, let's do private final vector two, press Enter to import it. The position equals new vector two. Finally, we need a static final variable for its fade time. So let's type private static final float. Fade time equals. Let's set it to 60 F for 60 seconds. So by the time the blood pool has been on the screen for 60 seconds, it will have disappeared. All right? And we also need a couple of variables for the blood pools texture. If we open the blood dot PNG asset again, these three elliptical shape textures are for the blood pools. We'll assign our random one to each blood pool when we create it. So back in the blood pool class, I'll come it texture. Then let's credit a variable for its texture. And because it's going to be a texture region inside a texture atlas, let's do private final texturesion Press Enter to import it and texture. Also, when we create a method to get a random texture later, we need to let the method know how many textures there are to choose from. As we just saw on blood dot PNG, there are three textures. So let's type private static final int. Texture amount equals three. Next, we need to create a constructor where we'll initialize all of the variables and get a random texture from the blood atlas. So first, let's type public blood pool. And we need to pass in the main game object here, let's type SFS, press Enter, then game. And here I'll comment, initialize state. Then let's set state time to zero F, active to false, and Alpha to one F, which means folly opaque. Next, we'll set the texture variable to a random texture region from the bd atlas. How come it, set the texture to a random blood pool texture. We're actually going to create a separate method for this called Get Random Blood pool texture. So let's type texture equals. Get Random blood pool texture. And we need to pass in the asset manager here, so let's type game dot assets dot Manager. Okay, for the Get Random Blood Pool texture method, let's go down here and type private texture Region. Get Random Blood Pool texture. Asset Manager, precenter then asset manager. First, we need to get the blood atlas from the asset manager, so I'll comment, get the blood texture Atlas from the asset manager. Then this type texture atlas precenterblood Atlas equals asset manager dot Git assets, presenter dot blood Atlas. Next I'll comment, return a random blood pool texture region from the blood atlas. If you open the blood atlas file, the blood pool regions are labeled blood pool zero, blood pool one, and blood pool two. Therefore, to choose a random one, we need to use the string blood pool plus a random integer 0-2. So back in the blood pool class, let's first type return blood atlas dot fine region. Blood pool. Plus, and to get a random integer, we can use the random method of the Matthew Tills class, passing in the largest integer that we want, like we did when we had the opponent AI choose random actions. So first, we can type Matthew tills, press Enter to import it, then dot random. And the maximum integer we want is texture amount minus one. We set texture amount to three, so this will pass in a two to the random method, which will then return either a zero or one or two. We next need to create a method for activating the blood pool where we'll do things like we set the blood pool state time and Alpha and set this position. So for this, let's type public void, activate. We need to pass in the position, so let's type float, position X, come a float position Y. And here I'll comment, activate the Bod pool. Let's first set active to true then state time to zero F, Alpha to one F. Let's set the position by typing position dot sets, position X, comment position Y. Another method we need to create is one for updating things like the Bod pool state time and Alpha value. For this method, let's type public void update. We need to pass in Delta time, so for a parameter, let's put float Delta time. First, if the blood pool isn't active, we don't need to update it. So I'll comment, I not active, don't update. Then this type, if not active, return. Next, we'll increment the state time by Delta T. So I'll comment, increment the state time by Delta time. Then this type state time plus equals Delta time. Next, we need to reduce the blood pool's Alpha value, making it get closer and closer to zero as its state time reaches the phat. Because the Alpha needs to be a float 0-1, the calculation we can use to get the current Alpha value is one minus the current state time divided by the fat. So first I'll comment, reduce the Alpha to make the blood pool more transparent over time. Then this type Alpha equals one F minus state time divided by fade time. Finally, we'll check if the Alpha value has reached zero, and if so, we'll deactivate the but pool. For this, I'll comment, if the Alpha has reached zero, deactivate the but pool. Then this type, if Alpha, less than or equal to zero F. Now we can set Alpha to zero F in case it dropped below zero, let's set active to false. Okay, now we just need a render method to draw the blood pool. So after the update method, let's type public void render. We need to pass in the sprite batch. So let's type sprite batch, percenter, then batch. First, like with update, if the blood pool isn't active, we don't want to do anything. So I'll comment. If not active, don't render. Then let's type, if not active, return. Next, we need to set the color of the sprite batch to use the blood pool as Alpha value. Our first comment, set the sprite batch as color using the blood pool as Alpha. Then it's type Batch dot side color. If you want to use ones for the RGB values, then Alpha for the Alpha value. Using ones for the RGB values will make it so it won't affect the RGB channels of the texture. We just want to affect this Alpha channel. Okay, now we can draw the blood pools texture, so I'll comment, draw the blood pool. Then it's type Batch dot draw texture coma position dot X, coma position dot Y, coma texture dot G region with. Times global variables, pre center that roll scale, come a texture that get region height, times global variables that roll scale. Finally, we need to set the Sprite batches color back to folio opaque. So I'll com it. We set the sprite batches color to foli opaque. Then let's call it batch Ds color one, one, one, comma one. Okay, we're finished with the blood pool class, so let's head over to the game screen class. First, at the top of the class, we need to add a few blood pool variables to the blood section. First, unlike with the blood spliders, we'll be drawing the blood pools underneath both fighters. So we'll only need a single blood pool array. For the array, let's type private blood pool. Press Enter to import it, then open closing brackets blood pools. Next, we need to keep track of the current index in the array. So let's do private current blood pool index. Finally, we need to set a blood pool amount. Because the blood poles will stay on the screen for a while, we need to have enough blood pool objects in the array to prevent active blood pools from being reset before they have finished fading. With a fade time of 60 seconds, we probably only need about 20 or so blood pool objects. But just in case we want to increase the fade time later, let's go ahead and create 100 objects. So for this variable, let's type private static final int blood pool amount. Equals 100. All right, now we need to head to the create blood method and initialize the but pool variables. First, after all of the blood splatter stuff, let's initialize the but pools. I'll come initialize the blood pools. And we first need to initialize the bud pool array to have the correct amount of blood pools by typing bud pools, equals new blood pool, Brackets but pool amount. We now need to loop through the array and initialize every blood pool object. That's type four inch I equals zero, semicolon I less than blood pool amount. Semicolon I plus plus, blood pools I equals new blood pool. We need to pass in the game object. Finally, we need to set the current blood pool index to zero, so I'll com it. Set the current blood pool index to the start of the array. Then let's type current blood pool index equals zero. Can let's head to the main render method. Because we want the blood poles to appear underneath the fighters, we need to draw them before we draw the fighters. So before we call render fighters here, how come it draw the blood pools. We'll create a method for this called Render Blood Pools. Now let's go down below the Render blood splatters method. And create the render blood pools method by typing private, void render Blood pools. And here we simply need to check if showing blood is enabled, and if so, we'll loop through the blood pool array and call the render method on each one, which will only draw the blood pool if it's active. So first I'll comment, I showing blood, draw all active blood pools. Then this type, if showing blood, Four blood pool blood pool, call in blood pools. Blood pool, dot render, and we need to pass in game dot batch. All right, that's set for rendering. So now let's go down to the update method. And here, after we update the blood splatters, we also need to update the blood pools. So I'll comment, update the blood pools. Then let's use a four loop to look through the blood pool array by typing four blood pool, blood pool, call in blood pools. Now we can call Blood Pool dot updates and pass in Delta T. Finally, we just need to add a few things to our spilled blood method down here. First, after all of the blood splatter stuff, we need to activate the blood pool, the current blood pool index in the array. So I'll comment, activate the current blood pool in the array. Then it site Blood pools, brackets current bod pool index. Dot activate. And we need to pass in the position of the blood pool. When I created the blood pool textures, I made them so that they will align with the shadows of the fighters. So for the position, we can simply put in the given fighter's position by typing fighter, De position, dot X, coma fighter, docket position dot Y. And now like with the blood splatters, we need to either increment the current blood pool index if it hasn't reached the end of the array or if it has, we'll set it back to zero. So first, I'll comment, increment the current blood pool index or return to the first if the end of the blood pool array has been reached. Then this type, if current blood pool index less than blood pool amount minus one. Current blood pool index, plus plus s. Current Bod Pool index equals zero. Okay, now we're ready to give this a go by running the game. Now when the fighters hit each other, we get blood pools under the injured fighter, and these will slowly fade away as we play the game. They will also carry over between rounds and between fights. Okay, before we move on, as I mentioned earlier, we're going to let the player turn the blood on and off in the setting screen later. But for now, we can make it so they can press a key to tackle the blood. To do this, let's go to the key down method. After we check if the key has been pressed to toggle the difficulty, we can add an SIF to this statement. And for the blood toggle key, we'll use K, so we can type key code, equals input keys K. And here I'll comment, toggle blood on or off. Then we can simply do showing blood equals not showing blood. So if it was true, it becomes false, and if it was false, it becomes true. Now let's give it a try. Alright, if we press K, it hides the blood. If we press it again, the blood comes back. Nice. Okay, in the next video, we'll start working on the other screens by creating the main menu screen. See there. 34. Main Menu Screen 1: Set Up the Widgets: Before we create the main menu screen, we need to go into the assets class and create a method for loading all the necessary assets. Let's create this method below the load audio method here by typing private void load menu assets. Now if we take a look at the textures Assets folder, we actually only have two menu asset files, menu items dot Atlas and menu items dot PNG, which make up the menu items textualis. If we open up menu items dot PNG really quick, this contains a bunch of textures that we'll use for both the main menu screen and the setting screen. Okay, so in the load menu assets method, we simply need to load the menu items textualis. So first I'll comment, load the menu items texture atlas. Tennis type manager dot load, menu items Atlas, texture atlas dot class. We also need to go up to the load method and add a call to the load menu assets method. Like with the game screen class, the main menu screen class is going to implement the screen class so that we'll be able to switch back and forth between it and our other classes that implement screen. Like we did with the game screen class, we want to put the main menu screen class inside the screens package here. So let's right click the package and go to New Java class. Let's call it main menu screen and part Center. Okay, so first, we need to implement screen. So let's type implement screen and press Enter to import it, and now we need to implement all of the required methods, which we can do by hovering over this line, clicking implement methods here, then clicking Okay. Next, let's go above the show method here. Let's add some variables. First, we'll need a variable pointing to the main game class. Let's put private, final, SFS, press Enter to import it, then game. Next, we'll need a stage object. The stage class is part of the scene two D framework in Lib GDX. Scene two D provides a hierarchy of graphical elements called actors, which consist of things like textures and UI elements that we draw to the screen. The advantage of using scene two D, especially for user interfaces, is that we can have parent actors which influence the size, positioning, and appearance of their child actors. As we'll see, this will provide a more efficient way of laying out UI elements on the screen without having to calculate all the positions and sizes like we did when creating the HUD. Anyway, the stage class is the root class of Scene two D and is responsible for doing things like rendering the actors to the screen and handling input from the user. And to create a stage object, we can type private, final stage. Make sure to import the scene two D version, then type stage. Next, we'll need a variable for getting the menu items texture atlas from the asset manager. So let's type private final texture Atlas, presenter, the menu item atlas. We now need to create some variables for the widgets. Widget is a class in the Scene two D framework for creating user interfaces, and there are many classes that extend the widget class, including image, which is basically used for displaying textures, label, which is used for displaying text and button, which lets us display textures that the user can click. We'll see later that using widgets, instead of just plain textures, makes displaying UI much more efficient. There are also many more types of widgets, including ones for creating checkboxes, progress bars, and dialogues, and I recommend checking them all out when you get the chance. Okay, so let's take a look at menu items dot PNG again. Mainly, we have a whole bunch of buttons, but we also have some textures that we don't want to be clickable, like the logo down here, the difficulty textures, and the fighter display textures, which will show the player's chosen fighter. For these non clickable textures, we'll use the image class, and for the rest, we'll use the button class. So back in the main menu screen class, let's start with the image objects. Our first comment here, image widgets. Then let's create one for the logo by typing private image and making sure to import the sent DUI version here, then logo image. Next, we'll need one for the fighter display background. Let's type private image, fighter display, background image. We also need one for the fighter display itself. Let's type private image, fighter display image. And the difficulty textures in here are actually for the setting screen, so we don't need to create variables for them yet. For the buttons in the main menu, we just need the play button, the settings button, the quit button, and this triangle button, which we'll put on the fighter display so that the player can move back and forth between the different fighter choices. Notice, however, that each button actually has two different textures, one that's gold and one that's more orange. We'll use the gold ones as the normal textures for the buttons, and we'll use the orange ones for when the player presses the buttons. And that's another advantage of using the button class because we can actually set different textures for different button states. Okay, so back in main menu screen, I'll create a section for button widgets. Then I'll start with the play game button by typing Private button. Again, making sure to import the scene two D UI version, then play a game button. Next us through Settings button by typing private button Settings button. Then the quick game button by typing private button Quick Game button. Okay, for the triangle button, we're actually going to be using the same texture for two different buttons. One for switching to the previous spider choice, and one for switching to the next Spider choice. The previous button will face to the left and the next button will face right. So for these buttons, let's type Private button, previous Spider button and private button. Next fighter button. Okay. And we also need to create a label object, which we'll use to show the chosen fighter's name. For this out first comment, label widgets. Us type private label. Import the send UI version, then fighter display name label. Okay, that's it for the widgets. One more variable we'll need is one to indicate the index of the player's current fighter choice, which we'll put to use in a later video. For this out comment, fighter choice. Then let's type private nt current fighter Choice index. Right now we need to create a constructor for this class. So after all of the variables, let's type public main menu screen. And for a parameter, let's type SFS game. First, let's set our global game variable to the past in one by typing this game equals game. Next, we need to initialize the stage. Also, the stage itself has a camera and viewport which you'll use when drawing the actors. So we'll need to set this viewport to the correct one for our game, which will be the same as the extend viewport that we used in the game screen class. So first I'll comment, set up the stage. Then it's type stage equals new stage. Next, let's set its viewport, which we can do by calling stage dot set Viewport. And we want it to be an extended viewport, so let's type new extend Viewport and press Enter to import it. And here we need to first pass in the minimum worldwidth of the game, which we want to be the world width value we created in the global variables class. Let's type global variables, press Center, then dot road width. Next, we need to pass in the minimum world height, which is global variables dot Min world Height. Next, for the maximum worldwidth, we'll also use global variables dot world Width. And we don't want a maximum road height, so let's put a zero here. Finally, we need to pass in a camera. We want to use the stages camera, and we can get it by calling stage dot Get camera. The next thing we need to do in the constructor is get the menu items Atlas from the asset manager. How come it, get the menu items texture atlas from the asset manager. Then let's type menu items Atlas. Equals game dot assets dot manager dot Get Assets precenter then dot menu items Atlas. Finally, we need to create all of the widgets. We'll do this in separate methods for each type of widget. First I'll comment, create the widgets. Then we'll create the images by calling create images. Then the buttons will create buttons. Then the label would create labels. Okay, below the constructor, let's create the create Images method by typing private void, create images. First, we'll create the logo image. So I'll comment, create the logo image. Then let's type logo image equals new image. And here we need to pass in a texture or texture region. We'll be using a texture region from the menu items Atlas. So let's type menu items Atlas Dt Fine Region. And the name of the texture region is logo with a L. Okay, we also need to scale the image's size by the world scale variable. To do this, we can call Logo image set size, Logo image dot Get width times global variables dot world scale. Kamal Logo image get Heights, Times Global Variables world scale. Next, we'll create the fighter display, background image. So I'll comment, create the fighter display background image. Then it's type fighter display, background image, equals new image, menu items Atlas, D fine region. This one is called fighter Display Background. To set the size, I'll copy the line for setting the logo image size here. Then I'll just replace the logo image instances in here with fighter display background image. Now we just need to create the fighter display image. I'll copy and paste all of these lines here. Then change them up for the fighter display image. And This one is called fighter display. If we open up menu items dot PNG again really quick, the fighter display is just a small version of a frame from one of the fighter sprite sheets. Like with the fighter sprite sheets, the texture is white and we can add some color to it. However, because the player will be able to change their fighter, we'll change the color of the fighter display later in the show method to make sure it's always set to the correct color. Next, we'll create the buttons. So below this method, let's type private void create buttons. We'll start with the play game button, so I'll comment, create the playGame button. Now because we're using different textures, depending on whether the button is being pressed, we need to create a button style object for each button. With a button style object, we can set which texture to use for each button state. To create one for the play game button, we can type button style, press Enter to import it, and play game button style equals new button dot button style. Okay, and the default button state is the upstate, which means that the button isn't being pressed by the user. To set a texture for the buttons upstate, we can first type play game button style dot U equals, and this actually requires a texture region drawable object. But this isn't a big deal because if we pass in a texture region object, when creating a texture region drawable, it would do the conversion for us. So let's type new texture region drawable. Press Inger to import it. Now we need to pass in the texture region for the Play game buttons Upstate, which we'll get from the menu items Atlist. For this, let's type menu items Atlas, D fine Region, and the name of it is simply play game button. Next, when a button is being pressed, it goes into the down state. So for setting this, we can type playGame button style dot down equals new Texture Region drawable. Menu items Atlas, DfineRgion. This one is called Play Game Button Down. Okay, now we need to create the button itself. To do this, we can type play game button equals new button, and we need to pass in play game button style. Like with the images, we need to scale the button by the world scale. So let's type play game button, that set size, play game button, do get width, times global variables, that world scale. Come a play game button. Do get height times global variables, that world scale. Okay, now we'll repeat all of this for the remaining buttons. Okay, here's the code for the settings button, quick game button, and the previous fighter and next fighter buttons. Notice that because the fighter buttons share the same texture, I created a single style for them both called Triangle button style. This style used the triangle button and triangle button down regions from the textualis. Then when I created the previous and Next Spider buttons, I passed in the triangle button style object for each of them. We're actually not yet finished with the previous button, however. If we open menu items that PNG again, the triangle button textures point to the right. This is good for the next Spider button, but for the previous spider button, we want the texture to point to the left. To do this, after we create the previous spider button and set its size, we first need to call previous Spider button that set transform and pass true. This will allow us to transform the button, such as by rotating it or flipping it, which is what we want to do. Now, by default, the origin of the button is at its bottom left, so it will rotate or flip along that point, which will affect the positioning of the button on the screen. Instead, we want to set the button's origin to its center. To do this, we can call previous fighter button dot set origin, previous fighter button, dot get width divided by two F, coma previous fighter button. DG height divided by two F. Finally, we want to flip the button horizontally. To do this, we call previous fighter button, D set scale X, and pass a negative one. Okay, that's it for the buttons. Now we just need to create the fighter display name label. So below this method, let's type private void create labels. For the fighter display name, we'll use the small font. So first, we need to create a Bitmap font object and get the small font from the asset manager. I'll comment, get the small font from the asset manager. Then we can do so by typing Bitmap font, pressing Enter to import it, and small font equals game dot assets dot manager dot get assets dot Small font. We also need to scale the font by the world scale, which we can do by calling Small font, do Git dataset scale, global variables dot WorldSCL. We need to call small font dot set us integer positions False, which as we learned back when we first created the fonts will prevent the letters from getting jumbled up. Okay? And for labels, we need to create a label style object, which will let us set things like which font to use for the label and the color of the font. First I'll comment, create the label style. Then to do so let's type label style, press Enter, then fighter display name label style equals new label dot label style. To set the style font to the small font, we can type fighter display name label style. Da font equals small font. And to set the color, we can type fighter display name label style. Da font color equals. Let's set it to black by typing color, making sure to import the GDX dot Graphics version, then dot black. Finally, we just need to create the fighter name display label itself. First style comment, create the fighter display name label. Then let's type fighter, display name label equals new label. Now on here we first need to pass in a string that will be the text for the label. This is, of course, going to change if the player changes their fighter, so we'll wait to do this in the show method. For now we can just use a blank string by typing quote. After that, we need to pass in the style. So let's type fighter display name label style. And that's it. Okay, in the next video, we'll get all of our widgets laid out on the screen. See. 35. Main Menu Screen 2: Draw the Widgets: As I mentioned at the end of the previous video, we want to set the text of the fighter name display label to the player fighter's name every time the show method gets called. This is because the player might have changed their fighter in the main menu, then played the game, then returned to the main menu, which will cause the show method to be called again. Okay, so in the show method, I first comment, set the fighter display name labels text to the name of player's fighter. And to set the text of the label, we can call fighter display name label dot set text. We want to use the player's name, which we can get by calling game dot player dot get name. I also think it would look better if you use all uppercase letters for the name label. To do this, after game dot player dot G name here, we can call the two uppercase method of the stream class by adding dot to uppercase. Alright, another thing we want to do in here is set the color of the fighter display image to the player fighter's color. For this our first comment, set the fighter display images color to the color of player's fighter. And to change the color of an image object, we can simply call it set color method and pass in a color. So let's type fighter display image, dot set color and pass in game dot player dot Get Color. Okay, we're next going to start laying out the widgets inside the stage. But before we do this, let's make it so we can see our progress as we go. First, we need to set the main menu screen as the default screen instead of the game screen. To do this, let's go to the main game class. At the top, after we create an object for the game screen, let's also create one for main menu screen by typing public main menu screen, pressing Enter, then main menu screen. Next in the create method, because we don't want to switch to the game screen anymore, I'll remove this part of the comment and delete the SutscreenGame screen line. Now our comment initialize the main menu screen and switch to it. Then we can initialize it by typing main menu screen equals new main menu screen and pass in this. And to switch to it, we can type set screen main menu screen. This won't work yet, though, because we haven't told the main menu screen how to render itself. So let's go back to the main menu class and go down to the render method. The first thing we will do in the render method is clear the screen to a blue color. We're actually going to use the same color for the setting screen and the loading screen. So let's first go over to the global variables class and create a variable for the color. We can create it here in the colors section. Let's do so by typing public static final color blue background equals new color, 0.25 F, 0.42 F, 0.61 f1f. Okay, back in main menu screen, let's set the screen color by typing screen Utils, pressing Enter to import it, then dot clear global variables, dot blue background. Okay, when we're using a stage, we don't have to do a bunch of calls to the Sprite Batchs draw method like we did in the game screen class. Instead, we simply have to call two methods stage dot act, which will perform some actions in the background and stage dot draw, which will draw all the stages actors to the screen. So first I'll comment, tell the stage to do actions and draw itself. Then us call stage dot at, and we need to pass in the Delta variable here. Next, we can call stage dot draw. And before we run this, we need to do a couple more things. First, in the resize method, we need to update the stage's viewport with a new screen size. So I'll comment, update the stage's viewport with the new screen size. And to do so, we can type stage dot get viewport, dot update with height, and true to center the camera. Next, in the dispose method, you need to call stage dot dispose, which will release the stage as resources from the memory. Okay, if we run this now, We currently just get an empty blue screen, and that's because we haven't yet actually added any actors to the stage. To do so, let's go back to the constructor. Another huge advantage of using scene two D for UI is that we can organize and position the UI elements using tables. Tables in scene two D work similarly to HTML tables, and that they allow us to organize things into rows and columns. Also their size and positioning will automatically adjust as we add things to them. For our layout, we're going to need a main table to hold everything. Inside the main table, we'll add two inner tables, a left side table, which will hold the logo and fighter display widgets and a right side table, which will hold the three buttons. And actually, all the fighter display widgets will be placed inside their own table, which will be added to the left side table. This will all make sense later. Okay? And we're going to do all of the table creation in a method called create tables, which we need to call after we create all the widgets. So at the bottom of the constructor, I'll comment, create the tables. Then let's call create tables. Alright, below all of these methods for creating the widgets, let's create the create tables method by typing private void create tables. We'll start by first creating the main table and adding it to the stage. I'll comment here, create the main table and add it to the stage. To create the table, we type table, import the scene DUI version, then main table equals new table. And for the main table, we wanted to fill up the entire stage. To do this, we call main table dot set fill parent and pass in True. Another thing we want to do is called main tablet set Round false. If we don't do this, when we add widgets to the table, the positioning will be rounded to the nearest integer values. This will result in the widgets not being lined up perfectly and won't look very good. We'll do this for all of our tables. And finally, to add a table or any actor to the stage, we call stage dot at actor and pass in the actor. So in this case, main table. If we run this now, it appears that nothing has happened because we haven't added any visible elements to the stage yet, but we now have a table that fills in the whole area here. And actually, if we go up here and call stage dot Seti Bgal true then run it. It's hard to tell, but there are now lines going along the edges of the screen. The blue one here on the right is the easiest to see. These are called debug lines, and they will help to make sure everything is lined up the way we want it. Calling the stage a set debug all method and passing in true will create debug lines along the bounding boxes of all of the actors, as we'll see in a bit. If we wanted to, we could also do this for a particular actor by calling it set debug method and passing in true. But we'll just debug them all for now. And when we're finished getting everything laid out, we just have to remember to remove this line. Okay, next, we'll create the left side table. First I'll comment, create the left side table. Then a type table, left side table equals new table. Let's call it left side table dot set round. False. For this table, we just want it to be big enough to hold all of the widgets we'll add to it, so we don't want to set it to fill the parent. Okay? The first widget we want to add to the left side table is the logo image. I'll comment, add the logo image to the left side table. And to do so, we call left side table dot AD and pass in an actor, so logo image. Okay, before we do anything else, let's go ahead and add the left side table to the main table so we can run the game and see how it looks. Down here, I'll comment, add the left side table to the main table. Then let's do so by calling Mintable dot A left side Table. Let's give it a run. Okay, so we have our logo image, but it has been stretched to fill up the entire screen. This is the default nature for a table. To fix this, when we add an actor to a table, we need to also tell the table what size to use. We can actually do this on the same line where we add the actor to the table. So here what we call left side table dot add Logo Image. Before the semicolon, we can put dot size, then pass in logo image dot get width, coma logo image, dog height. Now let's try it again. Okay, much better. Note that by default, LibGDx centers things in the table cells. Also, because we set debug all to true, we can see that it put green debug lines along the bounding box of the image. Okay? Now if we were to add another actor to the left side table, it would put the actor into a new column, but we want to add it to a new row instead. To do this, after we add the local image to the table, we need to call the row method on the table by typing left side table dot row. This will add a new row to the table. I'll also add to the common up here and start a new row. Also, by default, the actors will be placed right up against each other in the table. It would be better if we have some spacing or padding between them. To add some padding, after calling row here, we can go before the semicolon and type dot pad. We have methods for padding all sides and for padding a particular side. All we need is some padding for the top of the new row, so let's go with pad top. In this method, we need to pass in how much padding we want. This will be orld units. And I found that a good value for this is 1.5 F, but feel free to try other values if you want. Okay? Now we're ready to create a fighter display table and add it to the left side table. The fighter display table will actually consist of two tables, a main table that will use the fighter display background image as his background and an inner table that will have the previous fighter button, the fighter display image, and the next fighter button. Okay, so before we add the left side table to the main table down here, I'll comment, create the fighter display table and set as background to the fighter display background image. Lt us create the main fighter display table by typing table, fighter display table equals new table. Let's also call fighter display table that set round false. All right, so to set the background of a table, we call it set background method. So let's type fighter display table that set background. And we actually need to pass in a drawable object here. Fortunately, when we add a texture to an image object, it converts the texture into a drawable object, and to get the drawable, we call the images get drawable method. So we want to call fighter display background image that Get drawable. We also need to set the size of the table to be the same as the size of the fighter display background image. So let's fighter display table, that set size, fighter display, background image, Dogg width, coma fighter display, background image, Dogg height. Okay, let's give it a run. So we don't see the fighter display table yet, and that's because we forgot to add it to the left side table. So back in the code, I'll comment down here. Add the fighter display table to the left side table. Then let's call it left side table dot A, fighter Display Table. We also need to tell the left side table what size to use for the actor. So we can call it size, fighter display table dot get width come a fighter display table, do get height. Okay, let's try running it again. There we go. Now we have the logo image and the fighter display background image on two separate rows and with some padding between them. Now the reason we set the image here as the background of the table instead of adding the image to the table is that we want the inner table to be displayed inside this same area. If we added the image to the table instead, when we go to add the inner table to it, it would put it in a separate column. And by the way, the music is actually still playing in the game. I just muted it for the video so it wouldn't get too annoying. Can now let's create the fighter display Inter table. Let's do it before we add the fighter display table to the left side table. For the inner table, we'll have three widgets and three separate columns. The previous fighter button on the left, the fighter display image in the center, and the next fighter button on the right. So first I'll comment, create the fighter display in table and add the previous and Next fighter buttons and the fighter display image to it. Then let's create the table by typing table, fighter display in our table. Equals new table. And also called fighter display in our tablet Set Round. False. Okay, to add the previous fighter button to the table, we can type fighter display in our table dot add, previous Fighter button. We need to set the size by typing size, previous fighter button, dog Width. Come a previous fighter button. Dog heights. Next, we'll add the fighter Displayimage by typing fighter Display inner table dot Ad fighter Displayimage. Dot size, fighter Displayimage, dot get With. Come a fighter display image, Dog heights. Finally, let's add the next fighter button by typing fighter display in our table dot AD, Next Fighter button. Dot size. Next fighter button, dot get width. Come on Next fighter button, dot get height. Let's not forget to add the table to the fighter display table. Down here, I'll comment, add the fighter display inner table to the fighter display table. Then it's type fighter display table dot A, fighter display inner table, size, fighter display inner table. Dog width, C fighter display inner table. Do get height. One thing to know about tables, however, is that unless we actually set the size of a table manually by calling it set size method, calling get width and get height on it won't automatically return the correct values. In order to get the correct values, we first need to call the tables PAC method. So up here, after we finish adding things to the inner table, let's call fighter display inner table Da Pack. This will tell the table to correctly set its size according to everything that's inside it. Can now let's run the game. Alright, we have our widgets in the correct order and positions, and the fighter display is using the same color as the players spider. One thing we might want to do, though, is add some padding between the buttons and the fighter display. To do this, we can go to the line where we added the fighter display image to the table, and before the semicolon, we could put dot pad left. Let's go with 0.5 F. We also need to add the same amount of padding on the right, so let's type dot pad right, 0.5 F. Now if we run the widges are spaced out a bit better. Now one thing we forgot to do is add the fighter display name label to the bottom of the fighter display table. When we do this, we want to have it centered in this empty area here. As we've seen, when we add an actor to a table, it by default, gets centered inside this table cell, so we don't have to worry about that. We also don't need to worry about the width because that has already been taken care of when we added the inner table. So when we add the label, all we need to do is tell the table what height to use for the cell. Because we have the same amount of spacing on the top and the bottom to calculate the height of this area, we can subtract half the height of the display image from half the height of the background image. We also don't want to count this black border here, which is about half a world unit. So we'll subtract that as well. Okay, so after we add the inner table to the fighter display table here, we, of course, first need to add a new row. So let's call fighter display table dot o. Now, let's add the fighter display name label by calling fighter display table dot ad, fighter Display Name label. And because we just want to set the height, we can call it dot height. And for the height, we want fighter display background image, Dog height, divided by F minus fighter display image, Dog height, divided by two F, then -0.5 to account for the border. Okay, let's try it. Get our label widget with the correct text and the correct height as we can see by the debug lines around it. However, everything inside the main display table has been pushed up. To fix this, we need to add an empty row to the top of the table to account for the space up here as well. To do this above the line here where we add the inner table to the display table, we first need to add an empty actor to the table and make the height the same as the height that we use for the name label. To add an empty actor to the table, we can simply call fighter display tablet AD with no parameter. Let's set the height by calling height fighter display background image. Dog height, divided by F minus fighter display image, Dog height, divided by two F -0.5 F. And after this line, we need to go down a row by calling fighter display tablet Row. I'll also change the comment here too, fill in the fighter display table with an empty row on top for alignment, the fighter display inner table in the center and the fighter display name label at the bottom. All right, let's see how it looks. Perfect. Next, we'll create the right side table. The right side table is going to be much simpler than the left side table, as it would just consists of three buttons in three separate rows. So down here, after adding the left side table to the main table, I'll comment, create the right side table. Then this type table, right side table equals new table. Let's not forget to call right side table round false. Next I'll comment. Add the play game settings and quick game buttons to the right side table. Now let's have the play game button to it by typing right side table dot add, Play game Button dot SHE Playgamebton, dot get Width. Come a playGame button dot get height. Now we need to go down a row. So let's type right side table dot R. We'll also add one world unit of spacing between the buttons. So let's do dot pad top one F. Okay, now we can add the Settings button by typing right side table dot a, Settings button dot size, Settings button dot G Width Settings button dot get height. Then go down a row by typing right side table dot R dot pad Top one F. Now we just need to add the Quick game button by typing right side table dot ad, Quick game button, dot SE, quickgamebton, dot G W. Come a quick game button, dot get heights. Finally, we need to add the right side table to the main table, so I'll comment. Add the right side table to the main table. Then let's call maintable dot a. Right side table. Also, you might be wondering why we don't have to set the size when we add something to the main table. And that's simply because we set the main table to fill the entire screen. Therefore, the sizing of the table doesn't need to be adjusted every time we add something to it. Okay, let's run the game and see what we get. That looks okay, but we probably want to put some padding between the left and right side tables. To do this, at the end of the line where we add the right side table, we can put dot pad left and set it to something like F. All right, let's have a look. Nice. Everything is laid out perfectly. If we click one of the buttons, however, nothing happens yet. We'll fix that in the next video. See there. 36. Clicking the Buttons: Okay. In order to get the main menu buttons working, we first need to go down to the show method, like we did in the show method of the game screen class, we need to tell LibGDX what to use as the input processor. In GameScreen, we implemented the input processor class, and in the show method, we set GameScreen itself as the input processor. When using scene two D, however, we set the stage as the input processor. And we don't even have to bother with all the methods like key down, key up and touchdown as the stage will actually handle them for us. Okay, so I'll comment, set the stage as the input processor. To do so, we can type GDX, press Enter to Import it, then dot input, dot set input processor, and pass in stage. If we run the game now, When we click a button, we see that it switches back and forth between the up and down textures. So we now know that the clicks are at least being registered. Now we just need to tell each button what to do when the player clicks them. For this, let's go back to the crate buttons method. In order to tell a button what it should do when we click it, we need to add what's called a change listener to the button. We're going to do this for all of the buttons inside a method that we'll create called Add button Listeners, and we'll call the method at the bottom of the create Buttons method. So first I'll comment here, add the button listeners. Then let's call Add button Listeners. Okay, let's create this method down here by typing private void add button listeners. Let's begin with the listener for the playGame button. First I'll comment, add the playGame button listener. And to add a listener to a button, you need to call it add listener method. So let's type playGame button dot add listener. The change listener class that we'll use for the listener is an abstract class. So we'll instantiate it with an anonymous inner class expression by typing new changed listener and pressing Enter. As we can see, changed listener requires that we override the changed method, and this is where we tell the button what to do when the player clicks it. First, we can make it so clicking the button plays the clicksund. I'll comment, play clicksund. Then we can call game dot audio manager dot Play Sound. Pass N Assets, click Sound. Also, we need to make sure to put a semicolon after the closing curly brace in parenthesis here. Let's go ahead and give this a try. Now when we click the Play Game button, it plays a click Sound. All right, and of course, the other thing we want to happen when we click the button is to switch the screen to the game screen. For this, after playing the click Sound, I'll comment, switch to the game screen. Then we can call Game dot set screen and pass N game dot Game screen. Okay, let's see if it works. When we click the button, it plays the click sound and the game begins. Awesome. All right, next, we add a listener to the Settings button. So after all of these lines, I'll comment, add the Settings button listener. Then this type Settings button dot a listener. New change listener and press Center. We haven't yet created the setting screen, so we of course, can't switch to it, but we can go ahead and make it so the button plays the clicksund. I'll just copy and paste the lines from the play game button listener. Okay, let's put a semicolon down here and make sure it works. Now we'll add a listener to the Quick game button. I'll comment. Add the Quick game button listener. Then let's type Quick game button, dot add listener, new change listener. Press Center. Let's go ahead and paste the lines for playing the click sound. Now we want to make it so the button closes out the game. First I'll comment, close out the game. And to do this, we call gdx dot app dot EXE. All right, let's put a semicolon down here and try it. Perfect. Okay, now we just need to add listeners to the previous and Next Spider buttons. We're actually going to wait until the next video to start changing the fighters, but we can go ahead and make the buttons play the click sound. I'm just going to copy all the lines for the settings button and paste them down here. Then change it all for the previous Spider button. And I'll do the same for the next Spider button. Make sure they work. Nice. Right now, if we start the game and press the pause button, we, of course, want to make it so clicking the main menu button here, take us back to the main menu. We want to do this in the game over overlay as well. To get these buttons working, you need to go down to the touchdown method of the game screen class. Inside this method, we first check if the game is running, and if so, we handle all of this stuff. Then in the outs part of the statement, we figure out what to do when the game is either in the pause state or the game over state. For GameOver, we check if the play again button Sprite has been pressed, and for pause, we check if the continue button Sprite has been pressed. Unlike these two buttons, the main menu button gets displayed in both the pause state and the game overstate. Therefore, to handle when the player presses the main menu button Sprite, we can just add another Osif to this statement. Then type main menu button Sprite, Dt Get bounding rectangle, dot contains position dot X, come up position dot Y. Okay. And here, let's first play the clickund. I'll just copy and paste the lines from up here. Another thing we want to do is stop all the game sounds. This is mainly for when the player presses the main menu button and the game over overlay. If they press it while the cheer or boos sound is playing, we don't really need that sound to carry over to the main menu screen. So first I'll comment, stop all game sounds. Then it's type game audio Manager dot Stop Game Sounds. Also, if the player pause the game, then music will also be paused, so we want to resume it if the player return to the main menu. So I'll comment resume music if the game is paused. Then we can type I Gamesate is equal to gamestate dot paused, game audio Manager dot play Music. Okay, and one more thing we want to do before switching to the main menu screen is we want to deactivate all of the blood splatters. This is because when we switch between screens in the game, the previous screens objects remain in memory. So if the player pauses a game while a blood splatter is still animating, it's animation will be paused. But if the player then goes to the main menu screen, then back to the game screen, the blood splatters animation will resume. So we'll get a random blood splatter appearing at the beginning of the next game. Alright, so to deactivate all of the blood spliders, we simply need to loop through both fighters blood splatter arrays and call the deactivate method on each blood splatter. First I'll comment, deactivate all the blood spltrs. Then it's type four, int i equals zero, semicolon I less than blood splatter amount. Semicolon I plus plus. Plater blood splatters I dot deactivate, opponent blood spiders I dot deactivate. Finally, we can switch to the main menu screen. I'll comment. Switch to the main menu screen. Then it's called game dot set screen game dot Main Menu screen. Okay, let's run the game and make sure everything works. Alright, so if we click the pause button, it pause the game and the music, and now if we click the main menu button, it resumes the music and takes us back to the main menu. Now if we play the game again and finish it, clicking the main menu button will stop the sounds and take us back to the main menu. If we click the playGame button again, we still have the blood pools from the previous fight. We could deactivate these as well when we go back to the main menu, but I actually like how they carry over. So I'll leave them in, but feel free to deactivate them if you want. If you do want to, you will first have to create a public deactivate method in the blood pool class. Okay, before we move on to the next video, if we run the game again and minimize the window while it's on the main menu screen, the music continues to play. It would probably be better to pause the music the way we did in the game screen class. Then when we bring the window back up, we can resume the music. To do this, let's go back to the main menu screen and go down to the pause method. As we learned before, the pause method is called whenever the window gets minimized. So in here, we can also pause the music. I'll comment pause music. Then it's called game dot audio manager dot pas music. And to resume the music when the window comes back up, we can use the rezoom method here. I'll comment resume music if it's enabled. Then it's called game do audio manager dot play music. Now if we run the game and minimize the window, the music pauses, and when we bring the window back up, the music resumes playing. Okay, in the next video, we'll finish up the main menu screen by making it so the player can choose their fighter. See 37. Changing the Fighters: Before we can let the player choose their fighter, we need to have a list of fighters they can choose from. If we go into the data assets folder here, I've created this JSON file called Fighter Choices. If we open this up, the file basically consists of a list of four fighters with a name and RGB color values for each one. We could add another fighter if we wanted to by simply copying one of these chunks surrounded by curly braces. Put a comma after this closing curly brace here, then go down the line, paste the chunk and change the name and color values. But for now, I'll just stick with these four choices. Okay, so what we want to do is write some code to read this file and create an array of objects for holding the names and colors of each fighter choice. To begin, we'll create a class for holding each fighter choice, and we'll call it fighter choice. Let's create the class here in the Objects package. By right clicking the package, go to New Java class, typing fighter Choice and pressing Enter. The first thing we want to do is add a couple variables that will hold the name and the color values of the fighter. We look at the Json file again, the parts that are shown in purple here, name and color values are called keys, and the part that follows the col and after a key are the keys value or values. The value for the name key is a string as denoted by the quotation marks around it. For the color values key, we have three numbers between a pair of brackets. The brackets denote an array, and even though we don't have an F after these numbers, like we would in Java, these are actually floats. So the color values key points to an array of floats. In order to convert these key value pairs into fighter choice objects, the fighter choice class needs to have variables that match the keys exactly. This means that the names of the variables have to be the same as the names of the keys, and the types of the variables have to be the same as the types of the keys values. So with that in mind, let's go back to the fighter choice class. And by the way, it's also necessary that we make the variables public. So to create the variables, let's type public, string, name, and public float brackets color values, which will be an array of floats. Okay? And in order to set the color of a fighter, we'll call the fighter set color method, which requires a color object. Therefore, we'll need to convert the color values array into a color object. And to avoid having to do this multiple times, we'll store the converted color inside a private color object. For this, let's type private color, import the GDX style graphics version, then color. Okay, now we just need to create a couple of methods for returning both the name variable and the converted color object. For getting the name, let's type public string, get name, return name. For the color object, it's like public color, get color. Now, the first thing we want to do in here is check if the color variable is null. If it is, it means that the color values have not yet been converted into a color object, so we need to do so. Okay? So first, let's type if color is equal to null. I'll comment in here, I color hasn't been initialized, initialize it using the color values. Then this type color equals new color, color values zero, color values one, color values two, and we want it to be fully opaque, so we use a one for the Alpha channel. And after the I statement, we need to do return color. All right, so the first time this method gets called, it will first convert the color values into a color object. Anytime it gets called after that, it would just return the already converted color object. Also, you might be wondering why we didn't create a constructor for this class. And the reason is that we're going to be creating objects of this class a little bit differently as we'll see next. Right now, let's go to our main game class. In this class, we're going to create a method called Load fighter Choice List, which we'll use to convert the fighter choices dot JSON file into an array of fighter choice objects. And because we'll need to access this array from the main menu screen, we'll create a public global variable for it at the top of the main game class, and we'll add it here in the fighters section. We're actually going to use an array list for this, so that we'll be able to add items to it as we go. So first, let's type public final array list. And press Enter to import it, then less than sine. If you wanted to hold fighter choice objects, so let's type fighter Choice here and press Enter. Then let's put a greater than sine then fighter Choice list equals new array list. Less than greater than open and closed parentheses. Next, for the load fighter choice list method, let's go down here below the create method and type private void load fighter Choice List. Okay. And now, the reason I use the JSON file for the fighter choices instead of a simple text file, is that Lib gDx actually makes it very easy for us to read or parse JSON files, which we'll do now. I'll comment here, Load the fighter Choice list from the assets. First, we need to create a JSON object. We can do this by typing JSON, pressing Enter, then JSON equals new JSON. Next, we need to call the parse method of the JSON reader class and pass in the location of our JSON file, and we need to store the result in a JSON value object. Let's go ahead and do this. Then I'll explain how it works. So first, we need to create a JSON value object by typing JSON value, pressing Enter to import it. Let's call it fighter choices because it will be holding all of the fighter choices. Now we need to set this equal to new JSON reader, press Enter, then dot parse. And here we need to pass in the location of the JSON file, which we can do by typing GDX pressing Enter, then dot files dot Internal. And the file is located in the data folder. So let's put data slash Fighter Underscore Choices dot JSON. Okay, if we take a look at the JSON file again, what the Parse method basically does is it separates all of these parts between curly braces and returns them as an array, which we store inside a JSON value object. So now we just need to go through the array in the JSON value object, convert each item into a fighter choice object and add it to our fighter choice list. So to go through the fighter choices array, let's use a four loop by typing four and I equals zero, semicol and I less than fighter choices, dot size, semicol and I plus plus. Now we need to add the item to our fighter Choice list by first typing fighter Choice list dot AD. And to convert the item into a fighter choice object, we use json dot from JSON. And as the first parameter, we use fighterhoice, dot class. For the second parameter, we need to pass in the current item from the fighter Choices array. However, it's also required that we convert the item into a string. So to do this, we can type string dot value, fighterhoices, dot GTI. And as long as there are public variables in the fighter choice class that match the keys in the JSON file, this should all work without a hitch. Okay, now let's go up to the create method of the main game class. When we create the fighters here, instead of typing up the names and colors, we can use the first and second items from the fighter Choice list, which are actually the same as what we typed in here. But first, we need to actually load the fighter Choice list. So above these lines, I'll comment, Load the fighter Choice list. Then let's call Load Fighter Choice list. Okay, for the player fighter, I'll change the name to fighter Choice list dot Getzer dot Get NAM. And the color to fighter Choice List, dot get zero dot get color, which converts the color values into a color object. And for the opponent fighter, I'll use fighter Choice List, dot get one dot get Name. And Fighter Choice List, dot get one dot get color. All right. And just to make sure this works correctly, let's run the game. So the default player fighter is still named Slim Stalon and uses the red color. The opponent is still named Den diesel and uses the blue color. Awesome. Alright now let's head over to the main menu screen class and make it so the player can change their fighter. First, if we go up to the top of the class, back when we first created this class, we created a variable here called current fighter Choice Index. This will indicate where in the fighter choice list the player's current fighter is located. And we need to set this every time. The main menu screen becomes the visible screen. So let's go down to the show method. Okay, so at the bottom of this method, we're going to first set the current fighter choice index to zero. Then we'll loop through the fighter choice list until we find the item that matches the player's current fighter. To determine this, we'll simply check if their name values are equal. Okay, so I'll comment, find the index of players currently chosen fighter in the fighter choice list. Then let's first set current fighter choice index to zero. Now let's loop through the fighter Choice list by typing four int i equals zero, semicolon, I less than game dot fighter choice list dot size. Semicolon I plus plus. And here we want to check if the list items name matches the player fighter's name. To do this, we can type I game dot fighter Choice list dot dot g name equals game dot player dot get Name. And if so, we want to set the current fighter choice index to I. We also don't need to continue looping through the array anymore, so we can put a break here. All right? Now we just need to go up to the Add button listeners method and tell the previous and next fighter buttons what to do when the player clicks them. First, for the previous fighter button, we want to decrease the current fighter index by one, but only if the index is currently greater than zero. If it's zero, we'll set it to the last index in the fighter choice list. So after playing the click sound, I'll com it, choose the previous fighter in the fighter choice list or choose the last fighter if the beginning of the list has been reached. Then this type, if current fighter choice index, greater than zero, current fighter choice index minus minus. Then else current fighter choice index equals game dot fighter Choice list, D size minus one. After this, we want to get the current fighter choice object from the list and use it to set the player fighter's name and color. We also want to use it to set the color of the fighter display image in the main screen, as well as the text of the fighter display name label. So first I'll comment, set the name and color of player's fighter and the fighter display to those of the chosen fighter. Now let's type fighter Choice. Press Enter to import it. Then fighter Choice equals game do fighter Choice list, do GT, current fighter Choice Index. After that, let's do game dot player dot set name. Fighterchoice dot gt NAM. Then game dot player, dot side color, fighter Choice, dot get Color. Then fighter display image. Dot side color, fighter Choice, Dog color. And finally, fighter display name label, dot set text, fighter Choice, Dogname. Let's make it uppercase by adding dot two uppercase. Okay, for the next fighter button, let's copy all of these lines for the previous button. Then paste them in here after playing the click sound. For this one, you want to either increment the current fighter choice index by one or set it to zero if the end of the list has been reached. So I'll change the comment to choose the next fighter in the fighter choice list or choose the first fighter if the end of the list has been reached. For the I part here, you need to change this to current fighter choice index less than game dot fighter choice list dot size minus one, and change the line here to current fighter choice index plus plus. And in the outs part, you need to set current fighter choice index to zero. That's it. If you run the game now, you can click the previous and next fighter buttons to scroll through the fighter choices. And if we start the game, it will set the player's fighter to the chosen fighter. If you go back to the main menu, it will still display the correct fighter. Perfect. All right. Now one more thing we can do is choose a random fighter for the opponent. We also don't want the opponent to have the same fighter as the player that would just cause confusion. To do this, let's go to where we set what happens when the player clicks to play a game button. Before switching to the game screen here, we'll choose a random fighter from the list to use as the opponent Spider, and we'll make sure it doesn't match the players spider. First I'll comment, choose a random opponent fighter from the fighter Choice list, making sure it's different from players Spider. To get a random index from the list, we'll use the MatheuTills dot Random method and pass in the size of the list subtracted by one. Let's type int index equals MatuTills, Press Eger to import it, then random game dot fighter Choice list dot size minus one. Let's get the fighter choice at that index by typing fighter choice, fighter choice. Equals game fighter Choice list GT index. And we want to keep doing this until we find a fighter choice that doesn't match the player's fighter. Let's do this with a Wil loop by typing Wil fighter choice dot G name equals game dot player dot Get Name. Then we can copy and paste these two lines in here. But because we have already defined these variables, we need to remove the types in these lines. Finally, after the WOW loop, we just need to set the name and color of the opponent by calling game dot opponent, D side name, fighter choice, Dog name, and game dot opponent, do side color. Fighter choice, Dogg color. Alright, let's give it a run. Then when we start the game, it chooses a random fighter for the opponent, and it will never be the same as the player's fighter. Okay, now we can get rid of these debug lines in the main menu screen by going to the crate tables method and removing the stage set debug all true line here at the top. Alright, in the next video, we'll start working on letting the player change the settings of the game. See there. 38. Manage the Settings: To manage all of the settings that the player can change, we'll create a settings manager class. Let's do this in the resources package here by right clicking it, choosing new Java class, calling it settings manager. I'm pressing Inter. Now with the settings, we're going to want to save them to the player's computer or device so that when they run the game later on, we can load their previous settings. The settings will be saved inside our preferences file with the dot preface extension, and we'll need to give a name to each setting that we save in the preferences so that we can use the same name to load the setting later. So first, we'll create some static final string variables for holding the names of each setting. I'll add a section here for settings. Then let's begin with the setting for the music by typing private static final string. Is music on, equals quote Is music on. So there is music on string value here will be the name of the setting inside the preferences file. We also need one for the sounds. So let's type private static final string. R sounds on, equals quote R sounds on. Next for the difficulty setting, let's do private static final string. Difficulty setting equals quote difficulty. And for the blood setting, private static final string is blood on, equals quote is blood on. We actually want one more setting, which is whether or not to make the game window full screen. For this, let's type private static final string is full screen on, equals quote is full screen on. Okay. We also want some variables that will hold the values of all of the settings so that we don't have to keep reading from the preferences file. All of the settings, apart from the difficulty setting will be boolean values. So for the music setting, let's do private boolean, music setting on equals. Let's set it to true by default. For the sounds, let's type private boolean, sound setting on equals true. For the difficulty setting, we'll use the difficulty Enum from the global variables class. So let's type private global variables. And because this class is actually in the same package as the global variables class, we don't need to import it. So let's type dot difficulty, difficulty setting, equals. By default, let's set it to global variables dot difficulty dot easy. Next for the blood setting, let's do private boolean. Blood setting on equals true. And finally, for the four screen setting, let's do private boolean, four screen setting on equals, let's set this one to false by default. One more variable we'll need is a preferences object for holding the data of the preferences. Our first comment preferences. Then let's type private final preferences. Making sure to import the bad logic dot GDX version here, then prefs equals. To get data from a preferences file, we type GDX precenter then dot app dot Git preferences. And here we need to pass in a string that refers to the name of the preferences file we want to use. Feel free to use whatever you want. I'll go with quotesfSPEFS. It doesn't matter that this file doesn't exist yet, as it will get created automatically when we first save the settings. Next, we need to create a method for loading all the settings from the preferences file. For this, let's type public void load settings. And here I'll comment, get all the current settings from the preferences. Now, when we get a setting from the prefs variable, the method we use depends on the type of value we used when saving the setting to the preferences. For most of the settings we'll use Booleans, and we can get a Boolean value from a preferences objects by using the G Boolean method. So for the music setting, let's type music setting on equals prefs dot Get Boolean. And here we first need to tell which setting we want to get the value from. So let's pass in the is music on static variable. We also have the option of passing in a default value for the method to return in case it can't find the setting in the preferences file. We need to do this because the first time we load the settings, the preferences file won't actually exist yet. So for the music, let's put true for the default. Similarly, for the sounds, let's do sound setting on equals prefs, that'll get bullying. R sounds on true. For the difficulty setting, we can't actually save E Num values in the preferences. Instead, we'll convert the setting into an integer and save the integer. And when we load the setting, we'll convert the integer back into the correct Enum value. For Es we'll use a zero, for medium, we'll use a one, and for hard, we'll use a two. And to get an integer from the preferences, we use the G integer method. We want to call this method inside a switch statement, then set the difficulty setting to the Enum value that corresponds to the returned integer value. So spe switch prefs dot G integer. Difficulty setting. In case zero, difficulty setting equals global variables, that difficulty that easy. Break case one, difficulty setting, equals global variables, that difficulty that medium. Break. And for hard, we can do default. Difficulty setting equals global variables, that difficulty that hard. And by the way, with the G integer method, we don't have to pass in a default value because if it doesn't find the setting, it will just return a zero, which is fine because that will set the difficulty to easy by default. Okay, for the blood setting, let's do blood setting on. Equals preps, I get bullying. His blood setting on, true. And finally, for full screen, let's type full screen setting on equals prefs that get Boolean. Is full screen on, and we'll use false for the default value. All right, we next need to create two methods for each setting, one for changing its value and one for returning its value. For changing the Boolean settings, we'll create methods that toggle the setting on or off. So for the music setting, let's do public void toggle music setting. For a parameter, it's Tyblean on. If this is true, we'll turn the setting on, and if it's false, we'll turn the setting off. Okay, we only need to bother saving this setting if its current value doesn't match the past in value. Otherwise, we'll just be wasting time and processing power. So I'll comment here, if the new setting is different, update it. Then let's type, if music setting on is not equal to on. First, we want to update the music setting on variable to the new value by typing music setting on equals on. Next, with getting a value from the preferences, the method we use to save a value depends on the type of the value. For Booleans, we use the Put Boolean method. So let's type prefs dot Put Boolean. Okay, and we want to pass in the name of the setting, so is music on? Then we want to pass in the value for the setting so we can either pass in the music setting on variable or just pass in the on variable. Finally, we need to add dot flush to the end of this line. A call to the flush method will force the save to happen straightaway. Okay? And for the method to get the music setting, we can simply type public boolean. Is music setting on. Return music setting on. Next for toggling the sound setting, let's type public void, Tako sound setting, Boolean on. We can actually copy and paste the lines from the Togo music setting method, then change them for the sound setting. So sound setting on sound setting on and sounds on. The n let's create the Gitter method by typing public boolean is sound setting on. We turn sound setting on. For the difficulty setting, let's do public void set difficulty setting. And for a parameter, we want to pass in a difficulty Enum value. Let's type global variables. Difficulty. All right, first we want to check if the current difficulty setting is different from the past in difficulty value. I'll comment here, if the new setting is different, update. This type of difficulty setting is not equal to difficulty. Let's go ahead and set the difficulty setting to difficulty. Can now we want to use a switch statement to convert the new difficulty setting enum value into an integer value. So first, let's create an integer variable to hold the converted value by typing int difficulty int equals zero. Then let's do switch difficulty setting. Now, because we already have difficulty int set to zero by default, we can just skip the easy case. Let's type case medium. Difficulty int equals one, break, case hard. Difficulty int equals two. Finally, after the switch statement, we need to save the integer value to the preferences, which we can do with a put integer method. T's type prefs dot put integer, difficulty setting, coma difficulty Int. Let's not forget to add dot flush. All right for the Gier method, we can simply do public global variables. Do difficulty, get difficulty setting. Return difficulty setting. Next for the blood setting. Let's type public void, Togo Blood setting, bullying on. I'll copy and paste the lines from the Taco sound setting method. Change sound setting on to blood setting on. Change our sounds on to I blood on. For the Gitter let's do public boolean. His blood setting on. Return blood setting on. Finally, for the four screen setting, let's type public void, Tuggle four screen setting, boolean on. Then let's paste the lines. Change sound setting on to four screen setting on. And our sounds on to Is four screen on. And for the Gitter, let's type Public bullying. Is four screen setting on. We turn four screen setting on. Okay, we're finished with the settings manager class. So now let's head over to the main game class where we'll create a settings manager object and load all the settings. First, at the top of the class, after creating the audio manager object, let's like public settings manager. Press Enter to import it and settings manager. Okay, now on the create method, we'll initialize the settings manager and load all of the settings. We want to do this before we initialize the audio manager because we'll be using the settings manager to get the music and sound settings for the audio manager. So in here I'll comment, initialize the settings manager and load all the settings. Finish type settings manager equals new settings manager and settings manager Load Settings. Next, we need to remove the audio manager dot play music line here. Then I'll go down a line and comment, update the audio settings in the audio manager. First, we want to check if the music setting is on in the settings manager, which we can do by typing I settings manager dot is music setting on. And if so, we want to enable the music. Otherwise, we'll disable it. So let's type audio Manager dot Enable music. Else, audio manager dot disabled music. Remember that we made it so the enable music method will also start playing the music. Now let us do the same for the sound setting by typing I settings manager, that is sound setting on Audio manager that enables sounds. Else, audio manager that disables sounds. Alright, one more thing we want to do in here is check if the full screen setting is on, and if so, we'll make the window full screen. So first I'll comment. If the full screen setting is on, go to full screen. Then this type, I settings manager, that is full screen setting on And to go to full screen, we type GDX Graphics, dot set four screen mode. GDX Graphics dot G display mode. Now, because the default mode in our game is Windowed mode, we don't have to do anything if the four screen setting is turned off. But if we had made it so the default was four screen, to go to Windowed mode, we would use GDX Graphics, dot set Windowed mode and pass in the width and height and pixels that we want the Window to be. Okay, I'll delete this. Now, just to check that the full screen mode works, we can add an exclamation point before settings manager here in the If statement and run the game. Now it starts out on full screen. Okay, I'll remove the exclamation point so that it defaults to Window mode again. And in the next video, we'll get to work on the setting screen so that the player can actually change the settings. S there. 39. Settings Screen 1: Set Up the Widgets: To start creating the setting screen, we first need a setting screen class. So let's right click the screens package here. Go to New Java class, call it setting screen and press Enter. This class needs to implement the screen class. So after public class setting screen here, let's type implements, screen, and press Enter to import it. Let's go ahead and implement all of the necessary methods by hovering over this line, clicking implement methods. Take clicking ok. Okay, at the top of the class, we first need an object for the main game class. So let's type private, final SFS, precenter then game. Like with the main menu screen, we'll use a stage for the setting screen. So let's type private, final stage, Import the scene two D version, then stage. We'll also need to get the menu items texture atlas from the asset manager. So let's create a variable for this by typing private, final textutis, pressing Enter, then menu items Atlas. All right, we next need to add some variables for the widgets. If we open up menu items dot PNG and the Textures Assets folder, the widget we'll need are the settings image here, the sounds background image, the music background image, the full screen background image, the blood background image, the difficulty background image, and images for displaying the difficulty settings. This longer Show blood image here is actually just for mobile versions of the game, and we'll use it in the bonus Android section, right? And we'll also need a few buttons. The back button here, which will take us back to the main menu, the on and off checkbox buttons, the on and off toggle buttons, and we'll use the triangle button again for switching between the difficulty settings. Okay, so back in the setting screen class, let's start with the images. Our first comment, image widgets. Then let's type private image. Import the Centd dot UI version, then settings image. Lets us to private image, music setting background image. Then private image, sound setting background image, private image, difficulty setting background image, private image, full screen setting background image. Private image. Blood setting background image. And for the difficulty setting images, let's type private image, easy image, then private image, medium image, and private image Hardimage. Okay, next, for the buttons, I'll comment button widgets. Then let's type private button, Import the scene two D version, then back button. Okay, we're actually going to create two buttons that use the Toggle button textures, one for the music setting and one for the sound setting. So first, let's do private button music toggle button. Then private button. Sounds Taco button. We also need two buttons that use the triangle button texture for switching between the previous and next difficulty settings. For these, let's type private button, previous difficulty button, and private button Next difficulty button. Finally, we'll need two buttons that use the checkbox textures, one for the full screen setting, and one for the blood setting. Let's type private button, full screen check button, and private button Blood check button. All right well next create a constructor for the class. So below all of the variables, let's type public setting screen. Befor a parameter, we'll need SFS game. And here let's first do this game equals game. Next, we need to initialize the stage and set up its Viewport. I'll comment set up the stage. Then this type stage equals new stage. After that, let's do stage Dot Set viewport, and we'll use the same extend viewport settings as the other screen classes. Let's type new extend viewport, presenter, then global variables, presenter, rod width, comma global variables, dot Minord height, comma global variables, dot rod Width, C zero, and comsge dot G camera. Now we need to get the menu items Atlst from the asset manager. So I'll comment, get the menu items texture atls from the asset manager. Then let's type menu items Atls. Equals game dot assets dot manager, dot Git, assets precenter then dot menu items Atlist. Okay. Now we'll create methods for creating the images, the buttons, and the tables. Let's go ahead and call the methods here. First I'll comment, create the widgets. Then let's call create images and create buttons. Next all comment, create the tables. Then let's call create tables. Okay, below the constructor. Let's begin with the create images method by typing private void, create images. First, we'll create the settings image. So I'll com it, create the settings image. Then I'll start by typing Settings image equals new image, menu items atlas, Dt Fine region. And the region for this is called settings with as. Now we need to scale it size by the world scale. So let's type settings Image, set size, Settings Image dot get Wi times global variables, dot world scale. Settings image get heights, times global variables world scale. Next, we'll create the background images for the settings. I'll comment here, create the settings, background images. Then I'll start with the music one by typing music setting background image. Equals new image, menu items Atlas, Dt fine region, music setting background. Then let's set the size by typing music setting, background image, dot set size, music setting, background image, Dog width, times global variables that row scale. Come a music setting background image. Dog height, times global variables that row scale. Okay, and now I'll do all of this for the sound setting background image, the difficulty setting background image, the full screen setting background image, and the blood setting background image. Okay, here's the code for all the background images. Now we just need to create the images for the three difficulty settings. First I'll comment, create the difficulty images. Then let's start with easy image by typing easy image, equals new image. Menu items at list, define region, easy. And easy image that set size, easy image, that get width, times global variables that roll scale. Came my easy image, get heights, times global variables that roll scale. Okay. Now I'll do the same for medium image and hard image. There we go. Okay, we're done creating the image widgets. So now let's create the create buttons method below this by typing private void, create buttons. Let's begin with the back button. First style comment, create the B button. And like with the buttons we use in the main menu screen, we'll have two different textures for each setting screen button. Therefore, for each button, we need to create a button style object to connect the buttons to textures to two different states for the button. For the back button, we have a texture for the up or default state and a texture for the down state when the player presses the button. So to create the button style object, let's type button style, press Enser to import it, then back button style equals new button dot button style. Next, we'll set the up texture by typing Back button style dot U equals. Remember that this needs to be a texture region drawable object. Let's type New texture region drawable. Press center. Then we can pass in menu items atlas, DfineRgion Babton. Okay, for the down state, let's type Babton style dot down equals new texture region drawable. Menu items Atlas, Define region, Babton down. Next, we can create the back button itself by typing back button, equals new button, and pass in Bbton style. We also need to set it sizes site B button, set size, BbtonG width, times global variables, that world scale. Come a Bbtont get height. Times Goble variables, that world scale. Right next, we'll create the music toggle button and the Sounds toggle button. These will share the same button style, which we'll call toggle button style. So let's create it first. I'll comment. Create the Toggle button style. Then let's type button, D button style, toggle button style equals New button, D button style. For the Upstate, let's do Toggle button style dot U equals New texture Region drawable. Menu items Atlas dot Fine Region, Toggle button off. Okay. And instead of doing a down state, we're going to do a checked state. This is because the down state is only for when the user is currently pressing the button. Once they release the button, it goes back to the upstate. For the toggle buttons, we want them to switch back and forth between being on and off each time the user clicks it. To do this, we can switch the button between the check state and the unchecked state. The unchecked state is actually just a default upstate. And for the checked state, we can do toggle button style checked equals new texture region drawable. Menu items atlas DFineRgion toggle button on. Can let's create the Music Tokle button. First I'll comment, create the Music Tockle button. Then let's type Music Tockle button equals New button and pass Sintogle button style. Then let's set the size by typing Music Talkle button set size. Music Toggle button, dot G width times global variables, do world scale. C Music toggle button dot get height. Times global variables, world scale. Okay, and for the Sounds tackle button, I'll copy and paste these lines here. Change the comment to sounds Tackle button and change all of these to sounds tackle button. Next, we'll create the previous the next difficulty buttons. Both of these will use the triangle button textures. So start by creating a style for this. I'll comment, create the triangle button style. Then it's type button, Di button style. Triangle button style equals new button, Die button style. We'll just use the up and down states for this. For the upstate, let's do triangle button style dot up equals new texture region drawable. Menu items atlas. Do fine region, triangle button. And for the downstate, let's do triangle button style dot down, equals new texture region drawable. Menu items Atlas, DfineRgion triangle button down. Okay, for the previous difficulty button, I'll comment, create the previous difficulty button. Then let's type previous difficulty button, equals new button, and pass in triangle button style. Then let's set the size by typing previous difficulty button, dot set size, previous difficulty button, dot get width. Times global variables, that rolled scale. Come a previous difficulty button that get height. Times global variables, that rolled scale. For the next difficulty button, I'll copy and paste these lines and change everything to next difficulty button. Let's not forget, however, that we need to flip the previous difficulty button horizontally so that the texture faces to the left. To do this, after setting the size of the button, we first need to make it so that we can transform the button by typing previous difficulty button, Do set transform True. Next, in order to get the button to flip without changing position, you need to set its origin to its center by typing previous difficulty button, dot set origin, previous difficulty button, Dot get width divided by two F, come a previous difficulty button. Now get height divided by two F. Finally, we can flip it horizontally by calling previous difficulty button dot set scale X, and pass on negative one. After creating the difficulty buttons, you need to create the four screen check button and the blood check button. Both of these will use the same check button style, which we use on and off checkbox textures. For the style, I'll comment, create the check button style. Then it's type button, D button style. Check button style equals new button do button style. For the upstate, let's do check Buttonstyle dot up. Equals Du text Region drawable. Menu items alist. Do Fine Region. Check button off. Like with the toggle buttons, we want to use the check state for this. Let's type Check button style dot check. Equals New texture Region drawable. Menu items alist Dot Fine Region. Check button on. Okay, for the four screen check button, I'll comment, create the four screen check button. Then let's type four screen check button equals new button. Check button style. And four screen check button that says size, four screen check button do get width, times global variables, D Raw scale. Come a four screen check button, do get heights. Times global variables, that roll scale. Then I'll copy and paste these lines. And change them for the blood check button. All right. Before we start creating the tables, let's get everything set up correctly so that we can start drawing things to the screen as we go. First, let's go down to the render method. Like we did with the main menu screen, we're going to first clear the screen to the blue color we define in global variables. So let's type screen Utils, press Enter, then clear Global Variables, dot blue background. Next, we need to call the act and draw methods on the stage. So I'll comment, tell the stage to do actions and draw itself. Then let's type stage dot Act, and pass in Delta. Then stage draw. Lex in the resize method, we need to update the stages viewport. So I'll comment, update the stages viewport with a new screen size. Then this type stage dot get viewport dot update with height, true. Okay, and down here in the Dispose method, that's call stage dot dispose. We can also go ahead and make it so the music pauses in the pause method and resumes in the resoom method. So in the pause method, I'll com it pause music. Thens type game dot audio Manager dot Pas music. And in the rezoom method, I'll comment Resume music if it's enabled. Then it's called game dot audio manager dot play Music. Now we need to make it so we can actually get to the setting screen, which we'll do by clicking the settings button in the main menu screen. But first, we need to go to the main game class and create a setting screen object. Alright, so in the screens variable section here, let's type public setting screen, presenter, then setting screen. Next in the create method. Before we initialize and switch to the main menu screen, let's initialize the setting screen. I'll comment, initialize the setting screen. Then a slide setting screen equals new setting screen, and pass in this. Can now let's go to the main menu screen class. And here we want to go to where we add the Settings button listener and the Add button listeners method. After the line where we play the click sound, I'll come it, switch to the setting screen. Then it's called game D set screen game D setting screen. Okay, if we run the game, we get an error saying that the create tables method and the setting screen doesn't exist. So let's go down here below the create buttons method and create it by typing private void create tables. And we'll leave it empty for now. Right now, if we run the game, and click the settings button. It takes us to our currently empty setting screen. In the next video, we'll start creating the tables and get our widgets on the screen. See there. 40. Settings Screen 2: Draw the Widgets: All right inside the create tables method of the setting screen class. Let's first type stage, do set debug all. True, to show the debug lines for everything so we can make sure it's all properly aligned. Next, we'll create a main table, which will fill up the entire screen and hold all of the other tables. We'll go ahead and add it to the stage. So first I'll comment, create the main table and add it to the stage. To create it, let's type table. Import the scene to D version. The main table equals new table. We wanted to fill the screen, so let's type main table, dot set fill parent. True. We also want to call main table dot set Round false so that the positioning and size values don't get rounded to integers. Now let's add it to the stage by typing stage dot add actor and pass in main table. Okay, so inside the main table, we're actually going to have four separate tables, one for the back button and settings image, one for the audio settings, one for the difficulty setting, and one for the full screen and blood settings. Each of these tables will go into a separate row inside the main table. Also, each settings table will actually consist of two tables. The audio table will have a music table and a sound table. The difficulty table will have a main table with a background image and an inner table for choosing the difficulty setting, and the full screen and blood table, which you'll call the bottom table will consist of a full screen table and a blood table. Alright, so with all of that in mind, let's start with the table that will hold the Back button and the settings image, which we'll call the banner table. So first, I'll com it, create the banner table. Then this type table, banner table equals new table. Let's go ahead and call banner tablet Set Round. False. Okay, next, we'll add the back button and the settings image to the table. So I'll comment. Add the back button and the settings image to the banner table. Then let's type Banner table dot AD Babton. We need to tell what size to use by typing dot size. Back button, dot get width, coma Bbton dot get heights. Next for the settings Image, let's type banner table dot Ad settings Image size, settings Image Get Width, Coma Settings Image dot get height. Let's go ahead and add the banner table to the main table. I'll comment here. Add the banner table to the main table. And let's type main table dot ad, banner table. Now if we run the game and go to the setting screen, We have our widgets in two separate columns in the table. Currently, however, the widget are centered together vertically on the screen, so the center point is about right here. When we start adding the other tables, this isn't going to look very good. Instead, we want to make it so the center point of the table is at the center of the setting image. To do this, we can add a blank actor to the table on the right side of the setting image and make it the same size as the back button. So after we add the two wiges, but before we add the banner table to the main table, I'll comment, add an empty cell to the banner table, the same size as the back button in order to center the settings image. And to do so, we can type banner table dot AD with the no parameter. Then dot size, Bbton dot get width, Back button, dot get height. If we try it now, We have an empty cell on the right, and the center point is at the center of the settings image. Okay? And because we want to put the next table in a new row, after we add the banner table to the main table, let's go down a row by calling main table dot o. Let's also add one world unit of padding to the top by doing dot Pad top one F. Okay, next, we'll create an audio table, which will contain the music table and the sounds table. So first I'll comment, create the audio table. Then this type table, audio table, equals new table, and audio table that's set round false. Alright now we need to create the music table, we'll set its background to the music setting background image. So I'll comment, create the music table and set its background to the music setting background image. Then it first New table, music table, equals new table, and music table that's set round false. Okay, to set its background, we can type music table that set background. This needs to be a drawable object, which we can get from the music setting background image by typing music setting background image, get drawable. We also want the table to be the same size as the background image. So let's type music table, that's the size. Music setting background image, dot get width. Come a music setting, background image, dog height. Okay, now we want to add the music toggle button to the music table. However, we want the button to be aligned to the right side of the table. To do this, we can first add a blank actor to the table. That's the width of this empty area here. To get the width, we can take the width of the music setting background image and subtract the width of the button from it. We also want some padding of about two world units on the right between the button and the edge of the image. So we'll also subtract that from the width. Okay, so down here, I'll comment, add an empty cell for alignment and the music toggle button to the music table. Then let's add the empty cell by typing music table dot ad with no parameter, and we only care about the width. So let's type dot With Music setting background image, dot get With minus Music toggle button, dot get Width, minus two F. Now we can add the music toggle button to it by typing music table, dot A, Music toggle button dot size, musictoggle button.gw. Come a music toggle button, dot get Heights. All right. And in order to check this, let's go ahead and add the music table to the audio table and the audio table to the main table. First I'll comment, add the music table to the audio table. Then type audio table dot ad Music table. We need to tell it what size to use by typing dot size, music tablet get width. Come on music table get height. Next I'll comment, add the audio table to the main table. Then it's type maintable dot a audio Table. And as we learned in the video about the main menu screen, because the main table fills the entire screen, we don't have to set the size when adding things to it. All right, if we run the game now and go to the setting screen, we have our music table with this background set and the button aligned to the right with some padding here. Perfect. Next, we'll create a table just like this one before the sounds. Then put it in the audio table to the right of the music table. Okay, after adding the music table to the audio table, but before adding the audio table to the main table, I'll comment, create the sounds table and set as background to the sound setting background image. Then let's do so by typing table. Sounds table equals new table. Sounds table, that's set round, false. Sounds table, that's set background, Soundsetting backgroundimage, that get drawable. And sounds table that set size, sound setting background image, that get width. Come a sound setting background image, that get heights. Okay? And like with the music table, we need to first add an empty cell to the table, then add the Sounds toggle button. So first, I'll comment, add an empty cell for Alignment and the Sounds Toggle button to the sounds table. Then it's type soundstable dot ad dot W, sound setting background image, do get With minus Sounds Toggle button, dog With minus two F. Then sounds table dot A, Sounds toggle button. Size soundstogO button, dog With coma Sounds to or button, dog heights. Alright now we'll add the sounds table to the audio table. So I'll comment, add the sounds table to the audio table. Then this type audio table dot a. Sounds table Sounds table dot get W. Sounds table dog heights. Now if we run the game and click Settings, We get our music and sounds tables lined up nicely. Also, notice that we already have some spacing between the tables here. This is because when I created the sounds background texture, I added some extra blank space at the front of the image here. I did this to make it easier to get everything lined up correctly without having to worry about figuring out exactly how much padding to add. Okay, we'll next create the difficulty table, which will contain a difficulty selection table for switching between the difficulty settings. But first, we want to put the difficulty table below the audio table. So after we add the audio table to the main table, let's go down a row by typing maintable dot R. Let's add some padding to the top by typing dot Pad top one F. Okay, below this, I'll come it, create the difficulty table and set as background to the difficulty setting background image. Finish type table, difficulty table equals new table. Difficulty table, that set round, false, difficulty table, that set background, difficulty setting background image, docket drawable, and difficulty table that set size, difficulty setting background image, docket width, Come on difficulty setting, background image, do get height. Alright, now we need to create the difficulty selection table, which will go inside the difficulty table. So I'll comment, create the difficulty selection table. Finish type table, difficulty selection table equals new table. Difficulty selection table. That's set round, false. Okay, so first, when we add the difficulty setting images to the table, we actually want to put them on top of each other in the same table cell and only show the one that corresponds to the current difficulty setting. To lay out widgets on top of each other, we use the stack class of the scene two D framework. We can add the widgets that we want to stack into a stack object, then add the stack object to the table. Alright, so to create a stack object for the difficulty images, our first comment, create the difficulty image stack and add the difficulty images to it. Then it's type stack, pre center, then difficulty Image stack equals new stack. And to add a widget to the stack, we can use either the add method or the Add actor method. So to add the easy image, we can type difficulty image stack, dot add Esimage. Then we can do the same for the medium and hard images by typing difficulty image stack, dot add medium image, and difficulty image stack dot add Hart Image. We also need to set the size of the stack. Because these three widget are the same size, we can just use, for example, the size of the easy image. So let's type difficulty Image stack. That set size, easy image, that get width. Come a easy image, that get heights. All right, now we can add the previous difficulty button, the difficulty image stack, and the next difficulty button to the difficulty selection table. So first, I'll comment this out. Add the difficulty selection buttons and the difficulty Image stack to the difficulty selection table. Then this type difficulty selection table dot add, previous difficulty button size, previous difficulty button, dot get width. Come a previous difficulty button dot get heights. Then difficulty selection table dot add, difficulty Image stack size, difficulty Image stack, dot get width. Come difficulty image stack, do get heights. We also want to add some padding of, say, half a world unit between the image stack and the buttons on both sides. So let's type dot pad left, 0.5 F, dot pad right, 0.5 F. Finally, let's type difficulty selection table dot AD, Next difficulty button size. Next difficulty button, do get width. Next difficulty button, dog height. Now we can add the difficulty selection table to the difficulty table, then add the difficulty table to the main table. However, because we want the difficulty selection table to be aligned to the right side of the difficulty table, we also need to add a blank actor to fill in the empty space on the left side. So first I'll comment, add an empty cell for alignment and the difficulty selection table to the difficulty table. Then type difficulty table dot add, dot W, difficulty setting background image, Dt get Wi, minus difficulty selection table, doll get Width and minus F to add some padding to the right side. Now we can add the difficulty selection table by typing difficulty table dot A. Difficulty selection table size, difficulty selection table, do get Width Coma difficulty selection table, dog heights. Nextyle comment, add the difficulty table to the main table. Then let's type main table dot add difficulty Table. Let's go ahead and add a new row by typing main table dot R and add some padding to the top by typing dot pad top one F. Now if we run the game and go to the settings, We have a couple of problems. First, the difficulty table has been expanded to fill up the rest of the main table. I know I said before that because we have the main table filling up the screen, we don't have to set the size when adding things to it. However, we will occasionally run into times when we do need to set the size. I'm not sure exactly what causes the issue, but in any case, to fix it, after adding the difficulty table to the main table, we simply need to add size, difficulty table, dog width, coma difficulty table, dog height. But check out the setting screen now, The difficulty setting background image is properly sized, but the difficulty selection table is too far to the right. This is because, as I mentioned briefly, when we were creating the main menu screen, adding things to a table doesn't automatically set the table size values. So when we call get width and get height here on the difficulty selection table, it doesn't return the expected values. To get the correct values, after adding everything to the difficulty selection table here, we need to call difficulty selection table Doc PAC, which will force it to calculate its size values. Now when we check out the setting screen, Everything is lined up correctly. Now we just need to add the bottom table, which will contain the four screen table and the blood table. For the bottom table, I'll com it, create the bottom table. Then it's type table, bottom table, equals new table. Bottom table, that's set round, false. Next for the four screen table, I'll com it, create the four screen table and set as background to the four screen setting background image. Thinness type table, four screen table, equals new table, four screen table, that set round, valse, four screen table, that set background, four screen setting, background image. Do get drawable four screen table that set size, four screen setting, background image, do get width. Come a four screen setting, background image, do get height. Okay, now we'll add an empty actor to fill in the left side of the table. Then add the four screen check button to the table. I'll comment, add an empty cell for alignment and the four screen check button to the four screen table. Then it slide four screen table dot add dot W. Four screen setting background image. Doll get width minus four screen check button, do get width, minus two F. Then four screen table dot A, four screen Check button, size, four screen check button, dot get Wi. Come a four screen check button, do get heights. Add the four screen table to the bottom table. So I'll comment. Add the four screen table to the bottom table. Then type bottom table dot Ad four screen table size, four screen tablet get width. Come a four screen tablet get height. Let's also go ahead and add the bottom table to the main table so we can make sure it all works. I'll comment at the bottom table to the main table. Then type main table dot A, bottom table. Now let's run the game and go to the settings. Nice. Okay. Finally, for the blood table, I'll first copy all of these lines for creating and adding the full screen table and paste them before adding the bottom table to the main table. Then I'll change everything up to use the correct objects. Okay, here's the result. We have to make sure to use the blood table, the blood setting background image, and the blood check button. Let's give it a try. Perfect. Like with the sound setting background image, I added some extra transparent space on the left side of the blood setting background image to give it some padding. Okay, now we can go to the top of the crate tables method and remove the set Debug all line. Alright, in the next video, we'll make it so these settings actually show the correct settings. Let the player change them. See there. 41. Changing the Settings: In order to make it so the player can click the buttons in the setting screen, we first need to go to the show method and set the setting screen stage as the input processor. So first I'll comment, set the stage as the input processor. Then type GDX, pre center, then dot input dot set input processor, stage. Another thing we need to do in the show method is make it so the widgets show the correct current settings. So first I'll comment, set the settings widgets to show the current settings. Let's start with the music setting. So what we want to do is check if the music setting is on by calling the Is music setting on method of the settings manager, and if so, you need to set the music toggle button to the check state. If the music setting is off, you can just leave the button on the default upstate. So let's first type I game settings manager, do I music setting on. And to set the button to the check state, we call Music toggle button checked and pass in true. Similarly, for the sound setting, we can do if game that settings manager, that is sound setting on Sounds toggle button set checked. True. Next for the difficulty, we need to check what the current difficulty setting is by calling the G difficulty setting method of the settings manager, then we need to hide the two difficulty images that don't match the difficulty setting. Let's do this with a switch statement by first typing switch game Dot settings manager, Dot get difficulty setting. Case easy. If the difficulty is set to easy, we need to hide the medium image and the hard image. To hide a widget, we call it set visible method and pass in false. So for the medium image, we can cite medium image, dot set visible, false. And for the hard image, hard image, dot set visible, false. Now let's put a break. Then case, medium. Easy image that's a visible, false, hard image, that's a visible, false. Then break. Finally, for hard, we can just do default. Easy image that's a visible, false medium image that's a visible false. Okay, now we just have the four screen setting and the blood setting, which we do the same way as the music and sound setting. So for the four screen setting, let's do I game does settings manager, there is four screen setting on. Four screen check button. That's checked, true. And for the blood setting, if game that settings manager, there is blood setting on. Blood check button. Dt checked, true. If we run the game now and go to settings, it shows us the correct settings. The music and sounds are on. The difficulty is easy, full screen is off and blood is on. Now let's make it so we can click the buttons to change the settings. To tell the buttons what to do when we click them, we need to add some change listeners to them. Like we did in the main menu screen class, we'll create a method for this called Add button Listeners, which we'll call at the end of the create Buttons method. So first, let's go to the end of the Create Buttons method. And I'll comment, add the Button listeners. Then let's call Add button Listeners. Then below the create buttons method, let's type Private void add Button Listeners. We'll start with the Back button, which we want to take us back to the main menu when we click it. Okay. First I'll comment. Add the Back button listener. Let's do so by typing Babton dot ad listener, New change listener and press Center. First, we want to play the click Sound. So I'll comment, play Clicksund. Then this type game dot audio Manager, dot play sound assets dot click SMD. I want to set the screen to the main menu screen. So I'll comment, switch to the main menu screen. Then this type game dot set screen game dot main menu screen. Can I put a semicolon down here. And if we run the game and go to the settings, we can click the Back button to return to the main menu. All right next is the Music toggle button. I'll comment. Add the music toggle button listener. Then it's type Music toggle button, dot add listener, New change listener, and press Center. First, we'll play the click sound, so I'll just copy and paste the lines from up here. Now, each time we click a button widget, its state actually switches back and forth between being checked or unchecked. So what we want to do is call the toggle music setting method of the settings manager and toggle the music setting on or off based on whether the music toggle button is currently checked. So I'll comment, toggle the music setting based on the buttons checked state. Tennis type game settings manager, DTggle music setting. And to get a buttons checked state, we use the I checked method. So music toggle button, that is checked. One more thing we want to do is use the new music setting to determine whether to enable or disable the music in the audio manager. So I'll comment, if the music setting is on enable music, otherwise, disable it. Then this type game settings manager is music setting on game audio manager that enable music. Else, game audio manager disabled music. Now we can put a semicolon here. Then if we run the game and go to the settings, we can toggle the music on or off. This also saves the setting to the preferences. So if we close up the game and run it again, we can see that the music isn't playing. And if we go to the settings, we can see that the music setting is off. Next, the listener for the Sounds toggle button is very similar. So I'll first copy and paste all these lines. I'll change this to Sounds toggle button. Change this to sounds toggle button. Change this to toggle the sound setting. Change Toggle music setting here to to sound setting. And music toggle button to soundstago button. Then I'll change this comment to if the sound setting is on, enable sounds, otherwise, disable them. Now change this to Is sound setting on. Change this to enable sounds and this one to disable sounds. If we run the game now, we can turn off the sounds which includes the click sound. And if we start the game, we no longer hear any hit sounds. Okay, next, we have the previous and next difficulty buttons. With these, we want to cycle through the difficulty settings, changing the setting in the settings manager and showing only the difficulty setting image that corresponds to the current setting. Starting with the previous difficulty button, I'll comment, add the previous difficulty button listener. Finis type previous difficulty button, dot add listener, new change listener, pre center. We want to first play the click sound, of course, so I'll copy and paste the lines from up here. Next, I'll comment, go to the previous difficulty setting or go to hard if currently uneasy. Also make the corresponding difficulty image visible and the other is invisible. Then let's type switch, game dot settings manager, Dog difficulty setting. Case Easy. If it's currently easy, you want to change the setting to hard in the settings manager. Let's type game D Settings manager, D set difficulty setting. Global variables difficulty dot hard. Next, we want to hide both the easy image and the medium image by typing easy image, that's visible, false and medium image, that's a visible false. Then we want to show the hard image by typing hard image that's visible, true. Okay, let's set a break then case medium. We want to set the difficulty to easy this time. So that's called game that settings manager, that'll set difficulty setting. Global variables dot difficulty dot easy. Now let's show the easy image by typing easy image, that's a visible, true. Then hide the others by typing medium image, that's a visible false. And hard image, that's a visible false. Then let's at a break. And finally, for hard, we can do default. Game Dow settings manager, dot set difficulty setting, global variables dot difficulty dot medium. Easy image, that's a visible, false, medium image, that's a visible, true, and hard image that's a visible false. Okay, let's add a semicolon down here. Let's go ahead and copy and paste all of these lines for the next difficulty button. I'll change this to Next difficulty button and this to Next difficulty button. For the comment in here, I'll put go to the next difficulty setting or go to easy if currently on hard. Okay, if it's on easy, we want to go to medium. And we want to make the easy imagion visible, the medium image visible, and the hard imagion visible. Next for medium, we'll go to hard. Make the easy and medium images invisible and the hard image visible. Finally, for hard, we'll go back to easy. Make the easy image visible, and the other is invisible. All right, if we run the game and go to the setting screen now, we can cycle through the difficulty settings. If you put it on medium or hard and start the game, however, the game is still uneasy. This is because we never made it so that the game screen gets the difficulty setting from the settings manager. So let's do that now by going to the game screen class and up to the show method. Before calling Start game here, I'll comment, get the difficulty setting from the settings manager. Let's do so by typing difficulty equals game, that settings manager, get difficulty setting. We actually also need to get the blood setting from the settings manager. So I'll comment, get the blood setting from the settings manager. Then a type showing blood equals game that settings manager. That is blood setting on. Okay, if we run the game now, change the difficulty setting and start the game. It uses the correct difficulty setting. Okay, back in the setting screen class, we just need to add listeners for the full screen check button and the Blood check button. For the full screen check button, I'll comment, add the full screen check button listener. Then it's type four screen check button, dot a listener, New change listener and press Center. First, we'll play the click sound, so I'll copy and paste the lines from here. Next, we want to toggle the full screen setting, based on the buttons checked state. So I'll come it toggle the full screen setting, based on the buttons check state. Finnis type game settings manager. Do Toggle four screen setting. Full screen check button that is checked. Next, based on the new four screen setting, you want to either put the game in full screen mode or Windowed mode. So first I'll comment, if the four screen setting is on, go to full screen mode, otherwise, go to Windowed mode. In this type, if game that settings manager, that is full screen setting on. And as we saw earlier in the main game class, to go to full screen mode, we type GDX digraphics that set four screen mode. GDX digraphics Dot get display mode. Now let's do ls. And to go to Window mode, we type GDX dot graphics, dot set Window mode and pass in the desired within height of the window. We can use the defaults we created in the global variables class by typing global variables, dot Window width, coma global variables. Dot Window height. Now let's put a semicolon down here and give this a try. Okay, we can turn four screen on or off now. If we turn it on and close out the game, when we run it again, it automatically goes to four screen mode. Finally, let's add the blood check button listener. I'll comment. Add the blood check button listener. Then let's type blood check button, dot add listener, new change listener price Center. First I'll paste the lines for playing the click sound. Now we simply just need to toggle the blood setting, based on the buttons check state. So I'll comment, Toggle the blood setting, based on the buttons check state. Then let's do so by typing game that settings manager. Do toggle Blood setting. Blood check button is checked. All right, if we put a semicolon here and then run the game, We can now turn off the blood setting, and if we start the game, there will be no blood. And if we turn the setting back on and play the game, the blood returns. Awesome. Alright? The only thing we have left to do now in our game is add the loading screen, which we'll do in the next video. S there. 42. Create the Loading Screen: The loading screen is going to be the first screen we display in the game, and it will basically just load the assets into memory and show the loading progress by displaying a progress bar. When the assets are all finished loading, it will then switch to the main menu screen. Okay, so to begin, let's create the loading screen class by right clicking the screens package here, go to New Java class, typing loading screen, pressing Enter. This needs to implement the screen class, so let's type Implement screen Press Enter to import it. Then let's implement the methods by hovering over this line, choosing implement methods, and clicking Okay. Okay, now at the top of the class, we first want to add some variables. We'll start with objects for the main game class and for the viewports. Let's set private final SFS, precenter then game, and private final viewports, precenter then viewports. Next, we need to create some static final variables to define the progress bar. The progress bar is going to consist of two rectangles, one for the background and one for the progress bar itself, which will grow in width as the assets get loaded. So for the progress bar variables, I'll com it progress bar. Then I'll start with private static final floats, progress bar max width equals 58 F. Then let's do private static final float. Progress bar height equals five F. And for the background, let's do private static final float, progress bar background width. Equals progress bar max width plus F and private static final float, progress bar background height equals progress bar height plus F. All right. And because the assets will get loaded pretty fast, we're also going to add a short 1 second delay between when all of the assets get loaded and switching to the main menu screen. So for this, I'll comment, delay. Let's type private float delay timer. Private bullying, delay started. And private static final float. Delay time equals one F. Okay, we next need to create a constructor for the class, SustPublic loading screen. I will need SFS game as a parameter. Let's go ahead to set this dot game to game. Next, let's set up the viewport using the same extend viewport settings we use in the other screen classes. First dot comment, set up the viewport. Then a side viewport equals new Extend viewport, precenter then global variables, precenter that world width, como global variables, dot Mint world height. Comma global variables world width, comma zero. Next, we want to initialize the delay variables. So I'll comment, initialize the delay variables. The next type delay timer equals delay time. And delay started equals false, since we won't start it until after the assets have finished loading. Finally, we want to start loading the assets from the asset manager. We're currently doing this in the main game class, but we'll be removing it from there in a bit. Okay, I'll comment. Start loading assets from the asset manager. Let's do so by calling game dot assets dot Load. And remember that when we load assets with the asset manager, it actually adds them to a loading queue, or the assets will get loaded into memory as soon as possible. Okay, next, let's go ahead and update the viewport here in the resize method. I'll comment, update the viewport with the new screen size. Then type viewport dot update with height, true. All right, now we just need to do a few things in the render method. First, we'll clear the screen to the same blue color we use in the main menu screen and the setting screen. Subtype screen utils, precenter then clear, global variables, dot blue background. After that, we need to check if the delay has started. So let's type if delay started. If the delay has started, we want to check if the delay time has finished, and if so, we'll call a new method in the main game class called assets loaded. This we'll let the main game class know that the assets have finished loading, so it can then switch to the main menu screen. So I'll comment if the delay has started, check if the delay timer has finished. Then it's do I delay timer less than or equal to zero F. And I'll comment, If the delay timer has finished, tell the game that assets have finished loading. Then it is called game dot assets loaded, which we'll create later. If the delay timer hasn't finished yet, we simply want to decrease the delay timer by Delta. So let's do ts, and I'll comment, decrease the delay timer by Delta time. Theins type delay timer, minus equals Delta. Okay? And if the delay hasn't started, you want to check if the assets have finished loading. If we take a look at the main game class, after we load the assets in the create method, we call the asset managers finished loading method. This method basically freezes the application until all the assets have been loaded into memory. With the loading screen, we don't want to freeze the application because we want to be able to show the loading progress. Therefore, we don't want to call finished loading in the loading screen class. Instead, to check whether the assets have finished loading, we can call the asset managers update method. This will return true if all of the assets have been loaded. Otherwise, it will return false. So back in loading screen, let's put an CIF here. Then game dot assets dot manager dot update. If this returns true, all the assets are loaded, so we want to start the delay. I'll comment, I assets have finished loading, start the delay. Then this type delay started equals true. Now we'll draw the progress bar. One thing we don't want to do in the loading screen class is use assets because, of course, they haven't yet been loaded into memory. So instead, we'll use the shape render to draw rectangles for the progress bar. First, we need to tell the shaped render to use the loading screen viewports camera. So below this I statement, I'll set the shape render to use the viewports camera. Vinis type game, that shape render, that subrojection matrix, viewports dot g camera, dot combined. Now we can start drawing the rectangles. I'll comment, draw the progress bar using the current load progress. Then let's first call game dot shape render, dot begin. We want to use filled shapes. So let's pass in shape type, pre center, then dot filled. Okay, for the background rectangle, we'll use black. So let's set the shape renders color to black by typing game, dot shape render, dot side color, 00, zero, one. Then we can draw the background rectangle centered in the screen by typing game, dot shape render, Direct, viewport dot G rolled width divided by two F minus progress bar, background width. Divided by two F. Come a viewport dot G world height, divided by F minus progress bar, background height, divided by two F. Come a progress bar, background width. Come a progress bar, background height. Next, for the progress bar rectangle, we'll use the gold color that we defined in global variables. So let's type game, dot shape render, D side color, global variables dot gold. Then we can start drawing it by typing game, dot shape render, direct, viewport, do Geollwidth divided by two F minus progress bar max width. Divided by F, viewports dot get road height divided by two F minus progress bar height, divided by two F. Now to calculate the current width of the progress bar, you need to get the current loading progress from the asset manager. To do this, we call the Git progress method, which returns a float 0-1. So to calculate the progress bars width, we can simply do game dot assets dot manager, dot get progress, times progress bar max width. And for the height, we can just use progress bar height. Finally, we need to call game shape render end. And that's it for the loading screen. Now we just need to go back to the main game class and change some things in here. First, we need to create a loading screen object. So at the top in the screens category, let's type public loading screen, price Center, then loading screen. Now on the create method, let's get rid of all of these calls to the asset manager. In their play SleCmt, initialize the loading screen and switch to it. Let's do so by typing loading screen, equals new loading screen, this and set screen loading screen. Next, we need to move all this other stuff to an assets loaded method, which will be called by the loading screen when all of the assets are finished loading. Let's select all of these lines and cut them with Control X. Then below the create method, let's type public void, assets loaded and paste the lines. Okay, let's run the game and see what happens. Alright, we get a progress bar, followed by a short delay. Then it switches to the main menu screen. Excellent. Okay, that's pretty much it for our game. So I'll see you in the conclusion in the next video. 43. Conclusion: Congratulations on completing this course and creating an entire game using LibGDX and Java. We've come a long way over the past few hours going from a simple little demo project to a fully functional game. I hope this course inspires you to continue working with LibGDx. And if you decide to add more to this game or create an entirely new game using what we learned, I would love to check it out. So please send me pictures and videos of the game or the code for the game or even better if you publish a game, be sure to send me a link to it. Thank you again for choosing my course, and I wish you good luck on your game development journey. Take care. 44. Install the Android SDK: Before we can start adding Android functionality to our projects, we first need to install the Android SDK or Android software development kit. Similar to the JDK, the Android SDK is a comprehensive set of tools used to develop applications for the Android platform and we can actually install it directly through IntellaJ. To do so, in the IntellaJ welcome screen, we first want to go to New Project and choose Android here at the bottom left. It would then tell us that in order to create an Android project, we need to have the Android SDK installed, and we can install it by clicking the Install SDK button here. If we don't already have the Android SDK installed, it will tell us that no Android SDK has been found. All right, so we can click next. We then get to choose which Android SDK components we want to install. We can just leave it on the default options. We can also choose where we want to install the SDK. Feel free to leave yours on the default location, but I'm going to click the Browse button here, go to the D Drive. Add a folder with this button and call it Android. Then go inside the Android folder and add another folder called SDK. And with the SDK folder chosen, I'll click Okay. We actually also want to copy this path by selecting it all and pressing Control C because we'll be needing it in the next video. Right now, we can click next, then verify the settings, click next again and accept the license here. And when we click Finish, it will download and install all of the components. Once the Android SDK has finished installing, we can click Finish again. And actually, what we just did didn't download and install the entire Android SDK. It only did a partial install. In order for our LibGDX projects to work correctly, we need to do a full install. To do this, let's first click Cancel here as we don't really need to create a new project at the moment. And now on the welcome screen, let's click customize here on the left, then all settings down here. And if we search for SDK in the search box at the top left, it should find Android SDK in the system settings here. With this chosen, we can now see our Android SDK location on the right. And down here, we can see a list of all available Android SDK versions. The one we just now installed should be the topmost one that has a number for the API level. However, its status might say either not installed or partially installed. To do a full installation, we first need to check the box to the left of it, click Apply down here and click Okay here. And it should start the full installation, which could take a while. Okay, when that's done, we can click Finish. Then click Apply here, and it should now say installed for the status of the SDK version we just installed. Now we can click Okay, and close out of IntellaJ and I'll see you in the next video where we'll use the LibGDXPject generator to create a project with Android functionality. 45. Create & Import a New Project: It's possible to add Android functionality to an existing project that doesn't have it, but I found that it's actually much easier to create an empty LBGDXPject with Android functionality than transfer over the code from the other project. So right now, I have the LbDXPject generator open, and we're going to use it to create a project that's almost exactly the same as our previous SIC figure showdown project, but with Android functionality added to it. Okay, for the project name, I'll go with SIC figure Showdown Android. Now the package name needs to be exactly the same as the other project in order to not cause any problems when we transfer the code. If you don't remember the package name for your project, if you open it up in IntelliJ, you can find the package name inside the source folder of either the core folder or the desktop folder, minus comb grant dot SFS. So back in the project generator, I'll put Com Grant dot SFS for the package name. We also want to use the same main game class. I use SFS for mine. For the output folder, I'll go to my main LBGDXFolder then projects, right click and create a new folder called stick figure Showdown Android. Press Center and click Open. Okay, next, for the Android SDK location, yours is likely not showing the correct location for where you downloaded the Android SDK in the previous video. The only reason it's correct for me is that I used it to create an Android project in the past. If you still have the Android SDK location copied into the clipboard, you can paste it into the box now. If not, if you rewatch the end of the previous video, it will show you how to find the Android SDK location. Okay, next, for supported platforms, let's turn off everything except desktop and Android. And for official extensions, we want to check free type. And now we're ready to click the Generate button. And yes, we want to use the more recent version of the Android Build Tools and the Android API. Okay, so when everything is finished, if you're using the same version of the project generator as I am, it's likely telling you here that the build has failed. The reason for this is that in newer versions of the Android SDK, the names of two files that LibGDX requires have been changed. Now the project generator can't find the files. Hopefully, this will be fixed in a future update. But anyway, to get around the problem, we simply need to change the names of the files back to the original names. To find the files, we need to go into the Android SDK download folder. I'm going to copy the location into my clipboard and I'll leave everything as is in the project generator and minimize it. Then I'll paste the copied path into my file explorer here and presenter. And here we need to go into the Build Tools folder, then into the folder of the highest Android SDK version we have downloaded. And the first file we're looking for is the one called D Eight here. This file was originally called DX, and that's what the project generator is looking for. So we need to change the file's name back to DX. Okay, for the next file, we need to go into the lib folder here, and now we see another file called D Eight. We need to change this one to DX as well. Alright, now, if we bring the project generator back up with all of the settings still the same, let's click the Generate button again and choose, we want to overwrite it. And once it's finished generating, it should say that the Build was successful. Awesome. Now we can close out the project generator, and let's open up and tell a J. From the welcome screen, we can click Open here, then browse to the folder for the project we just created, so stick figure Showdown Android and click Okay. Now if we wait for everything to finish loading, we might get a few messages here, but we can ignore them. One problem we might have, however, is that if we go to build, build project, we get this message about being unable to make filled private, et cetera, et cetera. It took me a while to figure out how to fix this. And to do so, we first need to open up our main project folder here, then double click this gradle Properties file to open it. And here, at the end of the org dot gradle JV Margsine, you need to add a space. Then types, add opens, equals Java dot BSE, fdlashjava dot IO equals, all unnamed, W this part being in all caps. And now we need to click this button on the right that says Load Great Old changes. Okay, when that's finished, if we go to build Build project again, I should say that the Builder was successful. Alright, we can close up the Great Old Dot properties file now, with all of that stuff taken care of. In the next video, we'll learn how we can run the Android version of our project. See 46. Running LibGDX on Android: If we take a look at the project structure, the main difference we'll see between this project and our previous ones is that we now have an Android folder. The Android folder contains all of the files that are necessary for running our application on an Android device. If we take a look inside the source folder here, we can see that we have an Android launcher class. This is basically the Android version of the desktop launcher class in the desktop source folder, and it's required in order to run the Android version of the application. We can open it up if we want. However, we can't run it straight away like we can with the desktop launcher. And just to make sure that the desktop launcher works, let's go into the desktop folder, then source, then right click Desktop launcher and choose run desktop launcher dot Main. Okay? And if it works, we should get the square window with the red background and the bad logic image at the bottom left. All right, we can close this out now. To run the Android launcher, we have two options. First, if we have an Android device like a smartphone, we can plug the device into our computer using a USB cable and install and run the Android version of the application on it. To do this, with the device plugged in, if we go up to the Run configurations up here, where it currently says desktop launcher, we can drop this down and choose Android, and it might then show our Android device here next to it. If it says now devices, it likely just means that we haven't yet enabled developer options on the device. The process for enabling developer options might be slightly different depending on the device. But from what I've seen, most devices use the same process. The first step is to go to the device's settings menu. In the settings menu, we should see an about device or about phone option, which you want to click. Next, we should see either a software information option or a build number option. If we see a software information option, we want to click that, and then we should see a build number option. Okay, so what we want to do next is tap build number seven times. As we do that, it will likely pop up a message saying that we're a certain number of steps away from being a developer. And after we tap it seven times, if we go back to settings, we should now see a developer option setting at the bottom. Now we just need to click that and make sure that the switch at the top is turned on. Right, after all of that is finished, back in intell a J, as long as we have Android chosen for the run configuration and our device is plugged in, it should show the device here next to it. We can now click the Run button here and it will install and run the application on the device. We, of course, won't be able to see the application on the computer, but we should now be able to see it on the device's screen, and it should look mostly the same as a desktole blancher version, except it will be full screen. All right, if you are following along, let's go ahead and close out the application on our device and unplug the device. It should now say no devices next to the Run configuration. So the other way to run the Android version of our application is by using an Android virtual device. Android virtual devices or AVDs simulate particular Android devices, and we can run them on our computer using the Android emulator and IntelliJ. To create an AVD, we use the Android device manager, which we can find either by going to tools, Android Device Manager or by dropping down the box here that says no devices and choosing device manager. Before we continue, inside the Android folder over here, let's open up the built Grado file. What we're mainly concerned with in here is the default config section. This shows us the minimum Android SDK version that our application can run on, as well as the target version. When we create an AVD, we'll have to choose an Android version for the device, and we'll want to choose a version that isn't lower than Mint SDK version or higher than target SDK version. All right, so back in the device manager here, we can click Create Device, and now we can choose the configuration that we want to use for the ABD. First, we can choose the device category, which includes phones and tablets. We can choose a particular device from that category. We can see the name of the device, along with its size, resolution, and screen density, and we get a visual representation of the device's screen here. For this ABD, let's go with the phone category and choose the Nexus five x device. Now if we click Next, we get to choose the Android version for the device. As I mentioned before, we want to use something between the Mint SDK version and the target SDK version. To make things simple, let's just go with what we have for the target SDK version. So for me, I'll choose the one with API level 33 here. Because this is actually a system image and not the same as the Android SDK version that we downloaded earlier, we need to download this as well by clicking the button here next to it with a down arrow icon. Okay, when that finishes downloading and installing, we can click Finish here, and click Next here. Now we can change some settings of the AVD if we want, such as by giving it a different name or making it start up in either portrait mode or landscape mode. We can actually change this easily once we get the AVD up and running, and it will remember the change the next time we run it, so it's not necessary to change it here. All right, so let's go ahead and click Finish. Now, if we don't have a physical Android device plugged in, we should see our new AVD here. If you do have a device plugged in, you could drop down this box and switch between it and the AVD. And by the way, we can create multiple AVDs to test the application on multiple devices. So with the AVD chosen, we can minimize the device manager. Then let's click the Run button. Okay? The Android emulator panel should pop up, and if we give it a few minutes, the AVD should show up in here and the application should automatically be installed and start running. And if we want to change the orientation of the device, we can click these rotation buttons up here. To close out of the application, we can click the Back button here. Now, you might like having the Android emulator show up in this little panel here, but we can actually also make it come up as a standalone application. To do this, let's first close out of the panel by clicking the X here. Now let's go to File settings. And in the search box at the top left, let's type Emulator. Okay, now we can choose the Android emulator setting at the bottom. And here, we have this option that says Launch and a tool window. If you uncheck this, then click Apply and Okay. Now if we click the Run button, the Android emulator will launch as a standalone application. And we can freely move this around. We can close out of the Android emulator if we want, but then until a J, we have to reload it whenever we click the Run button again. So instead, we can simply minimize it. And now if we click Run, we can bring the Android emulator back up, and the application should start up again straight away. Okay, let's go ahead and close out of the application, and in the next video, we'll transfer the code from our SIC figure showdown project into this project. See 47. Transfer the Code: Okay, so what we'll do is we'll replace everything in the assets folder and the core and desktop source folders of this project with the files from the original stick figure showdown project. So first, we want to show everything in the assets folder, as well as the core source folder and the desktop source folder. And now we need to open up the previous project by going to File, and we should be able to find it under recent projects. Here it is. We want to open this one in a new window. And we might need to make the window a bit smaller and move it out of the way so we can see the other one. Okay, first, let's go into the Assets folder of the original project, and we need to close up all of the folders inside. Then we can select the first folder, hold Shift, and click the last folder to select all of the folders, right click and choose Copy. Then let's go into the Assets folder of our new project. Right click it, choose paste, and click Okay here. And we can delete bad logic dot JPG here. Okay, now let's go back to the original project, and then the core source folder. Let's close up all the folders inside the package folder. Then let's select all the folders and the main game class at the bottom and copy them. Then paste them into the same package folder and the new project. Let's click ORight to overwrite the main game class. Finally, in the desktop source folder of the old project, we can copy the desktop launcher class. Then right click the Desktop launcher and the new project, choose paste, then Okay, then overwrite. And now we can close out of the original project. Alright, now, let's go ahead and test everything out to make sure it all works like before. First, we can go up to the run configurations and choose Desktop launcher. Let's click the Run button. Okay, it seems like everything is working correctly. Awesome. Now, let's try it on the Android virtual device we created earlier by clicking the Run configuration box again and choosing Android. Let's make sure we have the Nexus ABD selected and not a physical device just in case we have one plugged in. Okay, if we click Run, we might have to first click Terminate here to stop the previous application. Now if we bring up the Android emulator window and let it finish installing, we can see that the game runs But one thing you'll likely notice straightaway is that there is now a short pause between the repetitions of the music. Other than that, everything seems to be working correctly. Okay, in the next video, we'll fix the super annoying music issue. See. 48. Fix the Audio: As we saw in the previous video, if we run the Android version of the game, we get an issue where the music has a slight delay between each repetition. This is actually caused by a problem between the way Android handles audio and the way the music class in LibGDx loops music. One solution to the problem is to use the sound class instead of the music class for the music. This only works, however, if the music file is small, as the sound class can only handle audio files that are less than 1 megabyte. Fortunately for us, our music file is quite small at only about 9 kilobytes. If the file was over a megabyte, you would have to either find a way to make it smaller or just use music that doesn't have to sound good on loop. Okay, so to switch our music asset to use the sound class instead of the music class, let's first go into the assets class, which is located in the resources package of the core folder. And here, let's go down to the load audio method. And when we load the music asset, let's change music dot class here to sound dot class. I'll press Control A to O to remove the music class input. Next let's go into the audio manager class, which is also located in the resources package. At the top, where we create the music variable, let's change it to private, final sound music, and I'll press Control At O again to remove the music class Import. And now we have a few errors that we need to fix. First, at the bottom of the constructor, there's no set looping method in the sound class. Instead, to put a sound object on loop, we call it loop method. However, this will also immediately start playing the sound on loop, which we don't want to do in the constructor. So let's just remove these lines here. Now let's go down to the nable music method. There's also no is playing method for the sound class. So let's remove this I statement along with this comment. What we're doing here instead is simply start looping the music by calling it loop method. So first I'll comment loop music. Then let's call music do loop. Similarly, in the disabled music method, let's remove these lines at the bottom, and we'll simply just stop the music. So I'll comment, stop music. Then let's call music do stop. Next in the play music method, we again have a call to I playing, which isn't available. So let's remove these lines. And what we'll do instead is check if the music is enabled, and if so, we'll call the music loop method. So I'll comment, I music is enabled, loop it. Finish type if music enabled. Music dot Loop. Next, for the pas music method, we unfortunately can't pause a looping sound and resume it because calling the loop method starts the audio from the beginning. Because our music audio is very short, however, this isn't really a problem. So instead of a pas music method, we're going to use a stop music method and simply stop the music. We don't just want to start typing a new name for the method, though, because we're calling this method from several other classes in the project, and we'll get errors in those classes about the pas music method not being found. Instead, we want to right click the method name, go to refactor, then rename Type Stop music and press Center. Renaming it this way will make it so that all calls to the previous method name now called the new method name. Now we can remove these lines, and we'll just stop the music if it's enabled. So I'll comment. If music is enabled, stop it. Then type I music enabled. Music dot sop. That's it. Now if we run the game on the Android emulator, you can hear that the music plays in a perfect loop again. One problem we do have now, however, is that if we go to the settings, another instance of the music starts playing on top of the current one. This is because if we go into the setting screen class, which is located inside the screens package, and the show method of this class, we have it, so if the music setting is on, it calls the music Toggle buttons set checked method. The set checked method actually triggers the buttons change listener. And in the music toggle buttons change listener, we have it. So if the music setting is on, it calls the audio Manager's Enable music method. Now, if we go back to the audio manager class, remember that in the enabled music method, we call music dot Loop. The problem with this is that when we call either the play method or the loop method on a sound object, it actually starts a new instance of the object's audio asset. So every time music D Loop gets called, if the previous instances haven't been stopped, we'll get another instance of the music looping on top of them. To fix this, we can go up to the top of the class. And in the music variable section, we can add a private Boolean music playing and initialize it to false. Now and enable music, we only need to perform any of this code if the music isn't currently playing. So at the top, I'll comment, I music is already playing, don't do anything. Then this type, I music playing, return. After calling music Dup here, we want to set music playing to true. Next in disabled music, after stopping the music, we can simply set music playing to false. Next on the play music method, we also only want to perform this code if the music isn't already playing. So I'll copy the lines for checking this in the Naval music method. Then paste them at the top of the play music method. And after calling music D up, you want to set music playing to true. Finally, in the stop music method, after stopping the music here, we can add music playing equals false. Okay, now if we run the game on the emulator again, We might get a problem with the music not starting at all, but if we start the game and go back to the main menu, the music starts. I've actually found this to only happen on the Android emulator. I've tried it on several physical Android devices, and the problem didn't occur. Anyways, it seems that the cause of it is that the music asset doesn't get completely loaded into memory on the emulator before the main menu screen comes up the first time, so it isn't able to play the music. To fix this, we can try going to the assets class and then the load method. We want to make it so that the load audio method gets called before all of the other methods. This will make it more likely that all of the audio assets get loaded in time. If we try running the game now, the music might start working when the main menu comes up. If not, another thing we can try is increasing the delay time between the loading screen and the main menu screen. To do this, let's open up the loading screen class. Let's change the delay time variable here to something like F. Now if we run the game, it should work correctly. But like I said, it seems like this problem doesn't occur on actual physical Android devices, so we could set the delay time back to one if we wanted to, but I'll leave mine on two. Alright and now if we go to the settings, we can see that it doesn't cause another instance of the music to start. So we've taken care of that problem as well. However, we actually have one more audio problem, which is that sometimes the game will stutter when playing multiple sounds at once. I found this issue to be more noticeable on physical devices than on the emulator. And the reason for the issue is that in Android, the sounds play synchronously on the game loop, forcing the rendering of the game to get paused at times while waiting for a sound to get processed. The solution to this problem is to play the sounds asynchronously. To do this, let's go to the Android launcher class. And here we need to override the create audio method. So first, let's go down a few lines and start typing create Audio. And when it shows up, we can press Enter to override it. If we want to replace this return line with return new asynchronous Android audio, press Enter to import it, then context comic and fig. Now if we run the game, it should work like normal. But if we were to play it on a physical device, we shouldn't notice any more stuttering. Okay, now if we start the game, we can still move around using the arrow keys. But if we press, for example, the F key to punch, it brings up the onscreen keyboard. This isn't good. And anyway, with mobile devices, we want to be able to do everything by touching the screen. So we'll be putting some buttons on the screen, both for moving our fighter and for attacking and blocking. But first, if we go back to the setting screen, there are a couple of things we want to change in here, and we'll do so in the next video. See. 49. Change the Settings Screen: Before we change the setting screen, if we run the game on a physical Android device, you'll notice that on the right side of the screen is showing the navigation buttons. If we run it on the emulator, this might show up as a small bar at the bottom of the screen. For the most part, mobile games will hide all of this so that it doesn't get in the player's way. To do this in our game, we can go into the Android launcher class, and after we initialize the config variable, we can type Config, DU and Mersv mode equals true. Now if we run it, the navigation buttons or bar will be hidden. Okay. Now if we go into the settings, toggling on and off the full screen setting here doesn't actually do anything. In Android, the game will remain in full screen. Therefore, we can get rid of this full screen box here altogether and replace it with a wider version of the blood box. If we go into the textures assets folder and open menu items dot PNG, I actually created a wider Show blood texture here, specifically for mobile versions of the game. All right, so let's go to the setting screen class. First, in the create Images method, When we create the blood setting background image, we want to first check whether the game is being run on an Android device, and if so, we'll use the longer version of the texture, which has the region name of Blood setting background Long and the atlas. If the game isn't being run on an Android device, we'll just use the normal version of the texture. Okay, so to check if the game is being run on an Android device, we can type a GDX dot app dot Get Type is equal to application type. Press Ender to import it. Then if we put a dot, we get a list of all the available application types, including Android, desktop, IOS, and WebGL. We just want to check if it's Android, so let's choose it here. And if we are using Android, let's blood setting background image to new image, menu items Atlas, DfineRgion Blood setting background long. Now we want to put an outs here. Then select this whole line for initializing the image. Cut it with Control X and paste it into the outs part. Alright, now let's go down to the crate tables method. Down here where we add the four screen table to the bottom table, we only want to do so if the application type isn't Android. So first, I'll add to the comment here, if not using Android, add the four screen table to the bottom table. Then we can cut this line out with Control X and type Fgdx dot app dot Get Type. It is not equal to application dot application type dot Android. Then paste the line back in. Okay, now, if we run the game on Android and go to the setting screen, We just get the long show blood image at the bottom with no four screen option. Alright, and if we run the desktop launcher and go to the settings, It's the same as it was before. Okay, in the next video, we'll start putting the fighter control buttons on the screen. See you there. 50. Fighter Controls 1: Set Up & Draw the Sprites: Start adding the mobile UI to the game, we first need to go to the assets class and load the mobile UI atlas into the asset manager. If we go into the textures Assets folder, we have a mobile it Atlas file and a mobileitPnG file. If we open the PNG file, we have textures for each of the possible directions for the joystick, which the player will use to move their fighter, and we have textures for a block button, a kick button, and a punch button. Okay, so back in the assets class, we first need to add a variable that points to the location of the mobile UI atlas. This is a gameplay asset, so we can add the variable to the bottom of the gameplay assets section here by typing public static final string, Mobile UI Atlas equals textures forward slash mobile dot AtlaS. Next, we can go down to the Load Game Play assets method. And at the bottom, we can load the mobile UI atlas by typing manager dot Load, mobile UI Atlas, coma texture Atlas dot class. Okay, now we need to go to the game screen class and add some variables for the mobile UI at the top. So after the blood section here, I'll add a new section for mobile UI. Okay, so first, we'll set a margin for how much space to put between the UI buttons and the sides of the screen. For this, let's type private static final float, mobile UI margin. Let's set it to 1.5 F or 1.5 world units. Next, we'll need a texture atlas object for getting the mobile UI atlas from the asset manager. For this, let's type private texture atlas, Mobile UI atlas. Next, we'll create some Sprite objects for each of the mobile UI elements. Let's start with the punch button by typing private Sprite, Punch button Sprite. Then let's do private Sprite, kick Button Sprite, then private Sprite, Block button Sprite. And finally, for the joystick, private sprite, joystick sprite. Okay. Now for the joystick, we actually need a few more variables. First, for convenience, we'll have a vector two object pointing to the center of the joystick. For this, let's type private final vector two, Joystick center equals new vector two. We also need a vector two for indicating the current direction of the joystick. Let's type private final vector two, Joystick direction equals new vector two. This will be similar to the movement direction variable we created in the fighter class. Zero will indicate no movement. Negative one will indicate left for X and down for Y, and one will indicate right for X and up for Y. Okay, finally, we need a variable to define the joysticks drag threshold. This is basically the sensitivity of the joystick. The lower the value, the less the player has to drag their finger on the joystick in order to change the direction. And the higher the value, the further they have to drag it. For this, let's do private static final float, Joystick drag threshold. Equals, and I found that a good value for this is one F, we can always try other values later. Okay? And because we want to let the player use two fingers on the screen at a time, one for moving their fighter with the joystick, and one for making their fighter attack or block, we need to keep track of the fingers or pointers on the screen. And we also want to know which action each pointer is performing. That sounds complicated, but it's not too bad because LibGDX actually takes care of most of the pointer tracking for us. Basically, we just need to keep track of the actions, and the three possible actions a pointer can have in our game are move, attack, or block. So let's create an Enum for these by typing private Enum, pointer action. Move attack, block. And now we just need to create an array of pointer actions to hold the action of each pointer on the screen. In some games, the player is able to use many pointers at once. But in our game, we only care about two pointers, one for using the joystick and one for clicking one of the attack or block buttons. This is because the player can only perform a single attack or block action at one time. So for the array, let's type private final pointer action brackets, pointer actions equals new pointer action. Open bracket and give it a size of two for two possible pointers. Okay, that's it for the variables. Now we need to create the mobile UI sprites. We'll do this in a method called create mobile UI, which we'll call from the constructor. So first, let's go to the constructor. And after calling the create Blood method down here, I'll comment, create the mobile UI. Then let's call create mobile UI. Then let's go down here below the create blood method. And create the new method by typing private void, create mobile UI. The first thing we need to do in here is get the mobile UI Atlas from the asset manager. So I'll comment, get the mobile UI texture Atlas from the asset manager. And let's type mobile UI Atlas equals game dot assets dot manager dot Git assets dot Mobile UI Atlas. Okay, next, let's create the punch button sprite. I'll comment, create the punch button. Then I'll start by typing punch button, Sprite equals new Sprite. Mobile UI atlas that Fine region. And the region name for the punch button is Punch button. Now we need to scale the size of the sprite by the world scale by typing punch button sprite, that size, punch button sprite do get width times Glibbo variables, that world scale. Come a punch button sprite. Dog height times quibble variables that world scale. Next for the kick button, I'll copy and paste these lines. Change the comet to create the kick button. Change the texture region to kick button, and change all of this to use the kick button sprite. Now I'll do the same for the block button. For the joystick, I'll copy and paste the lines again. Change this to create the joystick. Change the region name to joystick and change all of these to joystick sprite. Some of the things we want to do in here for the joystick is go ahead and set the joystick sprites position, since it will always be located at the bottom left of the screen with its X and Y coordinates both set to the mobile UI margin variable. We also want to set the Joystick center vector two object to the location of the Sprite center when displayed on the screen. And we want to set the Joystick direction vector two object to 00, which will mean that the joystick is not being dragged by the player. Okay, so to set the position of the sprite, let's type Joystick Sprite, dot set position. Mobile UI margin come a mobile UI margin. Next for the center, let's type Joystick center dot set, Joystick Sprite, dot get X, plus Joystickprt, get W divided by F, joystick Sprite, I'll get Y, plus Joystick sprite. I'll get height, divided by F. Finally, for the direction, let's do Joystick direction. Set, zero comma zero. Okay, now let's get these sprites rendered to the screen. First, let's go to the render method. After we call the Render Pause button method here, we'll call it render mobile UI method. We'll make it so the method only draws the mobile UI sprites if we're using Android. So I'll comment here, draw the mobile UI if using Android. Then let's call Render mobile UI. Now let's go down here below the render Pause button method. Let's create the new method by typing private void render mobile UI. First, if the application type is an Android, we don't want to draw anything. So I'll comment, if not using Android, don't draw anything. Then this type of gdx dot app dot GEDIpe is not equal to application type, preenter then Android, return. Okay? The first thing we'll draw is the Joystick Sprite. So I'll comment draw the joystick. And because we already set its position, we can just call Joystick Sprite dot draw passingam dot batch. Now if we run the Android version of the game and start the game, M we get the joy stick at the bottom left. However, we have a problem. We want to put the attack and block buttons here on the right where the pause button currently is, and we want to move the pause button to the bottom center. So let's go up to the render Pause button method really quick. And here, for using Android, we want to set the position of the sprite to the bottom center of the viewport. So at the top of the method, let's type Igdx dot app dot GETPEs equal to application dot application type dot Android. Pause button Sprite, dot supposition, viewport dot G Rolled Width, divided by F minus Pause button Sprite dot get Width, divided by F. And for the Y position, we'll just use Pause button margin. Now we can put notes here. Then cut this supposition line here and paste it into the outs part. Now if we start the Android version of the game, We get the pause button in the center. And just to make sure we can start the desktop version. And the pause button is still in the original location and no joystick. Cool. Okay, back in the render mobile UI method, we'll draw the buttons. First I'll comment, draw the buttons. Let's first set a variable for the spacing between the buttons. For this, let's type float button spacing equals 0.6 F, which we can change later if necessary. And we're going to draw the buttons in kind of a triangular shape with the punch button at the left, the kick button at the right, and the block button at the top center. We'll start with drawing the kick button at the right because we'll use its position to help us set the positions of the other buttons. To get the position of the kick button, we can take the width of the viewport and subtract both the mobile UI margin and the width of the kick button. And the Y position will simply be the mobile UI margin. Okay, so let's set this position by typing kick button Sprite, that set position. Viewport, get rolled width, minus mobile UI immersion, minus kickbton Sprite, do get width, coma mobile UI immersion. Then we can draw the button by typing Kick button Sprite, dot draw game dot batch. Next, to get the exposition of the punch button, we'll take the exposition of the kick button, subtract the button spacing, and subtract the width of the punch button. And as Y position will also be the mobile UI immersion. So let's type punch button Sprite dots position, Kickbton Sprite, dot get X minus button spacing, minus punch button Sprite dot get width. Come on mobile UI immersion. Let's draw it by calling. Punch button spray do draw game dot batch. Finally, for the block button to get the position, we'll take the kick buttons exposition, subtract half of the button spacing, then subtract half of the block buttons width. And for its Y position, we'll take the height of the block button, add the mobile UI margin to it, then subtract the button spacing. That sounds confusing, but it works out well. So let's type block button, sprite, do supposition, kick button, sprite, do get X, minus button spacing, divided by F minus block button sprite, do get width, divided by F, come a block button sprite I'll get height, plus mobile UI Immersion, minus button spacing. Then draw by calling Block Button Sprite, D draw game do Batch. Right now, if we start with the Android version of the game. We get the joystick on the left and the buttons on the right. Awesome. In the next video, we'll start to get it all working. See you. 51. Fighter Controls 2: Attacking & Blocking: Our mobile UI working, let's first set down to the touchdown method of the game screen class. One of the parameters we have in this method is pointer here, which is an integer. This indicates which of the user's fingers or pointers touched the screen when the touchdown method was called. LibGDX keeps track of how many pointers are on the screen at one time and also keeps track of the location of each pointer. And when a pointer first touches the screen, it gets an index, which is given in the pointer parameter. If the user has no pointers on the screen, then they touch the screen with a pointer. That pointer gets an index of zero, and as long as that pointer remains on the screen, it will continue to have an index of zero. If they keep pointer zero on the screen and touch the screen with another pointer, that pointer gets an index of one, and the next one gets a two and so on. However, if they release one of their pointers, say pointer zero, then touch the screen with either the same pointer again or a new pointer, LibGDx will give it the smallest available index. So because they released pointer zero earlier, the new pointer will get an index of zero, even if pointers one, two, et cetera, are still on the screen. Because of this and because we only need to handle two pointers in the game at once, the only pointer indexes that matter to us, our index is zero and one. Before we check if one of the UI sprites has been pressed, we first need to make sure the pointer parameter is either zero or one, or in other words, less than the length of our pointer actions array, which we set to two. Also, we only need to check if the UI sprites are being touched when the game is in the running state. So we'll add some more Asif blocks to the If statement here. Let's start with the attack and block buttons, as they will be easier than the joystick. First, for the punch button sprite, after checking if the pause button has been pressed, let's add an Osif here. We'll start by making sure pointer is less than the length of the pointer actions array by typing pointer, less than pointer actions dot length. Now we also want to check if the converted click position here is inside the punch button Sprites bounding rectangle. Let's type and punch button Sprite Git bounding rectangle, that contains position dot X come position dot Y. Now here, we want to make the players fight or punch, and we want to set the pointer's action to attack and the pointer actions array. So first of all, comment this out. If the pointer is included in the pointer actions array, and the punch button has been touched, make players fight or punch and set the pointer's action to attack. Now we can call game dot player dot punch. Then do pointer actions. Brackets pointer equals pointer action dot attack. Next for the kick button, let's add an SIF below this. Then type pointer, less than pointer actions, dot length, and Kickbton Sprite Get bounding rectangle. That contains position dot X, come on position dot Y. Okay, I'm going to just copy and paste all the lines from inside the punch button part. Change this to and the kick button has been touched. Make player Spider kick. Now change this to game dot player dot kick. Okay, for the block button, let's add another SIF. Then type pointer, less than pointer actions, dot length, and Block button Sprite get bounding rectangle. Do contains position dot X. Come up position dot Y. I'll paste the lines again. Change this to block button and make players fight or block. And for blocking, we want to set the pointers action to block. Now, here we'll call game dot player do block. And here we'll do pointer actions pointer equals pointer action do block. Okay, if we start with the Android version of the game now, We can press the punch button to punch, the kick button to kick, and the block button to block. Whoever once we block, it doesn't stop blocking. To fix this, let's go down to the touch up method. Like in the touchdown method, we have a pointer parameter. In touch up, pointer refers to the index of the pointer that has been released from the screen. So in order to make the player stop blocking, we want to check if pointer is inside the pointer actions array and that its action is set to block. This means that the pointer that pressed the block button has been released, so we need to stop blocking. Now you might be wondering why we don't just check if the pointer was inside the block button sprite when released. The reason is that if the player presses the block button, drags their finger outside of the button, then releases the finger, the release position won't be located inside the block buttons bounding rectangle. Okay, so first, we're just going to check if the pointer is inside the pointer actions array. So I'll comment. Check if the pointer is included in the pointer actions array. Then let's type if Pointer, less than pointer actions dot length. Next, we want to check if the pointer's action is block, and if so, we'll call the player fighter stop blocking method. So let's type if Pointer actions, brackets pointer is equal to pointer action dot block. I'll comment. If the pointer cause player's pinter to block, stop blocking. Then it's call game dot player do stop blocking. Next, outside of this inner if statement, we want to set the pointer's action to null to indicate that it no longer has an action. So I'll comment the pointer no longer has an action. Then it's type pointer actions, brackets pointer equals null. We're actually going to add an Asif part in here for the joystick later, which is why we didn't just make this one big if statement. Can let's not forget to return true down here to let it know we handled the event ourselves. Now if we start the game, we can press the blocked button to block, then release it to stop blocking. And if we press the button, then drag the cursor up here and release, it still works. Okay? In the next video, we'll get the joystick working. See. 52. Fighter Controls 3: Joystick Movement: The joystick, let's first go back up here to the touchdown method. And after checking the block button here, let's set an Sif. Then type pointer, less than pointer actions dot length, and joystick sprite, Dot Get bounding rectangle. That contains position dot X, come on position dot Y. And here we simply need to set the pointer's action to move. So I'll comment if the pointer is included in the pointer actions array and the joystick has been touched, set the pointers action to move. I then let's type pointer actions, brackets pointer equals pointer action move. The main code of the joystick will be going down here in the touch drag method. This method gets called whenever the user drags a pointer across the screen, and like with touchdown and touch up, lets us know the index of the pointer. Screen ex and screen Y here refer to the current position of the pointer. And the first thing we need to do is convert these into world coordinates like we did in the touchdown method. So first, let's copy these lines from the touchdown method and paste them at the top of the touch drag method. Let's go ahead and change this line to return true. Okay, before returning true, we'll do a few checks. First, we want to make sure that the round is in progress because we don't want the player to be able to move during the start and end round delays. And we also want to check that the pointer is inside the pointer actions of array and that its action is set to move. So first, I'll comment all of this out. Check that the round is in progress. The pointer is included in the pointer actions array, and the pointer's action is move. Then let's do all of this in a NIF statement by typing I round state is equal to roundstate.in progress. And pointer, less than pointer actions dot length, and pointer actions brackets pointer equals pointer action dot MOV. If all of this is true, we want to check if the Joysticks drag threshold has been passed and that its direction has changed. I'll comment, check if the Joysticks drag threshold has been passed and its direction has changed. Let's first create a Boolean variable to indicate whether the Joysticks direction has changed by typing Boolean, direction changed. Let's set it to false by default. So first, we'll compare the pointer's position to the center of the joystick, and if the pointer has moved away from the joystick center by at least the Joysticks drag threshold amount, this means that the Joysticks drag threshold has been passed. Okay, let's start by comparing the X coordinates by typing if position dot X greater than or equal to Joystick center dot X plus Joystick drag threshold. If this is the case, the platter is moving the joystick to the right, so we want to set the joysticks X direction to one. However, we only want to do so if the joysticks X direction isn't already set to one. This is because when the joystick changes direction, we need to change its texture to show the correct direction. And if we keep setting the texture when it isn't necessary to do so, it will just be a waste of CPU power. Okay, so let's check this by typing if Joystick direction X not equal to one. That will change the joysticks X direction to one or right. So I'll comment, change the joysticks X direction to right. Then let's type joystick direction, dot X equals one. And we also want to set direction change to true. This will let us know that we need to move the player's fighter in a different direction, as well as change the joystick sprites texture. Okay, now we need to do all of this for the opposite direction or left. So let's type OsifPosition, dot x, less than or equal to Joystick center dot x minus joystick drag threshold. Then if Joystick direction dot X is not equal to negative one, and I'll comment, change Joy six x direction to left. Then let's type joystick direction dot X equals negative one, and direction changed equals true. Okay, when you actually also need another Osifblock here for when the pointers X coordinate hasn't passed the Joysticks drag threshold. This is because the player might have dragged the joystick to the left or right, then back to the center. In which case, we need to change the joysticks direction to zero. So to do this, let's type Osif Joystick direction dot X is not equal to zero. And I'll com it. Change the joysticks X direction to center. Then let's type joystick direction dot X equals zero, and direction changed equals true. All right, we next need to compare the Y coordinates. Because we want the player to be able to move both horizontally and vertically, we'll do this in a separate I statement. So below this IF statement here, let's type if position dot Y, greater than or equal to joystick center dot Y plus joystick drag threshold. If joystick direction that Y, not equal to one. And I'll comment, change the joysticks Y direction to up. Then let's type joystick direction, that Y equals one, and direction changed equals true. Next, let's do O sieve position dot Y. Less center or equal to joystick center, do Y minus joystick drag threshold. If joystick direction, do Y, not equal to negative one, and I'll comment, change the joysticks Y direction to down. Then it's type joystick direction do Y equals negative one. And direction changed equals true. Finally, let's do out Sif. Joystick direction, that Y, not equal to zero. And I'll come it. Change joysticks Y direction to centered. Then let's do joystick direction, that Y equals zero, and direction changed equals true. Okay, after doing all of these comparisons, we'll check if the Joysticks direction has changed, and if so, we'll move the player's fighter in the new direction and change the joystick sprites texture to the correct texture. So first, below these two nerf statements, let's type if direction changed. And I'll comment, if the Joysticks direction has changed, move players fighter in the new direction. Let's start with the horizontal movement by typing I Joystick direction dot X is equal to one, game dot player dot Move Right. Elsiv Joystick direction dot X is equal to negative one. Game dot player dot move left Els. This means that the X direction is zero, so we want to make the player stop moving horizontally by calling game dot player, do stop moving right, and game dot player do stop moving left. Okay, under this IF statement, let's start a new one for the Y direction by typing. If Joystick direction dot Y is equal to one, game dot player dot move up. El SIF Joystick direction dot Y is equal to negative one. Game dot player dot move down. Els game dot player dot stop moving up. Game dot player dot Stop Moving Down. Now below these two F statements, we'll change the joysticks texture. So I'll first comment here, if the Joysticks direction has changed, set the joysticks texture to the texture for the new direction. Now if we check out the mobile ui Atlas vile, the way I name the texture regions for the joystick is I put the word joystick followed by the X direction, so left or right, followed by the Y direction, up or down. And the default centered region is just called joystick. And we also just have joystick down, joystick left, et cetera, for moving either only horizontally or only vertically. Okay? So to get the correct region named for the joystick, we can first create a string variable here called Joystick region and by default, set it to joystick with a J. Next, we can check if the Joysticks X direction is either a one or a negative one, and if so, we can add either the word right or the word left respectively to the joystick region string. So to do this, this type, if Joystick direction dot X, is equal to one. Joystick region, plus equals, right. Else CIF, joystick direction X is equal to negative one, Joystick Region, plus equals left. If the Joysticks X direction is zero, we don't want to add anything to the region name. Now we can do the same with the Y direction in a new F statement by typing, I Joystick direction dot Y is equal to one, Joystick region, plus equals quote up El CIF, joystick direction Y is equal to negative one, Joystick region, plus equals quote down. Finally, below these two I statements, we can set the Joystick Sprites new texture region using the Joystick region string by typing Joystick Sprite, dot set region, mobile UI atlas dot Fine Region. Joystick Region. And before we try this out, we need to do a couple of things with the joystick and the touch up method. So after we check if the pointer's action is set to block here, let's set an Sif and check if the pointers action is move by typing pointer actions, Becket's pointer is equal to pointer action dot move. And if so, the first thing we want to do is make the player's fighter stop moving completely. So I'll comment. If the pointer cause players fighter to move, stop moving. Then it's called game dot player dot Stop moving right. Game dot player do stop moving left. Game dot player dot stop moving up. And game dot player stop moving down. Next, we want to set the Joysticks direction to 00 and set the Sprites texture region to the default. First I'll comment all this out. Reset the Joysticks direction and it's texture to the default. Finish type Joystick direction, dot set 00 and Joystick Sprite dot set region. Mobile UI atlas Dt Fine Region. The default region name is Joystick with a J. C now if we start up the game, We can use the joystick to move around, and the texture of the joystick shows us which direction we're moving. And if we release the mouse, we stop moving, and the joystick texture goes back to the default. Now, unfortunately, unless we're using a computer with a touchscreen, we can't check in the emulator that we can use two different pointers for the joystick and the buttons. If we run the game on a physical Android device, however, we can see that it all works correctly. We do have a slight problem, though. If we drag the joystick so that its texture changes, and while still keeping our finger on the screen, if we press the pause button with a different finger and go to the main menu, then release the joystick finger. If we now start the game, the joystickprt still uses the previous texture. To fix this, we need to go into the show method. And here, before calling the Start game method, we need to check if the joystick isn't centered, and if so, we'll reset its direction and its texture. So first, I'll comment, if the joystick isn't centered, reset its direction and texture. Finnis type, I Joystick direction dot X is not equal to zero or joystick direction dot Y is not equal to zero. Joystick direction, do set, zero comma zero, Joystick Sprite D set region, mobile UI atlas DFineRgion Joystick. And one more thing we need to do in this show method is set all the actions in the pointer actions array to null. So after this I statement, I'll comment, set no action for all the pointers in the pointer actions array. And to do this easily, we can call the fill static method of the arrays class by typing arrays, pressing Enter to import it, then dot fill. We need to pass in the array, so pointer actions, followed by what value we want to fill the array with, so no. Okay now if we run the game on a physical Android device and go back to the main menu while holding the joystick off center, when we start the game again, the joystick starts back at the center. Perfect. All right, so that's how we can add Android functionality to a game in LibGDX. Thank you very much for watching.