Picture
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!
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.

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

Picture
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).

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, - 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;
}
}
}

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:
Picture
character2.png
File Size: 5 kb
File Type: png
Download File

Picture
heliboy3.png
File Size: 10 kb
File Type: png
Download File

Picture
character3.png
File Size: 5 kb
File Type: png
Download File

Picture
heliboy4.png
File Size: 10 kb
File Type: png
Download File

Picture
heliboy2.png
File Size: 10 kb
File Type: png
Download File

Picture
heliboy5.png
File Size: 10 kb
File Type: png
Download File

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;

}

}
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;
}

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

Picture
kiloboltday8.zip
File Size: 828 kb
File Type: zip
Download File

Picture
 


Comments

Matt
11/03/2012 1:48pm

Thanks for the heads up.

Reply
toiin3ron
11/03/2012 3:03pm

Do not worry, waiting patiently and congratulations to your brother.

Reply
11/03/2012 5:22pm

Patience is a virtue bro, we're cool...

Reply
Adrian Shatte
11/03/2012 5:35pm

Thanks James.

Reply
Roshan Karki link
11/03/2012 9:26pm

Congratulations to your brother.

Reply
darryl sorenson
11/03/2012 11:44pm

Thanks for keeping us posted cant wait

Reply
Conor
11/04/2012 8:30am

Congratulations to your brother mate! Loving the tutorial so far, hadn't realised it was a work in progress until I finished yesterday and there was no Day 8! I'll have to pay attention to the timestamps in future. Looking forward to the next release.

Reply
Zac link
11/04/2012 4:18pm

Congratulation to your brother and great tutorial. I'll be waiting on the edge of my seat for Day 8 to be written!

Reply
Pjflo
11/07/2012 5:23am

Great tut again!
Things are really starting to take shape now. Hoping for some kind of AI implementation in the next tutorial..
One quick question... the background image used.. is it royalty free? :P

Reply
James C.
11/07/2012 7:37am

I drew it :P

Reply
DeathRage
11/11/2012 3:00pm

Hey thanks for this great tut!
just please give more explanation for the update method in the animation class

Reply
James C.
11/24/2012 7:58pm

Hey, shoot me an email: jamescho7@kilobolt.com
I will send you an explanation there :)

Reply
Chien-hsueh Huang
11/11/2012 4:00pm

I had put my images into the data folder but even when I clean the project or reopen Eclipse, they do not appear. When I run my application, the frames where the robot is supposed to blink just has nothing. I even tried to drag the image to the folder in Eclipse but it just says "A resource already exists on disk 'blah blah directory/character2.png'."

Reply
Chien-hsueh Huang
11/11/2012 4:49pm

Comment deleted

Reply
Chien-hsueh Huang
11/11/2012 4:51pm

Opps my bad, I mean...
I got it now... I've messed around with it deleting things and stuff. At the end I right clicked and clicked 'refresh' on the data folder in Eclipse and all the images appeared. I'm not totally sure about what happened. :D

Reece
11/20/2012 12:45am

Awesome stuff!

Thank you very much!

Reply
Bogdan
11/24/2012 2:38pm

Hi,

I found a problem in the code which I'm surprised nobody else pointed it out.
In this function:

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++;

}
}
}

In the while loop, you are comparing the new animTime with the endTime of the current frame, which seems to be wrong. You should instead be comparing animTime with the sum of all endTimes of the frames from 0 to current frame. Something like this:


private int getFrameSum(int q) {
int i, s=0;
for (i=0; i<=q; i++)
s += getFrame(i).endTime;
return s;
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currentFrame = 0;

}

while (animTime > getFrameSum(currentFrame)) {
currentFrame++;

}
}
}

Think about this and tell me if you understand what I mean. Your method is wrong, however, for this program it also (accidentally) works only because elapsedTime has a small value and rarely (if ever) jumps more than two frames forward, so one incrementation of currentFrame is enough since that condition is almost always true. (So anyway it doesn't make sense to put a while where an if would suffice)

Please let me know if I misunderstood the idea in the code and if so, explain why your code is correct and mine not. Been thinking about it a lot today and was also amazed why your code does the right thing (proved when I ran the applet) and my code too.

Thanks

Reply
James C.
11/24/2012 7:52pm

Do a System.out.println(getFrame(currentFrame).endTime) inside the while loop. You will see that the endTime already is the aggregate of all previous endTimes (like you have created a function for). endTime is NOT equal to the duration of animation of the current frame. It already sums the previously animated frames' durations.

This is how the addFrame method is written:

public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}

Your code, which I was not able to try, as I do not have Eclipse installed, probably is redundant, adding what is already a sum into another sum.

s += getFrame(i).endTime << already accounted for previous frames.
//s will be multiples of endTime.


Reply
Bogdan
11/25/2012 3:03am

Oops, I hadn't looked carefully to the way the constructor was called. Now it makes sense. Thanks!

Nikolaos
12/02/2012 3:14am

Hello James.
Sorry to take you back to an earlier lesson.I was wondering how you made those pngs' background transparent?

Thank you for your tutorials
Nik

Reply
James C.
12/02/2012 8:41am

PNG files retain alpha values (meaning opacity). To create PNG files with transparent pixels, you have to use a program like Gimp or Photoshop.

Reply
Nikolaos
12/02/2012 1:20pm

Hi again James.
Thanks for the quick reply there.Really appreciate it.Another quick question for you.If you wanted to animate a sequence of images only once and only when a certain event occurred would you do it through the animation class or rather "manualy" by changing the currentSprite "manualy"?

Keep up this great tutorials...
Regards
Nik

James C.
12/02/2012 1:23pm

Sounds to me like either of those methods will work.

You can create an animation for it and start it only when this event occurs.

Rasheed
12/03/2012 6:53am

Hello kilobolt team and thank you for the great tuts.
I seem to have a problem understanding how elapsedTime changes the animation speed.Also is there a way to know exactly how much time an animation will take to complete?Adding the endTime of each frame would be an obvious answer but elapsedTime interferes with that and changes the whole animation time..

need4Sheed

Reply
Muhammad Babar
12/30/2012 10:19am

Dear Rasheed elapsed time doesnt change animation speed it simply keep on incrementing animation time and checks 2 things
1.if(animTime>=totalDuration)
{
animTime=animTime % totalDuration;
currentFrame=0;
}
means if animation time gets greater than total duration which we have setted for animation(e.g hanim.addFrame(heliboy, 100);) so 100 in the case,it sets animtime with 1 or 0 and current frame to 0

2.while(animTime>getFrame(currentFrame).endTime)
{
currentFrame++;
}
so while animation time is greater than your current frame end time its keeps on incrementing the current frame i.e it changes the images to create animation effect
hope you would have understand it now

Reply
M.Babar
12/30/2012 10:38am

Sorry i go a bit wrong there,at point 1.
correction
1..if(animTime>=totalDuration)
{
animTime=animTime % totalDuration;
currentFrame=0;
}
it checks if your animation time becomes greater than the total duration of whole animation,
for example
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);
in this case the total duration will be 800 so in this case it set the current frame with 0 i.e so the animation restart from the begining,and anim time with either 0 or 1,

Muhammad Babar
12/30/2012 10:24am

the speed of animation can be set in your animate() method,
anim.update(int);
hanim.update(int);
the higher the value you provide the rate of changing images(animation) becomes faster

usefull tip:play with the code by changing its values and some other things your will start to understand them,
wish you best of luck in future
Fee ImanAllah

Reply
Chey
12/10/2012 11:33am

First of all...Thank you for the tutorials. This is a lot of fun. Keep up the good work.

I did run into a minor glitch after this tutorial though. When I start the game I notice a flicker in the robot. Seems to happen each time I start the game and only within the first second or two. Have others experienced this?

Reply
James C
12/11/2012 8:37am

Thanks!

The glitch is normal. Don't worry about it for now.

Reply
Alex Sappington
12/13/2012 2:25pm

Great job on the tutorial, I am fourteen years old and trying to advance from Flash Actionscript 3 (a simpler language best suited for browser enviroments ) to a more high level one, and this tutorial is very easy to follow for me.
The only thing that seemed strange is that I am not noticing any difference between the character, character2 and character3 images that you have provided. Is this intentional and will be changed or am I missing somewhere where I need to edit them for a running robot. The Heliboy is working great and the animation looks really good though.

Anyways, great tutorial, I expect to finish all the lessons currently published over the weekend and eagerly await more.

Reply
Ziff
12/27/2012 1:12am

I really like the way you give a general overview of the required components for each part of the project. I usually take that framework and try to put it together myself. Afterwards I check to see if I got it right.

Reply
Muhammad Babar
12/30/2012 8:21am

Hats off to you James,your doing a great work,game development is my passion from the beginning,and now im learning the tricks of the trade,thanks

Reply
Mike T
01/06/2013 8:49pm

Thanks for these tutorials, things are going great. It's inspiring to see some results so quickly even though most of it is cut and paste. While I do understand the end results I'm not sure I could come up with such nicely written code as shown in these tutorials. I'm sure my implementation would be a lot messier and not done in such an efficient manner. I guess that skill will only come in time.

Reply
James C
01/07/2013 7:34am

Mike, you're right. Before you know it, you will be writing your own games without referencing everything you write. But do remember that programming doesn't require you to memorize!

As long as you can understand the code, all you need to do is solve the problem in your head and go about writing that down in programming syntax. Everyone stops to look up lines of code here and there, so don't feel like you far off! :)

Reply
Julien
01/10/2013 10:00pm

Hi James, thanks for all the great work you have put into these tutorials.

I have an issue I was wondering if you had any advice as to debugging.

I have noticed that by the time I got to this tutorial, an had a frame to look at for results instead of just the console, the result from running the code would change almost every time I run it.

It looks like it might keep a thread running after closing the applet.

The following results happen:

A - applet opens, nothing is animated and nothing moves, console still displays the s.out messages. - BUT, if I push restart in the applet menu it "MIGHT" fix every thing...

B - apple does not initialize correctly at all, has incorrect size.

C - works perfectly, but if I push restart in the applet menu it double speed of the animations.

In all cases I get these error message in the console at some point:

Exception in thread "Thread-3" java.lang.NullPointerException
at kiloboltgame.StartingClass.run(StartingClass.java:99)
at java.lang.Thread.run(Unknown Source)

Thanks for any advice!

Reply
Patrick
02/28/2013 2:49am

Hey James, great tutorial. Thanks for putting it together.
I was curious how you would alter the animation class to have the animation only cycle through once, instead of a continuous loop of the frames. I want a sequence to play on a button push, but the animation class above loops the animation, and doesn't always seem to start at the starting frame when called.
I appreciate your help!

Reply
S-Markt
03/05/2013 1:32am

as allways, great tut. now i am new to the whole oop-stuff. i have programmed basic, z80 and 68000 machinecode, but all topdownprogramming so i made typical mistakes in understanding oop. as you mentioned every second lesson, classes are only blueprints for objects and i didn´t had this in mind reading todays tutorial so i thought that all data of each animation would be put into one frame. you do a great job empathize with the ones who read your tuts, so i am a little ashamed for asking to mention in the begining of this lesson that for each character in the game there will be an own object off the animation class. it will help readers like me, to understand the whole lesson easier. again thanks alot for your work. one day somebody will write a song about you, i am sure!

Reply
S-Markt
03/05/2013 1:39am

two more questions:
what does this do? Project >> Clean >> OK!

and

how can i make a copy of the whole project and how can i put it back. i often change the code to try out things and copy and paste parts but i don´t want my original learningcode to get lost. is there an easy way in eclipse to make backups?

Reply
bgp
05/04/2013 5:14am

Hi S-Markt.
1) If you follow Project >> Clean you will see...
Clean will resolve the build problems and built states. The projects will be rebuilt from scratch. So I guess it eliminates a lot of "my source is not working" comments.
2) I make a copy of each day's source - by adding the files in the workspace to a zip file. I'm not sure if this is the best way to create a backup. I never tried to restore. Maybe somebody has a better idea... ???

Reply
Donkeyduck
03/20/2013 7:16am

Thanks for the tutorials! Maybe you can help me? I want to animate my character so that when I push left key the character makes walking animation. I do understand the basic animation logics but can't figure it out with the "sprites". Do I have to change something in public void run() and case KeyEvent.VK_RIGHT ? I have imported new png-s that I made myself.
Thanks!


Reply
KH
03/30/2013 8:41am

I had this problem too. You can have different pictures for different keys pressed by making booleans for when different things were pressed (change values of booleans inside keyPressed and keyReleased) and then using a bunch of if then statements. Probably not the best way, but it worked.

Reply
bob
04/06/2013 4:28am

hey james thnx 4 ur tuts m loving it!!can u pls expln wt does ds statmnt means-ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized??

Reply
molz
04/11/2013 5:10am

Hi I am confused with creating the framework package. is the framework package supposed to be in the kiloboltgame package? Or is it a different package like data? Because I keep trying to create a new package named "kiloboltgame.framework", eclipse keeps creating a framework package inside the existing kiloboltgame.

Reply
Vade
05/16/2013 5:48am

Hello! Great tut! Wonder if u could chew it to me:
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
Why do we sum here? Why it isn't just "totalDuration = duration;"

Reply
Maique
05/20/2013 9:30pm

Nice tutorial bro! but i have a little doubt about this part...

public synchronized void update(long elapsedTime){
if(frames.size() > 1){
animTime += elapsedTime;
System.out.println("Tamanho do ANIMTIME do objeto "+Thread.currentThread().getId()+": "+animTime);
if(animTime >= totalDuration){
animTime = animTime % totalDuration;
currentFrame = 0;
}

while(animTime > getFrame(currentFrame).endTime){
currentFrame++;
}
}
}

more specifically in the while statement..... because it produced a infinity loop.. the question is : how did it went out of the infinity loop if we do not incrementing ou decrement the "animTime?

Reply
Maique
05/20/2013 9:34pm

*Observation...

this line:

System.out.println("Tamanho do ANIMTIME do objeto "+Thread.currentThread().getId()+": "+animTime);

was only to do some testing... and sorry by the english, i'm brazilian and i dont speak and dont write very well.. but i try... heheh

Reply
Maique
05/20/2013 9:34pm

*Observation...

this line:

System.out.println("Tamanho do ANIMTIME do objeto "+Thread.currentThread().getId()+": "+animTime);

was only to do some testing... and sorry by the english, i'm brazilian and i dont speak and dont write very well.. but i try... heheh

Reply
Maique
05/20/2013 9:35pm

*Observation...

this line:

System.out.println("Tamanho do ANIMTIME do objeto "+Thread.currentThread().getId()+": "+animTime);

was only to do some testing... and sorry by the english, i'm brazilian and i dont speak and dont write very well.. but i try... heheh

Reply
Eliot
05/31/2013 11:07pm

I'm a bit confused on the animation speed. Basically you have the StartingClass.animate(); method with the values. That is put into the Animation.update() method.

Taking Heliboy for example at object initialization:
totalDuration == 800
animTime == 0
elapsedTime == 50

The next frame
animTime == 50

Then:
animTime == 100

etc...

That update happens every 17ms. I don't get how changing hanim.update(50); to hanim.update(75); speeds up the animation if it is updating every 17ms. To me it seems like it would be updating every 17ms regardless of the elapsed time value. Can you explain why that is happening please?

Reply
rwtwm
07/11/2013 3:06pm

Apologies for the revival of a dead thread, but I've been bouncing this around in my head for the last 5 minutes and worked it out. I thought I'd post in case someone else had the same problem.

The clue is in the fact that LARGER values increase the speed of updating. The animation class doesn't actually contain a timer instance, so a larger number pushed to the animation's update method tells the instance 'hanim' that more 'time' has elapsed since the last update.
Look at the first line in the update() method. The 'time' provided moves the animation's internal clock along by a larger amount.

That said, I have a question myself. It appears we have parameterised the animation speed in two different places, once when inputting the individual frame durations, and once more during the update function. Why would we not just use an increment in the update function and scale our frame length accordingly?

Thanks for an awesome tutorial series. I'll be sure to credit and link here when I release a game of my own!

Reply
moshidak
06/05/2013 6:07am

Hi, I'm very interested with this awesome tutorial. It will be great if the heli boy can move randomly to attack the main character, but I have no idea how. Can you give suggestion.

Reply
Matti
06/16/2013 8:32am

Awesome tutorial so far, looking forward to whats coming as i progress, thank you!

Then, a small detail i'm struggling with:
Earlier in the tutorial you said paint()-method draws the items in the order you write them. But now with the animation, projectiles are left behind the enemies. Whats causing this? I know it wont matter when we actually get to collision detection and killing the enemies, but i would like to understand why is it happening.
Thanks in advance.

Reply
Matti
06/16/2013 8:41am

Nevermind.. I managed to change the order i had earlier. Still obeys the order :)

Reply
Anum
06/17/2013 4:05am

Your tutorial series is just awesome.... I loved it :) Please can u explain me why u passed totalDuration instead of duration in addFrame method? isn't the endTime should be the particular assigned time? Thanks.

public synchronized void addFrame(Image image, long duration)
{
totalDuration += duration;
frames.add(new AnimFrame(image,totalDuration));
}

Reply
Ameer
06/30/2013 9:12pm

How do you make art for games?

Reply
Murtuza link
07/13/2013 6:01am

In the final code of this page, there are two extra braces which caused errors. I removed them and it worked. Can you consider removing it from the above code so that other people don't get confused. :)
By the way YOU Sir are awesome!

Reply
Raj
07/17/2013 11:28pm

Thanks for the amazing tutorial. It is extremely well explained.
I have one doubt though. There is no condition in StartingClass.java to check if the robot is ducked and if so, set currentSprite as characterDown. May be I am missing a point here.

However I did that myself in the code, but when I long-press the down arrow key, it produces flickering on the screen as the scene is repainted.

Any idea how to resolve this?

Reply
Joachim
08/05/2013 6:31am

Mr cho..lets say i wanted the robot's legs to move and i had a sequence of images showing the robot's movement..how would i do that?

Reply
Timo
08/09/2013 9:08pm

Hi!

I'm wondering how calling

repaint()

calls the
paint()

method.

Reply
Tom link
08/18/2013 8:52pm

lol. He omitted the explanation for the animation code because he doesn't understand it. :)

Reply
Bilal
08/25/2013 2:20am

Hey I tried the tutorial and it worked great. I only have one problem. When the robot is animated it flickers rather than a smooth animation. How can I fix that ?

Reply
SThiongane
08/26/2013 12:29pm

Great tutorial ! and you explain very well. i will come back with a lot of questions ! thanks

Reply
SThiongane
08/27/2013 1:26am

Hi James. I have noticed a little "bug" in the app and i tried to correct it. let me know if i am wrong !

I noticed that if i hold on the down button and the space button, the Robot is ducked first, then jumping. However when i release the space button (stop jumping), the drawn image is the "jumped" png.

I modified the code in the run method like this and that works nice :
if (robot.isJumped()) {
currentSprite = characterJumped;
} else if(robot.isDucked()) {
currentSprite = characterDown;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}

Reply
SThiongane
08/27/2013 1:26am

Hi James. I have noticed a little "bug" in the app and i tried to correct it. let me know if i am wrong !

I noticed that if i hold on the down button and the space button, the Robot is ducked first, then jumping. However when i release the space button (stop jumping), the drawn image is the "jumped" png.

I modified the code in the run method like this and that works nice :

if (robot.isJumped()) {
currentSprite = characterJumped;
} else if(robot.isDucked()) {
currentSprite = characterDown;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}

Reply
SThiongane
08/27/2013 1:28am

Hi James. I have noticed a little "bug" in the app and i tried to correct it. let me know if i am wrong !

I noticed that if i hold on the down button and the space button, the Robot is ducked first, then jumping. However when i release the space button (stop jumping), the drawn image is the "jumped" png.

I modified the code in the run method like this and that works nice :

Reply
SThiongane
08/27/2013 1:29am

Hi James. I have noticed a little "bug" in the app and i tried to correct it. let me know if i am wrong !

I noticed that if i hold on the down button and the space button, the Robot is ducked first, then jumping. However when i release the space button (stop jumping), the drawn image is the "jumped" png.

I modified the code in the run method like this and that works nice :

if (robot.isJumped()) {
currentSprite = characterJumped;
} else if(robot.isDucked()) {
currentSprite = characterDown;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}

Reply
Asfak Khan
09/06/2013 11:43am

i hv lots of errors in this code....plz help..

Reply
Hal
09/26/2013 1:05am

Thanks for the great tutorials! Kudos!

Reply
patrick d.
09/29/2013 8:16pm

it doesnt seem to run for me? wondering maybe if i skipped a step? i just get a black applet screen appear? any help? thanks in advance

Reply
Ej E.
10/02/2013 7:59am

Hi! Great tut! Been studying this for a while. Getting confused over the update of the animation class? Can you like provide a little more explanation regarding that part please? Thanks! Appreciate it very much. :)

Reply
Alex
10/05/2013 11:10pm

As the time getting passed, the tutorials are getting worst. Not because they are complex but you are not explaining them well. Writing the codes only are not enough. And because of this, the only thing that is driving me crazy is that you are asking donation. lol
.
Still 'm gonna complete all the tutorials any how cz I have spent hell lot of times on this site, can't just waste it by leaving the tutorials in middle.

Reply
Mikkie
12/11/2013 4:21am

Hi!
I'm trying to understand this animation class update method. I want to change the animation so when it reaches the final picture it stops. Now it's infinite loop and starts all over again.

I was wondering if I can use this animation class for animate explosions. Or do I need to make a whole new class with different kind of methods and constructors.

Thanks!

Reply
Jermaine
01/11/2014 9:15pm

Completely incomprehensible

Reply
Jermaine
01/11/2014 9:16pm

This lesson was completely incomprehensible

Reply
Waleed Ahmad Khan link
01/24/2014 1:33pm

here is a problem....

Exception in thread "Thread-4" java.lang.ArithmeticException: / by zero
at PkSoldierBattles.framework.Animation.update(Animation.java:39)
at PkSoldierBattle.FirstClass.animate(FirstClass.java:172)
at PkSoldierBattle.FirstClass.run(FirstClass.java:155)
at java.lang.Thread.run(Unknown Source)

Reply



Leave a Reply

    Author

    James Cho is the lead developer at Kilobolt Studios. He is a college student at Duke University and loves soccer, music, and sharing knowledge.