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.
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!
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;
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 StartingClass to 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.
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 StartingClass to 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;
}
}
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.
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?
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:
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;
}
}
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).
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 and bg2. 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.
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 and bg2. 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.
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):

background.png |
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:
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;
}
}
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.
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;
}
}
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. The booleans are there to keep track of movement. Every time that you press a keyboard button, these booleans will change. When you are holding down a keyboard button, a boolean (movingLeft or movingRight) will keep track of that.
For example: if I am holding down the left button (movingLeft == true) and I also press the right button (movingRight == true) and then release either one of those, previously the character would've stopped (as we would've called the stop method); however, now, it will check if either direction is still being held and choose from two possibilities: 1. stop the character if the other direction's button is not down. 2. if the other direction's button is down, move that way.
4. You will get errors in the StartingClass when you replace your Robot.java with the code above. We will fix these soon, so do not panic!
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. The booleans are there to keep track of movement. Every time that you press a keyboard button, these booleans will change. When you are holding down a keyboard button, a boolean (movingLeft or movingRight) will keep track of that.
For example: if I am holding down the left button (movingLeft == true) and I also press the right button (movingRight == true) and then release either one of those, previously the character would've stopped (as we would've called the stop method); however, now, it will check if either direction is still being held and choose from two possibilities: 1. stop the character if the other direction's button is not down. 2. if the other direction's button is down, move that way.
4. You will get errors 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:
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;
}
}
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.
![]()
|
![]()
|
![]()
|
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:
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();
}
}
}
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.
One last thing, add these getters/setters to the end of the class(it might be easier manually):
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.
One last thing, add these getters/setters to the end of the class(it might be easier manually):
public static Background getBg1() {
return bg1;
}
public static Background getBg2() {
return bg2;
}
return bg1;
}
public static Background getBg2() {
return bg2;
}
They will let us retrieve the variables bg1 and bg2 for use in other classes.
and we have the final StartingClass:
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); 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;
}
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(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;
}
}
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); 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;
}
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(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!

kiloboltgameday5.zip |
