Welcome to Day 8! It's been a while since the last update, and I hope this lesson will be both fun and informative. Today, we are going to be covering animations, which a lot of you have been asking for via email.
Theoretically, animations are simple. All you need to do is store a sequence of images (that we will refer to as frames) and the duration that each image will be displayed for. Next you just need to display the current frame in the sequence for the duration specified.
In practice, it requires a bit of work. So get ready to start thinking, and let's begin!
Theoretically, animations are simple. All you need to do is store a sequence of images (that we will refer to as frames) and the duration that each image will be displayed for. Next you just need to display the current frame in the sequence for the duration specified.
In practice, it requires a bit of work. So get ready to start thinking, and let's begin!
Lesson #2-24: Creating the Animation Class
As always, we will begin by creating a blueprint for all animations. This class will be different from those you have seen before (you will encounter a nested class!).
Note: This Animation class can be used over and over again in other projects (with certain modifications). That is why we label it a framework because it is basically reusable code that can help us in other games in the future.
Note 2: This Animation class is a simplified version of source code found in the book Developing Games in Java by David Brakeen. This book is quite technical and will probably be difficult for you right now, but after you've made a few games and know how everything works inside and out, I would suggest checking it out! The only downside is that the book is pretty old, and it focuses solely on full-screen Desktop games, which is not as open to indie developers as the mobile scene.
Note: This Animation class can be used over and over again in other projects (with certain modifications). That is why we label it a framework because it is basically reusable code that can help us in other games in the future.
Note 2: This Animation class is a simplified version of source code found in the book Developing Games in Java by David Brakeen. This book is quite technical and will probably be difficult for you right now, but after you've made a few games and know how everything works inside and out, I would suggest checking it out! The only downside is that the book is pretty old, and it focuses solely on full-screen Desktop games, which is not as open to indie developers as the mobile scene.
I. Creating a Package and animation class
We begin by creating a new package (this helps us keep our game classes and game framework classes separated).
1. Right click on the src folder of your project >> New >> Package.
2. Name it: kiloboltgame.framework.
1. Right click on the src folder of your project >> New >> Package.
2. Name it: kiloboltgame.framework.
3. Right click on the framework and create a new class named Animation.
Now let's talk about what we need to do in this class.
1. We will need variables that keep track of: frames, current frame, animation time, and total time.
2. First we must create a constructor, as always.
3. We need the following methods:
- One that will add a frame to a new animation.
- One that will update the current frame with the appropriate image.
- One that returns the current frame's image, so that we can paint it in the StartingClass.
- One that returns the current frame's index (index being the numerical location on a list of objects).
4. We need to create a nested inner-class called an AnimFrame that will be used to create objects that contain the current image and the duration that this image will be displayed. These AnimFrame objects will populate the ArrayList whose values we use to draw the object to the screen.
I will talk about each of these as we implement them, so let's just dive in (in the order listed).
Now let's talk about what we need to do in this class.
1. We will need variables that keep track of: frames, current frame, animation time, and total time.
2. First we must create a constructor, as always.
3. We need the following methods:
- One that will add a frame to a new animation.
- One that will update the current frame with the appropriate image.
- One that returns the current frame's image, so that we can paint it in the StartingClass.
- One that returns the current frame's index (index being the numerical location on a list of objects).
4. We need to create a nested inner-class called an AnimFrame that will be used to create objects that contain the current image and the duration that this image will be displayed. These AnimFrame objects will populate the ArrayList whose values we use to draw the object to the screen.
I will talk about each of these as we implement them, so let's just dive in (in the order listed).
II. Defining the Animation Class
1. Variables
Right now, you should just have an empty class.
Create the following variables:
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime; //long takes up more memory than int but can hold more accurate numbers.
private long totalDuration;
}
- The ArrayList frames will contain AnimFrame objects (to be created later) that have two values: an image and a duration it is displayed.
- currentFrame refers to the integer value index (the numerical location in the list starting with 0) of the current frame in the ArrayList (the current image shown).
- The third variable animTime will keep track of how much time has elapsed since the current image was displayed, so that when animTime reaches a certain value, we can switch over to the next frame.
- The last variable, totalDuration, refers to the amount of time that each frame (image) will be displayed for.
Let me illustrate these variables with a simplified example:
Assume that we have an ArrayList called frames, and it contains the following:
AnimFrame(image1, 100);
AnimFrame(image2, 200);
AnimFrame(image3, 400);
AnimFrame(image2, 200);
AnimFrame(image1,100);
- This group of AnimFrame objects is what we call the frame ArrayList.
- currentFrame, then, would be the index of the current image displayed. So if image3 is the current frame, the currentFrame value would be 2 (because it goes 0 - image1, 1 - image2, 2 - image3).
- animTime will then measure the amount of time for which image3 has been displayed. When animTime exceeds the totalDuration of 400, it will move onto the next AnimFrame: the fourth line in the example above.
Now that we got the variables taken care of, let's move on to the constructor.
2. Constructor
The constructor is simple. It will initialize the four variables that we have just created.
Place the following code in bold below the "private long totalDuration;" statement.
...
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
}
- The only thing you probably haven't seen before is synchronized. To understand this, we must first talk about Threads. You see, when we have multiple threads, they don't technically run simultaneously like I've mentioned in the previous lessons. They just switch back and forth (think of it as a switch. It can only be on one side at once).
In the above example, if animTime and currentFrame are synchronized, the two will always be called sequentially (together). If it is not synchronized, animTime = 0 might be called first, another thread will then taking over and run its statements, and then finally currentFrame = 0 will be called. This is a bit technical and you probably won't see the significance until later, so for now, just think of this as making things easier in the future.
3. Methods
We will talk about methods one at a time. As you add these methods, you will have a lot of errors until you finish implementing each one, so just ignore them until the class is complete.
I. addFrame method: This method will add take an AnimFrame object and "append" it to the ArrayList called frames.
It will look like this:
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
Synchronized comes up again here. It serves the same purpose. This code would be equivalent if synchronized was taken out and placed inside the method like it is in the constructor.
II. update method: The update method will be called repeatedly, and will switch frames as necessary.
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
As practice, you should logically try to understand what is going on here. Hint: elapsedTime is the amount of time that passed since the last update.
III. getImage method: This method will return the image that belongs to the currentFrame.
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
You might notice that this method does not have the void keyword, but instead, image. This basically means that this method will return a value to the statement that calls it.
For example, if I say:
System.out.println(getImage());
And the method had a keyword void, the method will return no value, and nothing will print.
But in this case, it will print the ID of the Image object that we return in the if and else statements.
IV. getFrame method: Finally, we create a method that returns the current AnimFrame of the animation sequence:
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
4. The Nested AnimFrame Class:
We mentioned AnimFrame objects quite a bit, but we never created a class to create them. We could create a whole new class, but it is easier and more readable if we place this inside the Animation class (which will be the only class that makes objects using it). This is called nesting.
So as you would if you were creating a method, place this code inside your class:
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
Remember to import everything (Ctrl + Shift + O), and your class should be finished.
This is what it should look like now:
Right now, you should just have an empty class.
Create the following variables:
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime; //long takes up more memory than int but can hold more accurate numbers.
private long totalDuration;
}
- The ArrayList frames will contain AnimFrame objects (to be created later) that have two values: an image and a duration it is displayed.
- currentFrame refers to the integer value index (the numerical location in the list starting with 0) of the current frame in the ArrayList (the current image shown).
- The third variable animTime will keep track of how much time has elapsed since the current image was displayed, so that when animTime reaches a certain value, we can switch over to the next frame.
- The last variable, totalDuration, refers to the amount of time that each frame (image) will be displayed for.
Let me illustrate these variables with a simplified example:
Assume that we have an ArrayList called frames, and it contains the following:
AnimFrame(image1, 100);
AnimFrame(image2, 200);
AnimFrame(image3, 400);
AnimFrame(image2, 200);
AnimFrame(image1,100);
- This group of AnimFrame objects is what we call the frame ArrayList.
- currentFrame, then, would be the index of the current image displayed. So if image3 is the current frame, the currentFrame value would be 2 (because it goes 0 - image1, 1 - image2, 2 - image3).
- animTime will then measure the amount of time for which image3 has been displayed. When animTime exceeds the totalDuration of 400, it will move onto the next AnimFrame: the fourth line in the example above.
Now that we got the variables taken care of, let's move on to the constructor.
2. Constructor
The constructor is simple. It will initialize the four variables that we have just created.
Place the following code in bold below the "private long totalDuration;" statement.
...
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
}
- The only thing you probably haven't seen before is synchronized. To understand this, we must first talk about Threads. You see, when we have multiple threads, they don't technically run simultaneously like I've mentioned in the previous lessons. They just switch back and forth (think of it as a switch. It can only be on one side at once).
In the above example, if animTime and currentFrame are synchronized, the two will always be called sequentially (together). If it is not synchronized, animTime = 0 might be called first, another thread will then taking over and run its statements, and then finally currentFrame = 0 will be called. This is a bit technical and you probably won't see the significance until later, so for now, just think of this as making things easier in the future.
3. Methods
We will talk about methods one at a time. As you add these methods, you will have a lot of errors until you finish implementing each one, so just ignore them until the class is complete.
I. addFrame method: This method will add take an AnimFrame object and "append" it to the ArrayList called frames.
It will look like this:
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
Synchronized comes up again here. It serves the same purpose. This code would be equivalent if synchronized was taken out and placed inside the method like it is in the constructor.
II. update method: The update method will be called repeatedly, and will switch frames as necessary.
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
As practice, you should logically try to understand what is going on here. Hint: elapsedTime is the amount of time that passed since the last update.
III. getImage method: This method will return the image that belongs to the currentFrame.
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
You might notice that this method does not have the void keyword, but instead, image. This basically means that this method will return a value to the statement that calls it.
For example, if I say:
System.out.println(getImage());
And the method had a keyword void, the method will return no value, and nothing will print.
But in this case, it will print the ID of the Image object that we return in the if and else statements.
IV. getFrame method: Finally, we create a method that returns the current AnimFrame of the animation sequence:
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
4. The Nested AnimFrame Class:
We mentioned AnimFrame objects quite a bit, but we never created a class to create them. We could create a whole new class, but it is easier and more readable if we place this inside the Animation class (which will be the only class that makes objects using it). This is called nesting.
So as you would if you were creating a method, place this code inside your class:
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
Remember to import everything (Ctrl + Shift + O), and your class should be finished.
This is what it should look like now:
FIGURE 2-32: Animation Class
package kiloboltgame.framework;
import java.awt.Image;
import java.util.ArrayList;
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime;
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
}
import java.awt.Image;
import java.util.ArrayList;
public class Animation {
private ArrayList frames;
private int currentFrame;
private long animTime;
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
synchronized (this) {
animTime = 0;
currentFrame = 0;
}
}
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;
}
while (animTime > getFrame(currentFrame).endTime) {
currentFrame++;
}
}
}
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
} else {
return getFrame(currentFrame).image;
}
}
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
}
Phew. That was a lot of work. Now let's put this to use! (That will help you understand the Animation class better).
Lesson #2-25: Animating Our Characters
Download the following images (please excuse my programmer art) and place them in the data folder:
![]()
![]()
|
![]()
![]()
|
![]()
![]()
|
Then do: Project >> Clean >> OK!
We will now work inside the StartingClass.
To use the Animation class, since it is in a whole new package, we must import it like so:
import kiloboltgame.framework.Animation;
This is one of those moments when everything makes more sense. You've been importing without knowing what is really going on, but now that you have imported your own class for once, you can now understand how it really works! :D
1. Update your Image declarations (to reflect the downloaded images):
private Image image, currentSprite, character, character2, character3, characterDown,
characterJumped, background, heliboy, heliboy2, heliboy3, heliboy4, heliboy5;
2. Create the following Animation objects. Anim will be used to animate the main character and hanim to animate the heliboys.
private Animation anim, hanim;
3. Make the following changes in bold to the init() method (defining the new Image variables). You will also notice the anim.addFrame statements. These create new frames for the anim object with an image and a duration!
@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");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");
background = getImage(base, "data/background.png");
anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);
hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);
currentSprite = anim.getImage();
}
5. Make the following changes in bold/red to the run() class. anim.getImage() retrieves the image associated with the current frame in the animation sequence. The currentSprite is updated so that it now has the value of this image:
@Override
public void run() {
while (true) {
robot.update();
if (robot.isJumped()) {
currentSprite = characterJumped;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}
//Bullets
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);
}
}
//Update Objects
hb.update();
hb2.update();
bg1.update();
bg2.update();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6. Create a new method called animate. The parameters (adjust as necessary) make it easy to change how quickly you animate. The higher the value, the faster each frame changes:
public void animate() {
anim.update(10);
hanim.update(50);
}
7. Within the paint() class, make the following changes in bold. hanim.getImage() retrieves the current frame's image for heliboy, and these bolded statements paint the heliboy with the current image:
@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(hanim.getImage(), hb.getCenterX() - 48, hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), hb2.getCenterX() - 48, hb2.getCenterY() - 48, this);
}
8. Finally, within the keyReleased() method, make the following changes in bold (when down button is released, it shows the current frame):
@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 = anim.getImage();
robot.setDucked(false);
break;
case KeyEvent.VK_LEFT:
robot.stopLeft();
break;
case KeyEvent.VK_RIGHT:
robot.stopRight();
break;
case KeyEvent.VK_SPACE:
break;
case KeyEvent.VK_CONTROL:
robot.setReadyToShoot(true);
break;
}
}
We will now work inside the StartingClass.
To use the Animation class, since it is in a whole new package, we must import it like so:
import kiloboltgame.framework.Animation;
This is one of those moments when everything makes more sense. You've been importing without knowing what is really going on, but now that you have imported your own class for once, you can now understand how it really works! :D
1. Update your Image declarations (to reflect the downloaded images):
private Image image, currentSprite, character, character2, character3, characterDown,
characterJumped, background, heliboy, heliboy2, heliboy3, heliboy4, heliboy5;
2. Create the following Animation objects. Anim will be used to animate the main character and hanim to animate the heliboys.
private Animation anim, hanim;
3. Make the following changes in bold to the init() method (defining the new Image variables). You will also notice the anim.addFrame statements. These create new frames for the anim object with an image and a duration!
@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");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");
background = getImage(base, "data/background.png");
anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);
hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);
currentSprite = anim.getImage();
}
5. Make the following changes in bold/red to the run() class. anim.getImage() retrieves the image associated with the current frame in the animation sequence. The currentSprite is updated so that it now has the value of this image:
@Override
public void run() {
while (true) {
robot.update();
if (robot.isJumped()) {
currentSprite = characterJumped;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}
//Bullets
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);
}
}
//Update Objects
hb.update();
hb2.update();
bg1.update();
bg2.update();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6. Create a new method called animate. The parameters (adjust as necessary) make it easy to change how quickly you animate. The higher the value, the faster each frame changes:
public void animate() {
anim.update(10);
hanim.update(50);
}
7. Within the paint() class, make the following changes in bold. hanim.getImage() retrieves the current frame's image for heliboy, and these bolded statements paint the heliboy with the current image:
@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(hanim.getImage(), hb.getCenterX() - 48, hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), hb2.getCenterX() - 48, hb2.getCenterY() - 48, this);
}
8. Finally, within the keyReleased() method, make the following changes in bold (when down button is released, it shows the current frame):
@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 = anim.getImage();
robot.setDucked(false);
break;
case KeyEvent.VK_LEFT:
robot.stopLeft();
break;
case KeyEvent.VK_RIGHT:
robot.stopRight();
break;
case KeyEvent.VK_SPACE:
break;
case KeyEvent.VK_CONTROL:
robot.setReadyToShoot(true);
break;
}
}
And that's it! Your StartingClass will now look like this:
Figure 2-33: StartingClass, end of Day 8
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;
import kiloboltgame.framework.Animation;
public class StartingClass extends Applet implements Runnable, KeyListener {
private Robot robot;
private Heliboy hb, hb2;
private Image image, currentSprite, character, character2, character3,
characterDown, characterJumped, background, heliboy, heliboy2,
heliboy3, heliboy4, heliboy5;
private Graphics second;
private URL base;
private static Background bg1, bg2;
private Animation anim, hanim;
@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");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");
background = getImage(base, "data/background.png");
anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);
hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);
currentSprite = anim.getImage();
}
@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 = anim.getImage();
}
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();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void animate() {
anim.update(10);
hanim.update(50);
}
@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(hanim.getImage(), hb.getCenterX() - 48, hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), 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 = anim.getImage();
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;
import java.util.ArrayList;
import kiloboltgame.framework.Animation;
public class StartingClass extends Applet implements Runnable, KeyListener {
private Robot robot;
private Heliboy hb, hb2;
private Image image, currentSprite, character, character2, character3,
characterDown, characterJumped, background, heliboy, heliboy2,
heliboy3, heliboy4, heliboy5;
private Graphics second;
private URL base;
private static Background bg1, bg2;
private Animation anim, hanim;
@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");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");
background = getImage(base, "data/background.png");
anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);
hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);
currentSprite = anim.getImage();
}
@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 = anim.getImage();
}
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();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void animate() {
anim.update(10);
hanim.update(50);
}
@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(hanim.getImage(), hb.getCenterX() - 48, hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), 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 = anim.getImage();
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;
}
}
I covered a lot of code in this lesson. If you need more explanation on anything, let me know! I will update the guide.
Also please examine this code! By this time, you should have a good understanding of how each method in the StartingClass works. So start with the init() and start() methods and try to follow the code as it would run. It will help you a lot. :)
I intentionally omitted explanation on certain things (such as the order of animation frames) because I thought it would be best to let you learn by running the code. So run it, play with it and mess with it until you understand! If you have any questions after that, you know I'm here.
Thank you for reading, and thank you so much for your patience and kind words! :)
The wedding went really well!
I hope you have a good few days, and we will talk about more fun stuff in a few days.
Also please examine this code! By this time, you should have a good understanding of how each method in the StartingClass works. So start with the init() and start() methods and try to follow the code as it would run. It will help you a lot. :)
I intentionally omitted explanation on certain things (such as the order of animation frames) because I thought it would be best to let you learn by running the code. So run it, play with it and mess with it until you understand! If you have any questions after that, you know I'm here.
Thank you for reading, and thank you so much for your patience and kind words! :)
The wedding went really well!
I hope you have a good few days, and we will talk about more fun stuff in a few days.

kiloboltday8.zip |
