You are on page 1of 118

THIS IS WHERE THE FUN BEGINS.

All the hard work. The hours of dedication. The years of dreaming. It culminates here. Welcome to Unit 2. By the time you walk away from this Unit, you will have made a fully playable game. Before we get into the actual coding, let's talk about logistics. 1. You will be making a Java based applet. The purpose of this is to make it easy to embed into HTML, so you will be seeing live examples on this page. 2. Your voice will matter. Use the comments box below. We will be adding nonessential features as you guys deem necessary. 3. If you have not followed Unit 1, we will be using Eclipse throughout this series. So I suggest that you go read the first few lessons in the first Unit to get yourself ready to develop. In the past two days, many of you (nearly a hundred) participated in the survey that was available on this page, and I read each response. Thank you so much for your feedback. Game Idea: The most popular game idea was a platforming shooter. So that is the direction we will be headed. Like I said before, YOU decide where we will be at the end of the unit. I will simply lead you there. Changes: The biggest complaint was that the lessons were too far apart. I will be changing that this Unit. Expect more frequent updates (most likely 2 lessons a week). I hope to finish this Unit in about a month. In addition, thanks to donations from users like you, we were able to upgrade our website with premium features. So, whenever I use a game asset (graphics, sound, etc), I will be posting them here for you to download and incorporate into your own project. You don't need to start up Photoshop and throw together artwork for this Unit. Of course to maintain this, Kilobolt has to pay a monthly fee. So if you donate any amount (yes even a dollar) it will help us retain this service and bring more exciting features to you in the coming lessons. In addition we are offering an advertising service. Kilobolt.com, although a very new website, receives significant traffic from very dedicated users. If you want to promote your business, contact us. We are offering two weeks of free advertising to qualifying partners.

Donate Here or Advertise Here.


Enough chit chat. Let's begin.

Lesson #2-1: Setting up a Project Open up Eclipse and create a Java project. You can do this by right clicking on the Package Explorer >> New >> Java Project:

Click to Enlarge

Call it KiloboltGame (consistency will ensure that we won't have errors due to naming).

Inside this project's src folder (remember this is where we store all our code): 1. Create a Package. Name it kiloboltgame. By convention its the package name begins lowercase. Packages are ways of organizing classes into folder-like structures. Right click on your newly created package and create a class: StartingClass.

KiloboltGame Project containing a kiloboltgame Package containing a StartingClass class.

Now we can start coding. Lesson #2-2: Adding Methods and extending Applet

By this time, I trust that you are all experienced Java programmers capable of discerning which brace corresponds with which.

As such I will no longer be color coding unless I believe it is absolutely necessary. 1. Add extends Applet after "StartingClass". Recall that we can borrow methods from a superclass by inheriting it. 2. You will see a red line under Applet. Import Applet by pressing Ctrl+Shift+O or by manually selecting the import by mouseover. 3. You can now add the following methods: init(), start(), stop(), destroy(). These are part of the Applet superclass, and by importing Applet, you gain the ability to use them. 4. An easy way to add a pre-defined method (as in one that is already written for you), is to use the auto-complete function. Begin typing "init" and then press Ctrl + Space. It will open an auto-complete suggestions box, from which you can choose the first init() function. Proceed to do this with all four methods and you will see this:

5. Press Ctrl + Shift + F to auto-organize your code. Shortcuts such as these will help you save a lot of time.

FIGURE 2-1
package kiloboltgame; import java.applet.Applet;

public class StartingClass extends Applet{ @Override public void init() { // TODO Auto-generated method stub super.init(); } @Override public void start() { //TODO Auto-generated method stub super.start(); } @Override public void stop() { // TODO Auto-generated method stub super.stop(); } @Override public void destroy() { // TODO Auto-generated method stub super.destroy(); } } Now, as always, we will talk about each of these methods. The four methods: init(), start(), stop(), and destroy() are frameworks for execution provided by the Applet class. In this Applet's life cycle, major events will call one of these methods and execute it. The @Override tests for errors upon compilation. In this case, we are using it to annotate that we are overriding methods from a parent class. It informs you when you make mistakes. More information on this can be found on Google. But for now, don't worry about it. :) Within each method, you will see a "super." This "super" is referring to the superclass (in this case Applet). You might also see a "this" in the future, which will refer to the current class. Don't worry about this for now. We can safely delete each "super" line:

FIGURE 2-2: FINAL CODE:


package kiloboltgame; import java.applet.Applet; public class StartingClass extends Applet{

@Override public void init() { // TODO Auto-generated method stub } @Override public void start() { //TODO Auto-generated method stub } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } } Before we move on! Mini Quiz: 1. How can you make a program keep running? How do you repeat statements? 2. What tool do we use for simultaneous processes in Java? 3. What is a game loop?

Answer these questions and check below!

Answers: 1. We utilize a loop. 2. A thread. 3. A game loop is the central component of a game. People refer to it as the heartbeat of the game. To put simply, it is a loop that will continuously check for changes in the game and make necessary updates. Graphics, controls, movement, physics all rely on the game loop in some way. Here's a fake game loop that will help you understand:

FIGURE 2-3
while (playerIsAlive){ updatePosition(); drawCharacterAtCurrentLocation(); applyGravity(); } Figure 2-3 incorporates three pseudo-methods (they are simply there for illustration).

We have a condition that while the playerIsAlive is true, we update his position, redraw his character at that new position, and apply gravity. This is a simplified example of a game loop. As we get into more advanced gaming concepts, we will talk about creating a proper game loop. But for now, a simple one will have to suffice (And on modern computers, a little bit of slacking here won't hurt us). But wait until we start developing on Android. Technical considerations can be PAINFUL. Day 1 will stop here. Day 2 will be up in an hour or two. In Day 2, we will talk about applying threads and loops to create the heartbeat of our upcoming game. Be excited. Oh and also, please post a good game name as a comment down below. :) We will see what kind of names come up. Thank you for being an awesome audience, and please share our page on Facebook. Help us reach 1,000 likes by November! I've been thinking about the direction that I want to go with this game, and as of now, I am considering making it a game about a Q-bot (cube robot)* going around shooting things and jumping across platforms. Two games that I am taking inspiration from are Metal Slug and Megaman. As we get deeper into the series, I want to add character upgrades, different types of platforms and power ups, and much more. But first, let us talk about the game thread and the game loop. (*Name not final)

Lesson #2-3: Implementing a Thread:


As mentioned in the previous unit, threads are a way of running simultaneous processes. We will be adding a thread to our code like so:

FIGURE 2-4: START() IN STARTINGCLASS.JAVA


@Override public void start() { Thread thread = new Thread(); thread.start(); }

Simple right? The first line creates a new instance of a thread (a Thread object called "thread"), and the second line starts the thread. When you call: thread.start(); , however, nothing will happen. Why? Refer to this example from Lesson #1-21:

FIGURE 2-5: EXAMPLE OF A THREAD


Thread thread = new Thread(){ public void run () { for (int i = 0; i < 10; i += 2){ System.out.println("hello"); } } }; Threads require a run method that determines what will happen in that thread. In Figure 24 we never declared a run method. There's a reason for this. We will be extracting the run method from the thread and creating a separate method in the class by doing the following: 1. Implement the Runnable interface by adding: "implements Runnable" to the class declaration like so: public class StartingClass extends Applet implements Runnable{... 2. Create a run() method in the class by typing out run and then pressing Ctrl + Space and selecting the first option: run() : void. It will auto complete the method for you. When you try to create a run method, it will give you the following information:

This tool tip describes that when we implement interface, we can extract the run method from the Thread that we created. So to "bridge" or connect this newly created run() method and the thread that we have created above: 3. Go back to this statement in the start method: Thread thread = new Thread(); and add "this": Thread thread = new Thread(this); We will talk more about "this" in the future. It is quite a flexible keyword, and therefore I think it's best if we talk about it from another perspective with more understanding. The final result:

FIGURE 2-6: STARTINGCLASS.JAVA


package kiloboltgame; import java.applet.Applet;

public class StartingClass extends Applet implements Runnable{ @Override public void init() {

} @Override public void start() { Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { // TODO Auto-generated method stub } }

Lesson #2-4: Creating the infinite game loop:


In this lesson, we will be creating a game loop. I mentioned in Day 1 that a game loop is the heartbeat of the game. It constantly checks for changes and makes updates to the game accordingly. More sophisticated game loops will be used in our later games, but for our purposes, all our game loop needs to do is the following: 1. Update characters 2. Update environment 3. Repaint the scene. 4. Sleep for 17 milliseconds. Why 17 milliseconds you ask? I'll tell you why. I'm sure many of you know that when you go on YouTube and watch the Epic Rap Battles or something, you are seeing a sequence of still images that are changing fast enough to give your brain the illusion of fluid motion. The number of times that the image changes in one second is referred to as FPS or frames per second (frame rate). At about ~30, we are able to see a smooth movement in games and movies; however, 60 fps is what most people consider an ideal to have no noticeable glitches or stuttering. Sleeping for 17(1000/60) milliseconds each time that the game loop runs means that the game will update every 17 milliseconds (which results in 60 updates per second). Of course this assumes that the computer is capable of handling the processes every 17 milliseconds and can

be disastrous when the machine slows down (hence this is a bad game loop), but for the purposes of our simple Java game on your modern day computer, it is simple, effective, easy to implement, and very intuitive. So first we create a game loop in the run() method of the class: 1. This loop will be a while loop, and will run indefinitely. To make a while loop run forever, we just writetrue as the condition for the loop: while(true){

} 2. Within this add the following segment of code: repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); }

Your run() method will look like this:

FIGURE 2-7: RUN() IN STARTINGCLASS.JAVA


@Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } Let's look at this line by line: repaint(); - built in method - calls the paint method (in which we draw objects onto the screen). We haven't created the paint method yet, but every 17 milliseconds, the paint method will be called.. Whenever we do something that may fail (like Thread.sleep(17);) the Java Compiler gives us an error saying that we need to surround the statement with a Try and Catch.

Surround with try/catch - error

What a try/catch does is the following. 1. In try, we attempt to do something, like sleep. 2. If something goes wrong, then the catch will save information regarding errors, and will print it to a console. Don't worry too much about it. It's just a built-in fail-safe system. It won't fail here. Just click the suggested quick fix. At this point the StartingClass.java will look like this:

FIGURE 2-8: STARTINGCLASS.JAVA


package kiloboltgame; import java.applet.Applet; public class StartingClass extends Applet implements Runnable{ @Override public void init() { } @Override public void start() { Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub

} @Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } }

Lesson #2-5: Defining size, background, and title.


This will be a short lesson that should wrap up most of our setup process. We briefly talked about the four methods that we setup in Day 1: init(), start(), stop(), and destroy(). These four methods run according to the program's life cycle. We will now discuss the init() method. When the applet runs for the first time, it will run the init() method (much like how a typical program runs the main(String args[]) method). So within this method, we define certain parameters for the applet including: 1. Size of the applet 2. Background color. 3. Applet Title. Inside this method, we will be adding 1. setSize(800, 480); to change the size to 800 pixels by 480 pixels (the most common Android resolution) 2. setBackground(Color.BLACK); to set background with the Color Black. NOTE: Color.BLACK refers to a constant called BLACK in the Color superclass, and hence you must import Color:

To use a constant from the Color superclass, we must import the Color superclass.

3. setFocusable(true); This statement makes sure that when the game starts, the applet takes focus and that our input goes directly into it. If this is not enabled, then you would have to click inside the applet before it starts handling keyboard events. 4. Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha");

Here again, you must import Frame to create a Frame object called frame. This is slightly complicated, but just know that the first line assigns the applet window to the frame variable an the second line just sets the title to be Q-Bot Alpha. At this point, you will have this:

FIGURE # 2-9: STARTINGCLASS.JAVA, END OF DAY 2


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; public class StartingClass extends Applet implements Runnable{ @Override public void init() { setSize(800, 480);

setBackground(Color.BLACK); setFocusable(true); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); } @Override public void start() { Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } } Short Cut Reminders: Ctrl+Shift+O: Auto import code (may import the wrong classes) Ctrl+Shift+F: Auto format code into proper indents Ctrl+Space: Open auto complete suggestions. Thank you everyone for reading, and feel free to leave comments and questions below!

From now on, I will be including the project folder as a source code for people who don't want to write their own code, or just want to examine their own.

Lesson #2-6: Implementing KeyListener


Welcome to Day 3. Today we will be adding code that will allow our game to respond to the user's actions on the keyboard. Here's how we will approach it: 1. In order for our game to "listen" for key presses and releases, we have to give it a listener. 2. To accomplish #1, we simply need to implement the "KeyListener" interface, and then add the KeyListener in our init() method. 3. Implementing an interface requires that you take every method defined in the KeyListener superclass and add it to your code. These three methods are: keyPressed(), keyReleased(), and keyTyped().

So let's get started by implementing the KeyListener. 1. Go to your StartingClass.java and examine your class declaration:

public class StartingClass extends Applet implements Runnable{ You will notice that you are already implementing the Runnable interface, so you simply add: ", KeyListener" to the end like so: public class StartingClass extends Applet implements Runnable, KeyListener{

2. When you add this, Java will give you an error saying "KeyListener cannot be resolved to a type". To resolve this error, just import KeyListener.

You need to import KeyListener before you can implement the interface.

3. You will now see an error: "The type StartingClass must implement the inherited abstract method KeyListener.keyReleased(KeyEvent)." Recall that when you implement an interface, you must take all of its methods and declare them in your class. So you can easily resolve this by choosing: Add unimplemented methods.

To implement an interface, you must make use of all of its methods.

4. Adding unimplemented methods will do two things: First, it will add the three methods: keyPressed(), keyReleased(), and keyTyped() to your code. These three methods require a KeyEvent object as a parameter. Java is smart enough to import KeyEvent for you. 5. Finally, add this implemented KeyListener to the current Applet by adding to the init() method: addKeyListener(this);

The resulting code is as follows:

FIGURE 2-10: STARTINGCLASS.JAVA


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

public class StartingClass extends Applet implements Runnable, KeyListener{ @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); } @Override public void start() { Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub }

@Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } } The first two methods are self-explanatory. keyTyped is slightly more complicated, but you can learn more about it here: http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/event/KeyEvent.html

Lesson #2-7: Listening for Key Presses


In our 2D-platformer, we will need at least five buttons to start. The four directional arrows will each be a button, and for now we will use the Space Bar for jump. We are now working in the keyPressed() method. Inside, we will create a switch that will carry out the appropriate action depending on which button is pressed: @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: break;

case KeyEvent.VK_DOWN: break; case KeyEvent.VK_LEFT: break; case KeyEvent.VK_RIGHT: break; case KeyEvent.VK_SPACE: break; } } Let's examine this in detail. We learned about switches in Unit 1. It is an alternative to a long list of if statements. A switch will compare the key, which is the variable we are checking for, with values, and then carry out the appropriate response. In this case, the key is e.getKeyCode(). e.getKeyCode() will return the code of the button that you press on the keyboard. (If you were to type System.out.println(e.getKeyCode()); , each time that you press a button, it will display the key code on the console). What the switch is doing here, then, is comparing the e.getKeyCode() returned from your button presses, and comparing it to multiple cases of values.

Lesson #2-8: Listening for Key Releases


We will apply the exact same concept to Key Releases. In this situation, e.getKeyCode() will return the key code upon release of a button (since it is in thekeyReleased() method). Then it will carry out an appropriate response depending on which button is released. @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: break; case KeyEvent.VK_DOWN: break; case KeyEvent.VK_LEFT: break; case KeyEvent.VK_RIGHT: break; case KeyEvent.VK_SPACE: break; } }

At the present, we have no character to actually move or stop. Therefore, we will just output some text to the console like so:

FIGURE 2-12: STARTINGCLASS.JAVA, END OF DAY 3


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class StartingClass extends Applet implements Runnable, KeyListener { @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); } @Override public void start() { Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { repaint(); try { Thread.sleep(17);

} catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: System.out.println("Move down"); break; case KeyEvent.VK_LEFT: System.out.println("Move left"); break; case KeyEvent.VK_RIGHT: System.out.println("Move right"); break; case KeyEvent.VK_SPACE: System.out.println("Jump"); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: System.out.println("Stop moving down"); break; case KeyEvent.VK_LEFT: System.out.println("Stop moving left"); break; case KeyEvent.VK_RIGHT:

System.out.println("Stop moving right"); break; case KeyEvent.VK_SPACE: System.out.println("Stop jumping"); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } } That's today's lesson! On Friday, we will be adding the background, our main character, and then add some movement! Thanks for following my tutorials, and let me know if I can help you in any way!

Download File

Instructions on importing projects can be found here.

Lesson #2-9: Creating the Main Character


Welcome to the fourth day! Now that we got the basic frameworks set up, we will be adding our main character. To do so, we will be creating a new class. Within this class, we will do the following: 1. Manage x, y coordinates and speed. 2. Make updates to speed and position. 3. Allow other classes to retrieve its x, y related variables. Therefore, the methods of this class will be divided into these categories: 1. Constantly updated methods that are called on each iteration of the game loop. 2. Methods that are only called upon player input.

3. Helper methods that retrieve and change the value of variables in the class. Let's get started. We begin by creating a new class called Robot.java in the kiloboltgame package (or whatever you named your package). Copy and paste the following:

FIGURE 2-13: ROBOT.JAVA


package kiloboltgame; import java.awt.Graphics; public class Robot { //In Java, Class Variables should be private so that only its methods can change them. private int centerX = 100; private int centerY = 382; private boolean jumped = false; private int speedX = 0; private int speedY = 1; public void update() { // Moves Character or Scrolls Background accordingly. if (speedX < 0) { centerX += speedX; } else if (speedX == 0) { System.out.println("Do not scroll the background."); } else { if (centerX <= 150) { centerX += speedX; } else { System.out.println("Scroll Background Here"); } } // Updates Y Position if (centerY + speedY >= 382) { centerY = 382; }else{ centerY += speedY; } // Handles Jumping if (jumped == true) {

speedY += 1; if (centerY + speedY >= 382) { centerY = 382; speedY = 0; jumped = false; } } // Prevents going beyond X coordinate of 0 if (centerX + speedX <= 60) { centerX = 61; } } public void moveRight() { speedX = 6; } public void moveLeft() { speedX = -6; } public void stop() { speedX = 0; } public void jump() { if (jumped == false) { speedY = -15; jumped = true; } } }

WHAT DO THOSE VARIABLES MEAN?


A brief description of the variables (there will be more a in depth discussion further down): 1. centerX, centerY are the x, y coordinates of our robot character's center. 2. speedX, speed Y are the rate at which these x and y positions change. 3. jumped changes to true if the character is in the air, and reverts to false when grounded. Now let's talk about each of the methods above. 1. Always called methods: update() : this method will be called on each iteration of the for loop. This is a very important method, so let's spend time taking about it. But before we do that, PLEASE look

through it one more time and try to make sense of the if statements. *When examining the update() method again, Keep the following in mind. 1. Speed can be negative, which means that a character with negative speedX would move to the left. 2. The Origin (0,0) pixel is at the TOP LEFT. I will talk about this below. This means that if a character has a positive speedY, he is FALLING, not RISING.

The origin is at the top left of the screen.

3. Recall the meaning of += : x += 1; is equivalent to: x = x + 1; 4. We are arbitrarily defining the ground at about 440 pixels. That means if the character's centerY is at about 382, his feet would reach the ground at ~440 pixels. 5. If the character's centerX is lesser than 60, his left hand will be outside the left edge of the screen. Now have a look at a diagram:

Basic Diagram

EXAMINE THIS UPDATE() METHOD ONCE MORE BEFORE MOVING ON!


public void update() { // Moves Character or Scrolls Background accordingly. if (speedX < 0) { centerX += speedX; //This changes centerX by adding speedX. } else if (speedX == 0) { System.out.println("Do not scroll the background."); } else { if (centerX <= 150) { //If the character's centerX is in the left 150 pixels centerX += speedX; //Change centerX by adding speedX. } else { System.out.println("Scroll Background Here"); / } } // Updates Y Position if (centerY + speedY >= 382) {

//382 is where the character's centerY would be if he were standing on the ground. centerY = 382; }else{ centerY += speedY; //Add speedY to centerY to determine its new position } // Handles Jumping if (jumped == true) { speedY += 1; //While the character is in the air, add 1 to his speedY. //NOTE: This will bring the character downwards! if (centerY + speedY >= 382) { centerY = 382; speedY = 0; jumped = false; } } // Prevents going beyond X coordinate of 0 if (centerX + speedX <= 60) { //If speedX plus centerX would bring the //outside the screen, centerX = 61; //Fix the character's centerX at 60 pixels. }

character

DISSECTING THE UPDATE() METHOD:


First, let's discuss the section labeled: // Moves Character or Scrolls Background accordingly. 1. Our first if statement: For our game to draw the character in the proper location, centerX must constantly be updated; however, if the character is not moving (speedX == 0), then there is no need to update centerX by adding speed to it. 2. Watch a few seconds of this video: In Metal Slug, the character freely moves in the left half of the screen, but once he starts moving to the right, the character stays at the same location while the background scrolls. This is what we are doing here. If speed is zero, then we will not scroll the background. If the character's centerX coordinate is less than 150, he can move freely. Else, we will scroll the background and stop moving the character. Now, let's discuss the sections labeled: //Updates Y Position and //Handles Jumping. Since gravity is always present, the character is constantly being pushed to the ground. We assume that the ground is located at about 440 pixels down from the top, and if the character's Y position plus his Y speed will bring him below the ground, we use the statement: centerY

= 382; to manually set the character at a height that will stop him from moving. The Handles jumping section will check the current speed and position to test whether the character is in mid-jump or on the ground. Finally, let's discuss the section labeled:// Prevents going beyond X coordinate of 0 This section just checks if the character is moving beyond the left edge of the screen and fixes his centerX coordinate at 61 if he tries to move off the screen. Now that we have discussed the update() method, we will move on to the other methods: 2. Methods called upon input:

moveRight(), which sets the character's horizontal speed (speedX) as 6. moveLeft(), which sets the character's speedX as -6. stop(), which sets the speedX as zero. jump(), which sets the vertical speed as -15.

3. Helper Methods We will be adding these later in today's lesson.

Lesson #2-10: Graphics 101


To make sense of those speed values, we need to talk about some basic graphical stuff. Pixels are the tiny squares on your display. They are the smallest unit of change in your display, and are either on or off and have a single color. Screen resolution is a representation of your horizontal and vertical pixel counts. For example, a resolution of 1920 by 1080 tells you that a display has 1920 horizontal pixels and 1080 vertical pixels. Individual pixel coordinates are given in (x,y); however, by convention and technological limitations, we choose the top left corner as (0,0). On a screen with resolution 1920 x 1080, then, the bottom right pixel has coordinates (1919, 1079) NOT (1920, 1080). This is because we begin counting from zero, not one. I will now apply this to describe how we create an illusion of motion in games. Within the StartingClass's paint method, we will write a statement that will look something like this: drawRobot(centerX, centerY). This will draw the Robot at the coordinate (centerX, centerY). With each iteration of the paint method, which is called roughly 60 times per second, the Robot will appear to move as its centerX and centerY values change. To change the centerX and centerY, we use the speedX and speedY variables, which represent the rate at which centerX and centerY will change with each update of the game (60 times per second). So when I say that moveRight() sets the character's horizontal speed as 6, I am saying that

each time that moveRight() is called, the character's speed will be given a value of 6 pixels. In the case of the robot, the update() method, will add this speed of 6 to the centerX value. Changing the centerX value will mean that the drawRobot(centerX, centerY) statement above will now "move" the robot to a new location with its new centerX. If this happens on a speed that is fast enough, we get the illusion of smooth motion.

Creating a Robot named robot.


With the Robot class created, we can now create a Robot object inside StartingClass. Locate: public class StartingClass extends Applet implements Runnable, KeyListener { and below it, declare: Private Robot robot; to create a private Robot object called robot. Now that we have created an instance of the Robot class, we can take Robot's methods and call them by writing: robot.methodName(); (methodName() is just a generic name for any method in the Robot class).

Lesson #2-11: Drawing the Main Character from StartingClass.java


There's lots of code to cover here, and I will try to keep the explanations as simple as possible; however, some of the concepts here are a bit illusive and you might wonder how you would come up with such a solution when programming by yourself in the future. This is one of those cases where you are not required to memorize or fully understand how something works, because it is so easy to implement and difficult to fully understand. We will be working in the StartingClass.java 1. First make sure that you have imported : java.awt.Graphics; 2. Then we will begin by going below the run method (it does not matter where, but I like grouping related methods together) and create the following methods: update(Graphics g) and paint(Graphics g). It is easy to do create these methods by typing update and then pressing Ctrl+Space, and choosing the first option. Do the same thing with paint.

The update() method is implicitly called automatically, and will loop over and over again. The paint() will similarly be always called, with the repaint() statement within the run() method. I. Defining the update() Method: Let's first deal with the update() method. We will use this method for double buffering - a technique that is used to prevent tearing and flickering. Feel free to read up more on this subject, as I am not an expert in this topic. The basic concept is that it works by retaining the previous position of the screen's current image for a short amount of time, so that the movement of the image looks smooth and natural. 1. Begin by declaring (below private Robot robot): private Image image; private Graphics second;

As class variables (accessible by all methods within the StartingClass). 2. If you receive an error on either Graphics or Images, press Ctrl+Shift+O to auto-import them. 3. Now go down the the update() method and add the following:

FIGURE 2-14: UPDATE() METHOD WITHIN STARTINGCLASS.JAVA


@Override public void update(Graphics g) { if (image == null) { image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); }

second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground()); paint(second);

g.drawImage(image, 0, 0, this);

} The individual statements of this segment of code is easy to understand. What is difficult is discerning how each of these pieces fit together as a whole to create a double buffering system. Feel free to look at this in detail, but my advice to you: ignore it, accept it, and move on. :) II. Defining the paint() Method: The paint() method will be used to draw our graphics to the screen. For now, we only need to draw the robot, so it will have one statement inside:

FIGURE 2-15: PAINT() METHOD WITHIN STARTINGCLASS.JAVA


@Override public void paint(Graphics g) { g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } The g.drawImage() method takes in the following parameters: g.drawImage(img, x, y, observer)

An Image variable, the x and y coordinates where you want to draw the Image, and an ImageObserver (which is slightly beyond the scope of this lesson). In our example, we will use the character variable to represent our robot image, and then draw the top left corner of the robot 61 pixels to the left, and 63 pixels above the (centerX, centerY), and then use the "this" keyword as our ImageObserver. At this point, you will have a lot of errors. We will address them each one at a time.

The paint() method should have errors.

I. ADDRESSING THE CHARACTER VARIABLE ERROR


1. The character variable is undefined, so we must first define it. Find the statement: private Image image; and add , character like so: private Image image, character; 2. Now we must assign a value to it. We will assign an image to the variable character. To do so, we must create a URL object that will allow us to use addresses (such as C:\Images\image.jpg) to refer to images. So, below private Image image, character; add: private URL base; (As mentioned before, private variables are only accessible from within the class - it is common practice to create class-wide variables as private). Remember to import URL. 3. Within the init() method, we will define the URL base and the assign value to character. Make these changes:

FIGURE 2-16: INIT() METHOD WITHIN STARTINGCLASS.JAVA


@Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this);

Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase(); } catch (Exception e) { // TODO: handle exception }

// Image Setups character = getImage(base, "data/character.png"); } 4. Finally, we must create a data folder by right clicking on src and creating a folder named data. Inside it, download, drag, and drop this image:

Download File

That should solve the character errors. The StartingClass.java will look like this now: (If you have errors, try Rebuilding by doing the following: On the toolbar above, click on Project > Clean > OK).

FIGURE 2-17: STARTINGCLASS.JAVA, AFTER FIXING CHARACTER ERRORS


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; public class StartingClass extends Applet implements Runnable, KeyListener {

private Image image, character; private Graphics second; private URL base; @Override public void init() { setSize(800, 480);

setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase(); } catch (Exception e) { // TODO: handle exception } // Image Setups character = getImage(base, "data/character.png"); } @Override public void start() { robot = new Robot(); Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void update(Graphics g) { if (image == null) {

image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground()); paint(second); g.drawImage(image, 0, 0, this); } @Override public void paint(Graphics g) { g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: System.out.println("Move down"); break; case KeyEvent.VK_LEFT: System.out.println("Move left"); break; case KeyEvent.VK_RIGHT: System.out.println("Move right"); break; case KeyEvent.VK_SPACE: System.out.println("Jump"); break; } } @Override public void keyReleased(KeyEvent e) {

switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: System.out.println("Stop moving down"); break; case KeyEvent.VK_LEFT: System.out.println("Stop moving left"); break; case KeyEvent.VK_RIGHT: System.out.println("Stop moving right"); break; case KeyEvent.VK_SPACE: System.out.println("Stop jumping"); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } }

II. ADDRESSING THE GET... ERRORS


We still have these errors in our StartingClass:

In my previous description of the Robot class, I mentioned that we would be creating: 1. Constantly updated methods that are called on each iteration of the game loop. 2. Methods that are only called upon player input. 3. Helper methods that retrieve and change the value of variables in the class.

We never did create the helper methods. So here we do that. 1. Open Robot.java again.

2. Right click anywhere on the white space, click Source >> and select Generate Getters and Setters.

Generate Getters and Setters

3. Select all, sort by getters then setters, and then press OK. What are getters and setters? Again, in Java, it is common practice to set class-wide variables as private. For other classes to access these private variables, they must use helper functions known as getters and setters. Let's have a look at a pair: public int getSpeedX() { return speedX; }

and public void setSpeedX(int speedX) { this.height = speedX; } Whenever a getter method is called, it returns the value that you "get." When I say: myNewVariable = getSpeedX(), my new variable will get the value of speedX. If I say: setSpeed(10); then my speedX will now have a value of 10. With these three things finished, the result is:

FIGURE 2-18: STARTINGCLASS.JAVA AFTER FIXING THE GET... ERRORS


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; public class StartingClass extends Applet implements Runnable, KeyListener { private Robot robot; private Image image, character; private Graphics second; private URL base; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase(); } catch (Exception e) { // TODO: handle exception } // Image Setups character = getImage(base, "data/character.png");

} @Override public void start() { robot = new Robot(); Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void update(Graphics g) { if (image == null) { image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground()); paint(second); g.drawImage(image, 0, 0, this); }

@Override public void paint(Graphics g) { g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: System.out.println("Move down"); break; case KeyEvent.VK_LEFT: System.out.println("Move left"); break; case KeyEvent.VK_RIGHT: System.out.println("Move right"); break; case KeyEvent.VK_SPACE: System.out.println("Jump"); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: System.out.println("Stop moving down"); break; case KeyEvent.VK_LEFT: System.out.println("Stop moving left"); break;

case KeyEvent.VK_RIGHT: System.out.println("Stop moving right"); break; case KeyEvent.VK_SPACE: System.out.println("Stop jumping"); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } }

Lesson #2-12: Character Movement


At this point, you should be able to draw your character to the screen without any flickering:

Our progress so far.

Now we will make our final set of changes to make this character move. First: In StartingClass go down to the keyPressed() method, and replace: 1. System.out.println("Move left"); with robot.moveLeft(); 2. System.out.println("Move right"); with robot.moveRight(); 3. System.out.println("Jump"); with robot.jump(); And in the keyReleased() method, replace: 1. System.out.println("Stop moving left"); and 2. System.out.println("Stop moving right"); with robot.stop(); Second: Within the run() method, we need to call robot.update();

FIGURE 2-19: RUN() METHOD WITHIN STARTINGCLASS.JAVA


@Override public void run() { while (true) { robot.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } Now you should finally have the end result:

FIGURE 2-20: STARTINGCLASS.JAVA, END OF DAY 4


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; public class StartingClass extends Applet implements Runnable, KeyListener { private Robot robot; private Image image, character; private Graphics second; private URL base; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase();

} catch (Exception e) { // TODO: handle exception } // Image Setups character = getImage(base, "data/character.png"); } @Override public void start() { robot = new Robot(); Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { robot.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void update(Graphics g) { if (image == null) { image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground());

paint(second); g.drawImage(image, 0, 0, this); } @Override public void paint(Graphics g) { g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: System.out.println("Move down"); break; case KeyEvent.VK_LEFT: robot.moveLeft(); break; case KeyEvent.VK_RIGHT: robot.moveRight(); break; case KeyEvent.VK_SPACE: robot.jump(); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: System.out.println("Stop moving down");

break; case KeyEvent.VK_LEFT: robot.stop(); break; case KeyEvent.VK_RIGHT: robot.stop(); break; case KeyEvent.VK_SPACE: System.out.println("Stop jumping"); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } }

FIGURE 2-21: ROBOT.JAVA, END OF DAY 4


package kiloboltgame; import java.awt.Graphics; public class Robot { private int centerX = 100; private int centerY = 382; private boolean jumped = false; private int speedX = 0; private int speedY = 1;

public void update() { // Moves Character or Scrolls Background accordingly. if (speedX < 0) { centerX += speedX; } else if (speedX == 0) { System.out.println("Do not scroll the background.");

} else { if (centerX <= 150) { centerX += speedX; } else { System.out.println("Scroll Background Here"); } } // Updates Y Position if (centerY + speedY >= 382) { centerY = 382; }else{ centerY += speedY; } // Handles Jumping if (jumped == true) { speedY += 1; if (centerY + speedY >= 382) { centerY = 382; speedY = 0; jumped = false; } } // Prevents going beyond X coordinate of 0 if (centerX + speedX <= 60) { centerX = 61; } } public void moveRight() { speedX = 6; } public void moveLeft() { speedX = -6; } public void stop() { speedX = 0; } public void jump() { if (jumped == false) { speedY = -15;

jumped = true; } } public int getCenterX() { return centerX; } public int getCenterY() { return centerY; } public boolean isJumped() { return jumped; } public int getSpeedX() { return speedX; } public int getSpeedY() { return speedY; } public void setCenterX(int centerX) { this.centerX = centerX; } public void setCenterY(int centerY) { this.centerY = centerY; } public void setJumped(boolean jumped) { this.jumped = jumped; } public void setSpeedX(int speedX) { this.speedX = speedX; } public void setSpeedY(int speedY) { this.speedY = speedY; }

} That's it for Day 4. This was a tough lesson. Please let me know in the comments section if you need me to clarify anything at all.

If your code is not working for any reason, try Rebuilding by doing the following: On the toolbar above, click on Project > Clean > OK. As a general rule, if your code does not work and you cannot find any errors, your first step should be to rebuild the project! Thanks for reading and please support us if you are learning! Every dollar helps us out tremendously!

Download File

Instructions on importing projects can be found here. Welcome to Day 5! I hope you are enjoying the changes to our website! I think they should make these tutorials more readable and therefore easier to follow. Also, as I do these twice a week, I am learning how to format things better. So even as the code gets more complex, hopefully the organization will get simpler so that you never get lost! Today we will apply some of the concepts we covered in the previous lessons to add a scrolling background. In addition, we will be adjusting the movement so that it does not "stick" like it used to. Lastly, we will change the player's sprite (character image) depending on his actions.

Lesson #2-13: Adding a Scrolling Background (Part 1)


Let's create a Background.java class in the kiloboltgame package:

Inside this Background class, we will be doing the following. 1. Creating variables that we will be using to manipulate the background. 2. Defining a constructor (which allows us to create objects (instances) with the Background class (remember that classes are blueprints for objects. By instance we refer to an object that was created using a class and therefore has its properties). 3. Creating an update() method that will let us move the background. 4. Adding getters/setters (recall from Day 4 that getters/setters are helper methods that allow us to retrieve and manipulate private variables) that deal with the variables created in step 1. Let's begin!

I. CREATING VARIABLES
We will be creating bgX, bgY, and speedX. The first two of these represent the x and y coordinates of the background's upper left corner. So all we need to do is declare (below the class declaration: public class Background { :) private int bgX, bgY, speedX;

II. DEFINING A CONSTRUCTOR


We created a Robot object to represent our main character. In this case, each image that we piece together for the background will be represented by a Background object. To create these Background objects, we need a Background constructor. So below the variable declaration, we create one. Here, we think ahead about what variables we might need to construct a Background object. When we create new background images, we probably

want to do so at a specific location that we choose, so here I will add two parameters - one for the x coordinate, another for the y: public Background (int x, int y){ bgX = x; bgY = y; speedX = 0; }

The variables bgX, bgY and SpeedX that we defined above will now take the following values: x, y, and 0, respectively. This means that whatever two values that we feed the constructor in the StartingClassto create a Background object will become the values of bgX and bgY. We want the background to start static, so we give it a speed of zero.

III. UPDATE() METHOD


With each iteration of the game loop (it is every 17 milliseconds for our game), we need to do something with the background. In our game, the background will scroll when the character moves to the right (think Metal Slug). So we need to create an update() method that we will use to make changes to the background (such as its position and speed). In this lesson we will create an infinitely scrolling background. It will consist of two long images that will consistently loop like this: 1, 2, 1, 2, 1... Going back to the update() method, let's first list its purposes: 1. It should update the position. 2. If the background is no longer visible, it should either destroy itself (to free up memory) or be recycled by moving to a visible (or soon to be visible) location. For now, we will be using background images with dimensions 2160 x 480, so remembering what I said about looping (1, 2, 1, 2, 1...) try to conceptualize what this code does: public void update() { bgX += speedX; if (bgX <= -2160){ bgX += 4320; } } Quick Note: One of the most frequent questions that beginner programmers ask is whether a method name is pre-defined by the Java Language or arbitrarily chosen by the programmer. In this case, since we are creating a new method that does not exist, update() is a name of our own choosing; however, if you refer to the StartingClass.java, none of the methods can have their names changed. This is because we are borrowing these methods from superclasses and interfaces. An easy way to tell is to see if @Override denotes the method. Override usually means "use this new definition of the method, not the pre-defined

one in a superclass." When you create a method with Eclipse's help (using Ctrl + Space), READ the tool tip. It will often offer more insight. Quick Note #2: Be a thoughtful programmer. Think about how things work. Don't just take what I say and accept it without scrutiny. Try to conceptually understand why everything works the way it does. That is the only way you will get better to the point where you can make your own programs. If you have any questions, ask in the comments section. I love answering questions and explaining things in more depth. Okay. Now that you thought about the update() method, let's see what it does. bgX += speedX - this will change the bgX by the speed in the x direction. That makes sense. Now, the if statement: When the x coordinate of the background is at -2160 or below (it is no longer visible), we move it up by 4320 pixels (so that it can go 2160 pixels ahead of the currently visible image. If this is confusing, it will make more sense when we start running this code at the end). Question: Why does the if statement say: bgX <= -2160 and not bgX == - 2160? There is a very important reason. Hint: Think about what would happen if the speed of the Background was not a factor of 2160. Ask yourself these questions: What is the smallest unit of change in X position? Can bgX change instantly from 2161 to 2159? If so, will the condition still be satisfied?

IV. GETTERS AND SETTERS METHODS


This is easy. Eclipse does the work for us. Right click in the code, select Source > Generate Getters and Setters. It's better to have more than less, so we will generate the pair for all three of them:

And now our result:

FIGURE 2-22: BACKGROUND.JAVA


package kiloboltgame; public class Background { private int bgX, bgY, speedX; public Background(int x, int y){ bgX = x;

bgY = y; speedX = 0; } public void update() { bgX += speedX; if (bgX <= -2160){ bgX += 4320; } } public int getBgX() { return bgX; } public int getBgY() { return bgY; } public int getSpeedX() { return speedX; } public void setBgX(int bgX) { this.bgX = bgX; } public void setBgY(int bgY) { this.bgY = bgY; } public void setSpeedX(int speedX) { this.speedX = speedX; }

Lesson #2-14: Adding a Scrolling Background (Part 2)


With the Background.java class complete, we can now add our two background images! We will be dealing with the StartingClass for this lesson, so open that up. As a general rule: Whenever we add a new object to our game, we have to do the following: 0. Create a class for it so that we have a blueprint for it. (We did this for our background). 1. Within the StartingClass, we must create objects using this class (first by declaring them as variables below the class declaration and second by assigning them values in the start()

method). 2. Within the run() method, we must call the object's update() method. 3. We must paint the new object in the paint() method. Let's start with number 1 (we finished number 0 above).

I. DECLARE VARIABLE AND INITIALIZE THEM


To declare the variables: In StartingClass, below the rest of the variables, write the following: private static Background bg1, bg2; (We make them static so that we can create getters and setters for them to be used in othe classes for movement). To initialize them with values: Scroll down to the start() method, and write ABOVE robot = new Robot(); bg1 = new Background(0, 0); bg2 = new Background(2160, 0);

This will create two new Background objects separate from each other that you can refer to as bg1 andbg2. Recall that we required two integer values as parameters in the Background constructor, so we feed it x, y coordinates for each newly created Background object.

II. CALL THE UPDATE() METHOD


We want these two new Background objects to update with each iteration of the game loop. So we scroll to the run() method, and below robot.update(); we add:

bg1.update(); bg2.update();

That should do it.

III. PAINT THE BACKGROUND


Finally, we want to paint the Background. First, let's create an image variable that will hold this image (download this and save into your data folder):

Download File

1. Make changes to the line: private Image image, character; like so: private Image image, character, background; 2. Now below where we defined the character image:

character = getImage(base, "data/character.png"); Define background: background = getImage(base, "data/background.png"); 3. Then scroll to the paint method and add this: g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this); g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this); Images are painted in the order they appear. So if you want the character to be above the background, you need to put these two lines above the line that paints the character! The resulting StartingClass:

FIGURE 2-23: STARTINGCLASS.JAVA


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; public class StartingClass extends Applet implements Runnable, KeyListener { private Robot robot; private Image image, character, background; private Graphics second; private URL base; private static Background bg1, bg2; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase(); } catch (Exception e) { // TODO: handle exception } // Image Setups

character = getImage(base, "data/character.png"); background = getImage(base, "data/background.png"); } @Override public void start() { bg1 = new Background(0,0); bg2 = new Background(2160, 0); robot = new Robot();

Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { robot.update(); bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void update(Graphics g) { if (image == null) { image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight());

second.setColor(getForeground()); paint(second); g.drawImage(image, 0, 0, this); } @Override public void paint(Graphics g) { g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this); g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this); g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: System.out.println("Move down"); break; case KeyEvent.VK_LEFT: robot.moveLeft(); break; case KeyEvent.VK_RIGHT: robot.moveRight(); break; case KeyEvent.VK_SPACE: System.out.println("Jump"); robot.jump(); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up");

break; case KeyEvent.VK_DOWN: System.out.println("Stop moving down"); break; case KeyEvent.VK_LEFT: robot.stop(); break; case KeyEvent.VK_RIGHT: robot.stop(); break; case KeyEvent.VK_SPACE: System.out.println("Stop jumping"); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } public static Background getBg1() { return bg1; } public static Background getBg2() { return bg2; }

Lesson #2-15: Fixing Character Movement


As of now, whenever either left or right arrow key is released, the character will stop (even if the other one is held down). This makes for very uncomfortable movement and will not make our game a good one. The strategy to fixing this simple. We will keep boolean variables that keep track of whether each directional arrow is down, and when one is released, we will check if the other one is pressed before we stop or continue moving the opposite direction. Also, we will be allowing the character to duck, and will prevent movement while ducked.

As I feel like all the changes I made to the Robot class can be understood when you examine them line by line, I will now paste the full Robot class here, instead of showing its creation step by step. If you have any specific questions, do let me know.

MAKING NECESSARY CHANGES TO ROBOT CLASS FIGURE 2-24: ROBOT.JAVA


package kiloboltgame; import java.awt.Graphics; public class Robot { // Constants are Here final int JUMPSPEED = -15; final int MOVESPEED = 5; final int GROUND = 382; private int centerX = 100; private int centerY = GROUND; private boolean jumped = false; private boolean movingLeft = false; private boolean movingRight = false; private boolean ducked = false; Private static Background bg1 = StartingClass.getBg1(); private static Background bg2 = StartingClass.getBg2(); private int speedX = 0; private int speedY = 1; public void update() { // Moves Character or Scrolls Background accordingly. if (speedX < 0) { centerX += speedX; } if (speedX == 0 || speedX < 0) { bg1.setSpeedX(0); bg2.setSpeedX(0); } if (centerX <= 200 && speedX > 0) { centerX += speedX; } if (speedX > 0 && centerX > 200){

bg1.setSpeedX(-MOVESPEED); bg2.setSpeedX(-MOVESPEED); } // Updates Y Position centerY += speedY; if (centerY + speedY >= GROUND) { centerY = GROUND; } // Handles Jumping if (jumped == true) { speedY += 1; if (centerY + speedY >= GROUND) { centerY = GROUND; speedY = 0; jumped = false; } } // Prevents going beyond X coordinate of 0 if (centerX + speedX <= 60) { centerX = 61; } } public void moveRight() { if (ducked == false) { speedX = MOVESPEED; } } public void moveLeft() { if (ducked == false) { speedX = -MOVESPEED; } } public void stopRight() { setMovingRight(false); stop(); } public void stopLeft() { setMovingLeft(false); stop(); }

private void stop() { if (isMovingRight() == false && isMovingLeft() == false) { speedX = 0; } if (isMovingRight() == false && isMovingLeft() == true) { moveLeft(); } if (isMovingRight() == true && isMovingLeft() == false) { moveRight(); } } public void jump() { if (jumped == false) { speedY = JUMPSPEED; jumped = true; } } public int getCenterX() { return centerX; } public int getCenterY() { return centerY; } public boolean isJumped() { return jumped; } public int getSpeedX() { return speedX; } public int getSpeedY() { return speedY; } public void setCenterX(int centerX) { this.centerX = centerX; } public void setCenterY(int centerY) { this.centerY = centerY; }

public void setJumped(boolean jumped) { this.jumped = jumped; } public void setSpeedX(int speedX) { this.speedX = speedX; } public void setSpeedY(int speedY) { this.speedY = speedY; } public boolean isDucked() { return ducked; } public void setDucked(boolean ducked) { this.ducked = ducked; } public boolean isMovingRight() { return movingRight; } public void setMovingRight(boolean movingRight) { this.movingRight = movingRight; } public boolean isMovingLeft() { return movingLeft; } public void setMovingLeft(boolean movingLeft) { this.movingLeft = movingLeft; } } Dissecting the changes:

1. I never talked about constants before, so let me spend a few minutes talking about them. Constants are types of variables. They just are variables that have permanent values. To indicate a constant, we just use the keyword final (which cannot be used as a variable name for this reason), and by convention (not law) we CAPITALIZE ITS NAME. The lines: final int JUMPSPEED = -15;

final int MOVESPEED = 5; final int GROUND = 382;

Are three constants. Previously, I had these hard-coded, but creating constants for each of these values makes it easy to make changes to some of the basics of the game. In each location that I used -15, 5, and 382, I replaced the integers with these constants. 2. I replaced most of the System.out.println() placeholders with functional code. Where we previously indicated the background would scroll or stop, I added code that does exactly that. 3. You will get an error in the StartingClass when you replace your Robot.java with the code above. We will fix these soon, so do not panic!

MAKING NECESSARY CHANGES TO STARTINGCLASS


The changes here will specifically deal with just two methods in the StartingClass: keyPressed() and keyReleased(). This is because our character movement occurs here. Replace the two methods with the below code:

FIGURE 2-25: KEYPRESSED() AND KEYRELEASED()


@Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: currentSprite = characterDown; if (robot.isJumped() == false){ robot.setDucked(true); robot.setSpeedX(0); } break; case KeyEvent.VK_LEFT: robot.moveLeft(); robot.setMovingLeft(true); break; case KeyEvent.VK_RIGHT:

robot.moveRight(); robot.setMovingRight(true); break; case KeyEvent.VK_SPACE: robot.jump(); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: currentSprite = character; robot.setDucked(false); break; case KeyEvent.VK_LEFT: robot.stopLeft(); break; case KeyEvent.VK_RIGHT: robot.stopRight(); break; case KeyEvent.VK_SPACE: break; } } You will have errors, but we will fix them in this next section:

Lesson #2-16: Dynamic Sprites


To add some flavor to the game, I updated the look of our character and created two alternative sprites.

Download File

Download File

Download File

Please download the three images and place them inside your data folder. Make sure you overwrite the existing character.png! 1. We want to create a Image object for the down and jumped png's (we already did character.png), so add characterDown and characterJumped to the variable declaration statement like so: "private Image image, currentSprite, character, characterDown, characterJumped, background;" Note: Order of the declaration does not matter, but I like to organize them. What is currentSprite, you ask? At the present, we are painting the Robot robot (Robot object named robot) with the image character, but we now want to paint currentSprite, which will dynamically change to character, characterDown or characterJumped according to what the Robot robot is doing. 2. Next, update the // Image Setups section of the code: character = getImage(base, "data/character.png"); characterDown = getImage(base, "data/down.png"); characterJumped = getImage(base, "data/jumped.png"); currentSprite = character; background = getImage(base, "data/background.png"); 3. Make the following changes (in bold) to the run() method:

FIGURE 2-26: RUN() METHOD IN STARTINGCLASS


@Override public void run() { while (true) { robot.update(); if (robot.isJumped()){ currentSprite = characterJumped; }else if (robot.isJumped() == false && robot.isDucked() == false){ currentSprite = character; } bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace();

} } } These changes will check the current state of the robot and make necessary changes to currentSprite. 4. The last thing you need to do is change the Image variable that is being painted for the robot: g.drawImage(character, robot.getCenterX() - 61, robot.getCenterY() - 63, this); should now be: g.drawImage(currentSprite, robot.getCenterX() - 61, robot.getCenterY() - 63, this);

That should fix all the problems in your code, and we have the final StartingClass:

FIGURE 2-27: STARTINGCLASS, END OF DAY 5


package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; public class StartingClass extends Applet implements Runnable, KeyListener { private Robot robot; private Image image, currentSprite, character, characterDown, characterJumped, background; private Graphics second; private URL base; private static Background bg1, bg2; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase();

} catch (Exception e) { // TODO: handle exception } // Image Setups character = getImage(base, "data/character.png"); characterDown = getImage(base, "data/down.png"); characterJumped = getImage(base, "data/jumped.png"); currentSprite = character; background = getImage(base, "data/background.png"); } @Override public void start() { bg1 = new Background(0,0); bg2 = new Background(2160, 0); Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { robot.update(); if (robot.isJumped()){ currentSprite = characterJumped; }else if (robot.isJumped() == false && robot.isDucked() == false){ currentSprite = character; } bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } }

robot = new Robot();

} @Override public void update(Graphics g) { if (image == null) { image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground()); paint(second); g.drawImage(image, 0, 0, this); } @Override public void paint(Graphics g) { g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this); g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this); g.drawImage(currentSprite, robot.getCenterX() - 61, robot.getCenterY() - 63, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: currentSprite = characterDown; if (robot.isJumped() == false){ robot.setDucked(true); robot.setSpeedX(0); } break; case KeyEvent.VK_LEFT: robot.moveLeft(); robot.setMovingLeft(true); break; case KeyEvent.VK_RIGHT: robot.moveRight();

robot.setMovingRight(true); break; case KeyEvent.VK_SPACE: robot.jump(); break; } } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: currentSprite = character; robot.setDucked(false); break; case KeyEvent.VK_LEFT: robot.stopLeft(); break; case KeyEvent.VK_RIGHT: robot.stopRight(); break; case KeyEvent.VK_SPACE: break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } public static Background getBg1() { return bg1; } public static Background getBg2() { return bg2;

} } That's it for Day 5! I hope that you guys learned a lot and that I did not proceed too quickly. Thank you for reading my tutorials and supporting Kilobolt Studios! Got questions? Ask away!

Hello everyone, and welcome to Day 6 of Unit 2. In today's lesson, we will be adding an enemy to our game, because our main char Our first enemy will look like this:

The Menacing Heliboy

Too add Heliboy to our game, we will first create an Enemy class (which will serve as the superclass for most enemy types), and als

Lesson #2-17: Creating the Enemy Class


Here's how we will proceed: 1. Create the Enemy class. 2. Declare variables 3. Create behavior related methods 4. Create helper methods (Getters and Setters)

The Enemy class will be pretty simple. Since this will be a superclass, we will define commonly used variables and methods, so that

I. CREATING THE ENEMY CLASS

Creating the Enemy class.

1. Right-click on our kiloboltgame package >> New >> Class. 2. Name it "Enemy". 3. Press OK!

II. DECLARING VARIABLES


All the enemies that we create will probably have the following: 1. Max Health 2. Current Health 3. Power (damage output) 4. Speed 5. X coordinate 6. Y coordinate

In addition, whenever the background scrolls, the enemy should move in the same direction. So we will create a reference to the bg Within the class definition, declare the following:

private int maxHealth, currentHealth, power, speedX, centerX, centerY; private Background bg = StartingClass.getBg1();

III. CREATING BEHAVIORAL METHODS


Add the following code below your variable declarations:

As with all other game objects, we will need to first create a method that will constantly be running: update(); In addition, we will crea

//Behavioral Methods public void update() { centerX += speedX;

speedX = bg.getSpeedX(); } public void die() { } public void attack() { }

TIP: In Eclipse, you can indent multiple lines of code at once by selecting multiple lines and pressing Tab. You can also dedent by p TIP #2: You can auto format your code (fix indents and such) by pressing Ctrl + Shift + F.

IV. GENERATE GETTERS AND SETTERS


We will need to create methods that will allow us to retrieve and manipulate the variables declared in this class. 1. Right-click on your code >> Source >> Generate Getters and Setters. 2. Select All, and press OK. 3. Eclipse will automatically add the following code:

public int getMaxHealth() { return maxHealth; } public int getCurrentHealth() { return currentHealth; } public int getPower() { return power; } public int getSpeedX() { return speedX; } public int getCenterX() { return centerX; } public int getCenterY() { return centerY; } public Background getBg() { return bg; } public void setMaxHealth(int maxHealth) { this.maxHealth = maxHealth; } public void setCurrentHealth(int currentHealth) { this.currentHealth = currentHealth; } public void setPower(int power) { this.power = power; }

public void setSpeedX(int speedX) { this.speedX = speedX; } public void setCenterX(int centerX) { this.centerX = centerX; } public void setCenterY(int centerY) { this.centerY = centerY; } public void setBg(Background bg) { this.bg = bg; }

And you are done with the Enemy class for now! It should now look like this:

FIGURE 2-28: ENEMY CLASS


package kiloboltgame; public class Enemy {

private int maxHealth, currentHealth, power, speedX, centerX, centerY; private Background bg = StartingClass.getBg1();

// Behavioral Methods public void update() { centerX += speedX; speedX = bg.getSpeedX();

public void die() {

public void attack() {

public int getMaxHealth() { return maxHealth; }

public int getCurrentHealth() { return currentHealth; }

public int getPower() { return power; }

public int getSpeedX() { return speedX; }

public int getCenterX() { return centerX; }

public int getCenterY() { return centerY; }

public Background getBg() { return bg; }

public void setMaxHealth(int maxHealth) { this.maxHealth = maxHealth; }

public void setCurrentHealth(int currentHealth) { this.currentHealth = currentHealth; }

public void setPower(int power) { this.power = power; }

public void setSpeedX(int speedX) { this.speedX = speedX; }

public void setCenterX(int centerX) { this.centerX = centerX; }

public void setCenterY(int centerY) { this.centerY = centerY; }

public void setBg(Background bg) { this.bg = bg; }

Lesson #2-18: Creating the Heliboy class.


1. Right-click on kiloboltgame >> New >> Class 2. Name it "Heliboy" 3. Now, in the Superclass: option, press Browse. 4. Type "enemy" in Choose a type:

We will now be creating the Heliboy class. We will be creating this class slightly differently, so even if you know how to make classes in Eclipse, read the instructions

Choosing the Enemy Superclass

5. Press OK. 6. Check "Constructors from superclass:"

7. Press Finish. Voila! The following code generates:

FIGURE 2-28: HELIBOY CLASS


package kiloboltgame; public class Heliboy extends Enemy { public Heliboy() { // TODO Auto-generated constructor stub } }

By taking steps 4 and 6 : choosing Enemy as a superclass and adding a constructor, Eclipse automatically generated the code in bo Replace the //TODO message with the following code: setCenterX(centerX);

setCenterY(centerY); And add the following parameters to the constructor so that we can use centerX and centerY:

public Heliboy(int centerX, int centerY) { And now you have the completed Heliboy class. Whenever you create a new Heliboy object, you will pass in two parameters, which

Lesson #2-19: Displaying Heliboy


To display Heliboy in our game, we must do the following. 1. Create an Image object for Heliboy. 2. Define hb variables that will be objects created using the Heliboy constructor. 3. Call the update() method for these objects. 4. Paint hb objects with the Image object created in step 1.

I. CREATE IMAGE OBJECT - HELIBOY


Download File

1. Download the above image, place it in your data folder, and rebuild the project by going to Project >> Clean >> Clean All Projects 2. Open StatingClass.java, and add heliboy to the Image declarations. private Image image, currentSprite, character, characterDown, characterJumped, background, heliboy; 3. In the // Image Setups section, define the heliboy Image object: heliboy = getImage(base, "data/heliboy.png");

II. CREATE HB VARIABLES


1. Below the private Robot robot; declaration, add the following: private Heliboy hb, hb2; We will be creating two Heliboy objects.

2. Within the start() method, add the two bolded lines BELOW the Background creation (the Enemy superclass looks for these back bg1 = new Background(0, 0); ... hb = new Heliboy(340, 360); hb2 = new Heliboy(700, 360); ... Thread thread = new Thread(this); ... This will create two Heliboy objects centered at (340, 360) and (700, 360).

III. CALL THE UPDATE METHOD


Scroll down to the run() method, and add: hb.update(); hb2.update(); Like so: @Override public void run() { while (true) { robot.update();

if (robot.isJumped()){ currentSprite = characterJumped; }else if (robot.isJumped() == false && robot.isDucked() == false){ currentSprite = character; } hb.update(); hb2.update(); bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } }

You might notice that we never defined update() in the Heliboy class; however, since the Heliboy class inherited the Enemy class, c class. This is inheritance in action. :)

IV. PAINT THE HB OBJECTS


Finally, we have to make the newly created Heliboy objects appear on the screen. 1. Go to the paint() method and add the following at the end: g.drawImage(heliboy, hb.getCenterX() - 48, hb.getCenterY() - 48, this); g.drawImage(heliboy, hb2.getCenterX() - 48, hb2.getCenterY() - 48, this);

The Heliboy.png image you downloaded has dimensions 96 x 96. So, if we paint 48 pixels lower in both X and Y (by subtracting 48), Heliboy(340, 360);) will represent the centerX and centerY coordinates of the newly drawn images.

And that's it for Day 6! I hope you guys are beginning to pick up on the patterns, so you can add your own ideas to the game as they Thank you for supporting Kilobolt and tell your friends about us! :)

Download File

Go to Day 5: Background and Sprites


9 Comments

Go to Day 7: Shooting B

Welcome to Day 7. Thank you to those of you who submitted ideas on the story of this game. We will be implementing some of them

This lesson will be dedicated to arming our character, so that next week, when we go over collision detection, we will have more to w

This is how we will approach today's task: 1. We will first create a Projectiles class, which we will use as a blueprint for our bullet objects. 2. Within the Robot class, we will create a method that handles shooting. 3. Finally, in the StartingClass, we will paint the bullets to the screen, and allow the user to shoot with the Control button.

Lesson #2-20: Creating the Projectile Class The Projectile class, as mentioned, will be a blueprint for creating bullets! As with many of the previous classes, we will do the following: 1. Create the class. 2. Declare variables 3. Create an update() method and create helper Getters and Setters methods I. CREATING THE PROJECTILE CLASS 1. Right click on the kiloboltgame package >> New >> Class 2. Name it "Projectile". 3. Check the box labeled "Constructors from superclass:" 4. Press OK! You should now see the following: package kiloboltgame; public class Projectile { public Projectile() { // TODO Auto-generated constructor stub } } II. DECLARING VARIABLES Let's now declare the variables that we will be using. private int x, y, speedX; private boolean visible; These are class-wide variables, so they should be below the class declaration: public class Projectile{ private int x, y, speedX; private boolean visible; Now, we need to make some changes to the constructor: public Projectile(){ } As of now, it takes in no parameters. We want it to take in two values, a starting X coordinate and a starting Y coordinate, which will 1. So we add the two parameters: public Projectile(int startX, int startY){ }

2. To set these startX and startY variables into the class-wide x and y variables, and to set the speedXof the bullet and to initialize th constructor: x = startX; y = startY; speedX = 7; visible = true;

Your constructor should now look like this: public Projectile(int startX, int startY){

x = startX; y = startY; speedX = 7; visible = true;

} III. CREATING THE UPDATE() AND HELPER METHODS The update() method in this class, as in the other classes, will be called with each update of the game. Naturally, then, we should change the location of the bullet here with respect to speed, and react when the bullet collides or goes off 1. So first create an update method: public void update(){

} 2. Add the following statements: x += speedX; if (x > 800) { visible = false; } - The x += speedX; statement will continually update the x coordinate by adding to it the speed in the x direction.

- The if statement checks if the bullet is off the screen, and makes it invisible. In the other classes, we will remove these bullets so th 3. Now create Getters and Setters: - Right click on the code, go to Source >> Generate Getters and Setters - Select All - Press OK You should get the following: public int getX() { return x; }

public int getY() { return y; }

public int getSpeedX() { return speedX; }

public boolean isVisible() { return visible; }

public void setX(int x) { this.x = x; }

public void setY(int y) { this.y = y; }

public void setSpeedX(int speedX) { this.speedX = speedX;

public void setVisible(boolean visible) { this.visible = visible; }

That should be it for the Projectile class for now. Here's the final code: FIGURE 2-29: PROJECTILE CLASS package kiloboltgame; public class Projectile { private int x, y, speedX; private boolean visible; public Projectile(int startX, int startY){ x = startX; y = startY; speedX = 7; visible = true; } public void update(){ x += speedX; if (x > 800){ visible = false; } } public int getX() { return x; } public int getY() { return y; } public int getSpeedX() { return speedX; } public boolean isVisible() { return visible; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setSpeedX(int speedX) { this.speedX = speedX; } public void setVisible(boolean visible) { this.visible = visible; }

} Lesson #2-21: Creating an ArrayList in the Robot Class It would be extremely time consuming and inefficient if we were to manually create bullets each time that the player pressed "shoot."

us to store our Projectile objects in a "container" of increasing size. 1. To do so, we first declare (below all the other variable declarations): private ArrayList<Projectile> projectiles = new ArrayList<Projectile>(); (Make sure you import ArrayList, Ctrl+Shift+O).

Doing this creates an ArrayList (of Projectiles) called projectiles. 2. Now, below the jump() method, I will add the shoot() method: public void shoot() { Projectile p = new Projectile(centerX + 50, centerY - 25); projectiles.add(p); }

This method simply creates a new Projectile, labels it p, and adds it to the projectiles ArrayList. We create this 50 pixels to the right a 3. Create a Getter method as follows: public ArrayList getProjectiles() { return projectiles; } This will allow us to reference this newly created ArrayList from the other classes. Your finished Robot class will look like this: FIGURE 2-30: ROBOT CLASS (CHANGES IN BOLD) package kiloboltgame; import java.util.ArrayList; public class Robot { // Constants are Here final int JUMPSPEED = -15; final int MOVESPEED = 5; final int GROUND = 382; private int centerX = 100; private int centerY = GROUND; private boolean jumped = false; private boolean movingLeft = false; private boolean movingRight = false; private boolean ducked = false; private int speedX = 0; private int speedY = 1; private Background bg1 = StartingClass.getBg1(); private Background bg2 = StartingClass.getBg2(); private ArrayList<Projectile> projectiles = new ArrayList<Projectile>(); public void update() { // Moves Character or Scrolls Background accordingly. if (speedX < 0) { centerX += speedX; } if (speedX == 0 || speedX < 0) { bg1.setSpeedX(0); bg2.setSpeedX(0); } if (centerX <= 200 && speedX > 0) {

centerX += speedX; } if (speedX > 0 && centerX > 200){ bg1.setSpeedX(-MOVESPEED); bg2.setSpeedX(-MOVESPEED); } // Updates Y Position centerY += speedY; if (centerY + speedY >= GROUND) { centerY = GROUND; } // Handles Jumping if (jumped == true) { speedY += 1; if (centerY + speedY >= GROUND) { centerY = GROUND; speedY = 0; jumped = false; } } // Prevents going beyond X coordinate of 0 if (centerX + speedX <= 60) { centerX = 61; } } public void moveRight() { if (ducked == false) { speedX = MOVESPEED; } } public void moveLeft() { if (ducked == false) { speedX = -MOVESPEED; } } public void stopRight() { setMovingRight(false); stop(); } public void stopLeft() { setMovingLeft(false); stop(); } private void stop() { if (isMovingRight() == false && isMovingLeft() == false) { speedX = 0; }

if (isMovingRight() == false && isMovingLeft() == true) { moveLeft(); }

if (isMovingRight() == true && isMovingLeft() == false) { moveRight(); } }

public void jump() { if (jumped == false) { speedY = JUMPSPEED; jumped = true; } } public void shoot() { Projectile p = new Projectile(centerX + 50, centerY - 25); projectiles.add(p); }

public int getCenterX() { return centerX; } public int getCenterY() { return centerY; }

public boolean isJumped() { return jumped; }

public int getSpeedX() { return speedX; }

public int getSpeedY() { return speedY; }

public void setCenterX(int centerX) { this.centerX = centerX; }

public void setCenterY(int centerY) { this.centerY = centerY; }

public void setJumped(boolean jumped) { this.jumped = jumped; }

public void setSpeedX(int speedX) { this.speedX = speedX; }

public void setSpeedY(int speedY) { this.speedY = speedY; }

public boolean isDucked() { return ducked; }

public void setDucked(boolean ducked) { this.ducked = ducked; }

public boolean isMovingRight() { return movingRight; }

public void setMovingRight(boolean movingRight) { this.movingRight = movingRight; }

public boolean isMovingLeft() { return movingLeft; }

public void setMovingLeft(boolean movingLeft) { this.movingLeft = movingLeft; }

public ArrayList getProjectiles() { return projectiles; }

} Lesson #2-22: Painting the Bullets in the StartingClass I. CHANGING THE RUN() METHOD Make the changes in bold to your run() method: @Override public void run() { while (true) { robot.update(); if (robot.isJumped()) { currentSprite = characterJumped; } else if (robot.isJumped() == false && robot.isDucked() == false) { currentSprite = character; } ArrayList projectiles = robot.getProjectiles(); for (int i = 0; i < projectiles.size(); i++) { Projectile p = (Projectile) projectiles.get(i); if (p.isVisible() == true) { p.update(); } else { projectiles.remove(i); } } hb.update(); hb2.update(); bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } }

} (Make sure you import ArrayList: Ctrl+Shift+O) Let's talk about what we did here: We created a new ArrayList called projectiles and gave it the value of the projectiles ArrayList in robot. We then created a for loop, which runs as long as the index i is lesser than the size of the projectiles ArrayList (which is the number Then we check which Projectile object is in the i-th (4th, 5th, etc). place in the ArrayList (the Projectile object with an index of i).

To illustrate indexes, here's another example: List = [1, 2, 3, 5, 7] The indexes of each of these are: 0, 1, 2, 3, and 4. This means that the for loop will go through each object in the ArrayList of Projec

After that, it checks if this p is on the screen (isVisible()). If true, it updates it. Else, it removes this p by removing the i-th index from t

To summarize all this, we create a for loop with an index called i which goes up by 1 each time. We set the i-th Projectile in the proje on screen). II. CHANGING THE PAINT() METHOD Make the changes in bold in the paint() method: @Override public void paint(Graphics g) { g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this); g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this); ArrayList projectiles = robot.getProjectiles(); for (int i = 0; i < projectiles.size(); i++) { Projectile p = (Projectile) projectiles.get(i); g.setColor(Color.YELLOW); g.fillRect(p.getX(), p.getY(), 10, 5); } g.drawImage(currentSprite, robot.getCenterX() - 61, robot.getCenterY() - 63, this); g.drawImage(heliboy, hb.getCenterX() - 48, hb.getCenterY() - 48, this); g.drawImage(heliboy, hb2.getCenterX() - 48, hb2.getCenterY() - 48, this); } The same concepts apply here.

We created an ArrayList called projectiles (the one from the update() method is only usable by the update() method, so this is a uniq class.

We used a for loop and painted each Projectile object called p, which represents the i-th Projectile in the ArrayList at the current itera at the X and Y coordinate of the p Projectile. Lesson #2-23: Shooting on Keypress We want to use the Ctrl button to shoot our bullets. Navigate to the keyPressed() method in StartingClass, and make the following changes in bold: @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: currentSprite = characterDown; if (robot.isJumped() == false) { robot.setDucked(true); robot.setSpeedX(0); } break; case KeyEvent.VK_LEFT:

robot.moveLeft(); robot.setMovingLeft(true); break; case KeyEvent.VK_RIGHT: robot.moveRight(); robot.setMovingRight(true); break; case KeyEvent.VK_SPACE: robot.jump(); break;

case KeyEvent.VK_CONTROL: if (robot.isDucked() == false && robot.isJumped() == false) { robot.shoot(); } break; } }

All we did was add another case to the switch, so that when CONTROL is pressed, the robot calls itsshoot() method. We also check We can now run the game, and you should be able to shoot with the Control button! The final StartingClass follows (changes in bold): FIGURE 2-31: STARTINGCLASS, END OF DAY 7 package kiloboltgame; import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.URL; import java.util.ArrayList; public class StartingClass extends Applet implements Runnable, KeyListener { private Robot robot; private Heliboy hb, hb2; private Image image, currentSprite, character, characterDown, characterJumped, background, heliboy; private Graphics second; private URL base; private static Background bg1, bg2; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); setFocusable(true); addKeyListener(this); Frame frame = (Frame) this.getParent().getParent(); frame.setTitle("Q-Bot Alpha"); try { base = getDocumentBase(); } catch (Exception e) { // TODO: handle exception } // Image Setups

character = getImage(base, "data/character.png"); characterDown = getImage(base, "data/down.png"); characterJumped = getImage(base, "data/jumped.png"); currentSprite = character; heliboy = getImage(base, "data/heliboy.png"); background = getImage(base, "data/background.png"); } @Override public void start() { bg1 = new Background(0, 0); bg2 = new Background(2160, 0); hb = new Heliboy(340, 360); hb2 = new Heliboy(700, 360); robot = new Robot(); Thread thread = new Thread(this); thread.start(); } @Override public void stop() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void run() { while (true) { robot.update(); if (robot.isJumped()) { currentSprite = characterJumped; } else if (robot.isJumped() == false && robot.isDucked() == false) { currentSprite = character; }

ArrayList projectiles = robot.getProjectiles(); for (int i = 0; i < projectiles.size(); i++) { Projectile p = (Projectile) projectiles.get(i); if (p.isVisible() == true) { p.update(); } else { projectiles.remove(i); } } hb.update(); hb2.update(); bg1.update(); bg2.update(); repaint(); try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void update(Graphics g) { if (image == null) {

image = createImage(this.getWidth(), this.getHeight()); second = image.getGraphics(); } second.setColor(getBackground()); second.fillRect(0, 0, getWidth(), getHeight()); second.setColor(getForeground()); paint(second); g.drawImage(image, 0, 0, this); } @Override public void paint(Graphics g) { g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this); g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this); ArrayList projectiles = robot.getProjectiles(); for (int i = 0; i < projectiles.size(); i++) { Projectile p = (Projectile) projectiles.get(i); g.setColor(Color.YELLOW); g.fillRect(p.getX(), p.getY(), 10, 5); } g.drawImage(currentSprite, robot.getCenterX() - 61, robot.getCenterY() - 63, this); g.drawImage(heliboy, hb.getCenterX() - 48, hb.getCenterY() - 48, this); g.drawImage(heliboy, hb2.getCenterX() - 48, hb2.getCenterY() - 48, this); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Move up"); break; case KeyEvent.VK_DOWN: currentSprite = characterDown; if (robot.isJumped() == false) { robot.setDucked(true); robot.setSpeedX(0); } break; case KeyEvent.VK_LEFT: robot.moveLeft(); robot.setMovingLeft(true); break; case KeyEvent.VK_RIGHT: robot.moveRight(); robot.setMovingRight(true); break; case KeyEvent.VK_SPACE: robot.jump(); break; case KeyEvent.VK_CONTROL: if (robot.isDucked() == false && robot.isJumped() == false) { robot.shoot(); } break;

} } @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Stop moving up"); break; case KeyEvent.VK_DOWN: currentSprite = character; robot.setDucked(false); break; case KeyEvent.VK_LEFT: robot.stopLeft(); break; case KeyEvent.VK_RIGHT: robot.stopRight(); break; case KeyEvent.VK_SPACE: break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } public static Background getBg1() { return bg1; } public static Background getBg2() { return bg2; } } Thank you for reading this lesson! Things are starting to get a bit repetitive, so we will be mixing things up next time around. As always, we appreciate your support, and we're extremely pleased that we broke 750 Facebook Likes!

Download File

Go to Day 6: Adding Enemies


16 Comments

Go to Day 8: Anima

Welcome to Day 9! Today's lesson will be short, but full of information. We are building towards loading pre-made Level designs and rendering tiles to the screen. To do so, we must first go over some basic fundamentals. Therefore, we will be creating a new project to practice and study these new topics! Lesson #2-26: Arrays One of the most powerful tools in a programmer's arsenal is the array. You can think of an array as a list of items. Let's say you had three favorite things: 1. Playing 2. Eating 3. Sleeping These three things could easily be categorized into one list so that you can refer people to this list rather than three separate variables. That's what an array does, in principle. 1. To see this in action, create a new Project, and name it whatever you want. 2. Within the src folder, create a new Java class called: Array. 3. Add the main method: public static void main(String args[]) { } 4. Within the main method, declare the following: String[] favoriteThings = new String [3]; This creates an Array (list) of String objects called stringArray, and allocates memory to accommodate three (3) items. Alternatively, this same line can be written as below, by shifting the brackets: String favoriteThings[] = new String [3]; Astute observers will recognize this syntax. If you examine the parameter of the main method: String args[], you will now see that the main method takes in an array of Strings as a parameter. These arrays are passed in at a lower level, and you will not encounter them in Eclipse.

Your code should be looking like this:

FIGURE 2-34: ARRAY CLASS


public class Array { public static void main(String args[]) { String[] favoriteThings = new String [3]; } } Now let's talk about how to fill this Array. We can refer to positions in the array as indices. The first item in the list will have an index of 0, the second will have 1, and so on. To refer to a specific index of an Array, we can write: array[index]. Going back to our code, we can list our three favorite things as follows: ... public static void main(String args[]) { String[] favoriteThings = new String [3]; favoriteThings[0] = "Playing"; favoriteThings[1] = "Eating"; favoriteThings[2] = "Sleeping"; } ... What if we now want to print all these items? It's tempting to use System.out.println(favoriteThings), but this will return what seems to be nonsense. Instead, we must use a for loop, and iterate over an index to print each line. This is how I would approach it: for (int i = 0; i < favoriteThings.length; i++) { System.out.print(i+1 + ". "); System.out.println(favoriteThings[i]); }

This code introduces nothing new. It is just application of previously-covered topics. Make sure you understand this before moving on! If we place this for loop at the bottom of the main method, we will see the following output:

Understand how it works? Then you are ready for the next level. Lesson #2-27: Two-Dimensional Arrays The most practical application of a two-dimensional Array is when you need to store a list of values with (x, y) positions. For our game's purposes, we will use a 2D array to hold a value at each position, and then paint the object/character/platform associated with said value at its (x, y) coordinate.

2D Arrays are very similar to single dimensional arrays, but we add a second dimension (like adding a width). Two create a 2D array, we simply state: int[][] intArray = new int[30][50]; //Creates a 2D array of integers with height 30 and width 50. 1. Create a new class called Tilemap. 2. Add the following code:

FIGURE 2-35: TILEMAP CLASS


import java.util.Random; public class Tilemap {

public static void main(String args[]) { Tilemap tp = new Tilemap(); } public Tilemap() { int[][] tilemap = new int[30][50]; System.out.println("New Tilemap created."); Random r = new Random();

int rows = tilemap.length; int columns = tilemap[1].length; printTiles(rows, columns, tilemap, r); } private void printTiles(int rows, int columns, int[][] tilemap, Random r) { for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { tilemap[i][j] = r.nextInt(5); System.out.print(" " + tilemap[i][j]); } System.out.println(""); } } } Before you run this code, let's talk about what each segment of code does. 1. We import the Random class (to be used later) 2. In the main method, we create a new Tilemap object called tp. This calls the constructor below the main method. 3. In the constructor, we create a new two dimensional array called tilemap, with height 30, and width 50. - We also create a Random object called r Two-dimensional arrays can be thought of as Arrays of Arrays. Meaning that it is a list of lists. The larger list has 30 empty spots. Each of those 30 spots has 50 spots across. Therefore, when we refer to tilemap.length, we get the length of the larger list, which is 30. When we refer to tilemap[1].length, we get the length of the small list with index 1 in the large list, which is the second small list in the large list. This will have a value of 50. 4. We then call the printTiles() method, which takes in various parameters, uses two for loops (one for the columns and another for the rows). This printTiles() method gives each space a value between 0 to 4 (at random). Then we print each row and column. The result should look something like this:

In the next lesson, we will associate each integer from 0 to 4 with a colored rectangle and fill the screen. This will then segue into a Unit 3, beginning with a series of lessons on Level design/rendering and collision physics!
Hello and welcome! This is the last lesson of Unit 2. More fun is just around the corner in Unit 3. Let's finish strong! In today's lesson, we will be building upon what we covered in Day 9. If you have not looked at Day 9 yet, please do so before proceeding! What we will be covering: 1. We will be creating a two-dimensional Array that holds a random set of values. 2. Using this two-dimensional array, we will print colored squares to the screen.

What is the significance of this lesson? When we continue our 2D game development, we will be creating levels (maps) using text files. In each text file, we will represent ob with keyboard characters, and our game will parse the file to create levels that we can interact with. This lesson will be an application of what we learned in Day 9. After this, we should be able to accomplish the above task with ease

lessons. Note: We will be creating a NEW class today. You do not need to open any previous files. Let's begin.

Lesson #2-28: Creating the Tilemap


1. Begin by creating a class named Renderer in any test Project. (Tip: You can create a new Project by right-clicking on the Package Explorer >> New >> Java Project.) 2. Extend Applet and import it. - add "extends Applet" at the end of the class declaration. - Press Ctrl+Shift+O to organize imports automatically. It should look like this: import java.applet.Applet;

public class Renderer extends Applet{ }

3. Override the init() and paint() methods. Since we have extended an Applet (inheritance), Java will look for the paint() and init() methods. As we will be defining these method than using the one in the Applet method, so we use the " @Override" keyword. The two methods will look like this (Make sure to import Graphics) (changes in Bold): import java.applet.Applet; import java.awt.Graphics; public class Renderer extends Applet{ @Override public void init() { } @Override public void paint(Graphics g) { } } 4. Define the init() method.

In the init() method, we want to do the following: - Set the size of the window. - Set the color of the Background - Create the Tilemap. So we add the following lines of code: setSize(800, 480); setBackground(Color.BLACK); createTilemap(); Eclipse will give you an error as follows:

Since we have never created a createTilemap() method, we must do so right now. 5. Click on the 1 quickfix: Create method 'createTilemap()'. This should create a method as follows:

private void createTilemap() {

We must do two things in the Tilemap() method.


1. We need to create a two-dimensional array. We will call it tilemap and call its height rows and its width columns. 2. Using a random number generator, we will fill tilemap with integers between 0 and 4, inclusive.

Let's do these two things:


1. Creating a 2D Array Before we begin, to make things easier, we will create three static variables at the top of the class. - Directly below "public class Renderer extends Applet {", add the following:

static int[][] tilemap; static int rows, columns; This creates a static (class-wide) two-dimensional Array called tilemap and two integers called rowsand columns. Scroll back inside the createTilemap() method. We will now define the three variables by declaring the following:

tilemap = new int[50][30]; rows = tilemap.length; columns = tilemap[1].length;

- The first line creates a new 2D Array with 50 elements, which each contain 30 elements. - tilemap.length is 50, as tilemap contains 50 elements. - Each of these 50 elements contain 30 elements. Therefore, tilemap[1] refers to the element with index 1 in this 50 element list (the fifty). Note: Remember that indexes start at 0. Note 2: We can refer to all 50 elements in tilemap using the indexes: 0 through 49.

2. Randomly Fill tilemap Array

- To fill the tilemap Array randomly, we first create a new Random object: Random r = new Random();

- Then we use two for loops to fill each index with a random number between 0 and 4, inclusive: for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { tilemap[i][j] = r.nextInt(5); } }

Note: Recall that tilemap[i][j] refers to the spot with coordinates (i, j) inside the 2D array. Refer to this simplified illustration if you are confused!

Here's what the code will look like as of now:

FIGURE 2-36: RENDERER CLASS, CREATETILE


import java.applet.Applet; import java.awt.Color; import java.awt.Graphics; import java.util.Random; public class Renderer extends Applet { static int[][] tilemap; static int rows, columns; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); createTilemap(); } private void createTilemap() { tilemap = new int[50][30];

rows = tilemap.length; columns = tilemap[49].length;

Random r = new Random();

for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { tilemap[i][j] = r.nextInt(5); } } } @Override public void paint(Graphics g) { } } With the tilemap created, we can now paint it to the screen!

Lesson #2-29: Painting the Tilemap


This is what we are trying to accomplish: 1. Create a square with side length 16 pixels - 50 across and 30 down (800 x 480). 2. Give this square a random color based on the integer value (0 to 4, inclusive) inside the tilemap. - We begin Scroll to the paint(Graphics g) method. - Add the following code inside the method:

for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; } }

This is the same nested for loop we saw in the createTilemap() method. We will use i and j to refer to locations in the 2D Array.

We want to translate this (i, j) location into a coordinate system of (16*i, 16*j), because we want to represent, for example, (50, 30), a So within the inner for loop, we add the following in BOLD: for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; int mod_i = 16*i; int mod_j = 16*j; } }

Note: The underscore "_" does not have any meaning. It is just part of the variable names, which stand for modified I and m

Now directly below the two variable declarations (and still within the inner for loop), we need to create a switch that takes tilemap[i][ that switches are used to compare the value of a key against cases) and compare with the integers 0 through 4, inclusive.

For each case, we will set the color of g, which needs to be set directly before you paint an object, and then paint a rectangle on the coordinates (mod_i, mod_j). The full class will now look like this:

FIGURE 2-37: RENDERER CLASS, COMPLETED


import java.applet.Applet; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics;

import java.util.Random; public class Renderer2 extends Applet { static int[][] tilemap; static int rows, columns; @Override public void init() { setSize(800, 480); setBackground(Color.BLACK); createTilemap(); } @Override public void paint(Graphics g) { for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { int mod_i = 16*i; int mod_j = 16*j; switch (tilemap[i][j]) { case 0: g.setColor(Color.RED); g.fillRect(mod_i, mod_j, 16, 16); break; case 1: g.setColor(Color.BLUE); g.fillRect(mod_i, mod_j, 16, 16); break; case 2: g.setColor(Color.YELLOW); g.fillRect(mod_i, mod_j, 16, 16); break; case 3: g.setColor(Color.WHITE); g.fillRect(mod_i, mod_j, 16, 16); break; case 4: g.setColor(Color.GREEN); g.fillRect(mod_i, mod_j, 16, 16); break; } } } } private void createTilemap() { tilemap = new int[50][30]; rows = tilemap.length; columns = tilemap[49].length;

Random r = new Random(); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { tilemap[i][j] = r.nextInt(5); }

} } } Now press Play! You should see a beautiful painting like this:

And with that, our mini-break from 2D game development (Days 9 and 10) is over, and so concludes Unit 2! In the next lessons, we will be applying these techniques to create our levels, and eventually introduce collision detection.

I hope that this lesson made sense to everyone! I know that everyone is at different levels of Java experience and understanding, an balance as to not bore people and to not leave people behind. I might assume that you remember something from a previous lesson forgotten it already. So let me know if you need anything better explained. I'd be glad to do that for you.

And thank you to everyone who liked Kilobolt Studios on Facebook! We have surpassed 1,000 likes and it is all because of your h In the next few months, Kilobolt will expand and offer more tutorials, better games, and a super-cool website. If you would like to hel donating!

Go to Day 9: 2-D Arrays


4 Comments

Continue to U

You might also like