Define the Panel
public class MyAnimationPanel extends JPanel implements ActionListener {
public int x = 0;
public Image sprite;
public MyAnimationPanel () {
Timer timer = new Timer (100, this); // To activate the actionPerformed method every 100 milliseconds
timer.start();
this.setBackground(Color.blue);
this.sprite = new ImageIcon("myImage.png").getImage(); // Assign the image to be displayed
}
@Override
public void actionPerformed (ActionEvent event) { // Needs to implement ActionListener for this, executes when timer ticks
x+=1; // Change x, which we will use to set the position of the image
repaint(); // This calls paint function, preferable when repeatedly called
}
public void paint (Graphics gra) {
Graphics2D g = (Graphics2D) gra; // Cast to child class, which has more methods
super.paint(g); // Display background
g.drawImage(sprite,x,10,null); // Display image at position x, which increases at every step
}
}
- You will need to import the following from java.awt: Color, Graphics, Graphics2D, Image, event.ActionEvent, event.ActionListener; and from javax.swing: ImageIcon, Timer, JPanel
- This code would require the project folder to contain an image called myImage.png, of course you can replace that call with a filepath to your image
- You can change the parameter in the Timer constructor to a lower number if you want it to have a faster frame rate
- This code increments the value of x for every frame, so that it looks like the image is moving. Of course you can adjust it to change the position in a different way
Set up the display window
MyAnimationPanel myPanel = new MyAnimationPanel ();
myPanel.setPreferredSize( new Dimension(400,400) );
JFrame myFrame = new JFrame();
myFrame.add(myPanel);
myFrame.pack();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setLocationRelativeTo(null);
- You will need to import java.awt.Dimension and javax.swing.JFrame
- This is set up the same as in the graphics lesson, except that the panel class is defined separately
Sprite Animations
public class MySpriteAnimationPanel extends JPanel implements ActionListener {
private BufferedImage spriteSheet; // BufferedImage is used so you can access a part of the image at a time
private int frameWidth, frameHeight, frameCount, frameIndex;
public MySpriteAnimationPanel () {
try {
Timer timer = new Timer(100, this); // To activate the actionPerformed method every 100 milliseconds
timer.start();
this.setBackground(Color.blue);
this.spriteSheet = ImageIO.read(
new File("demoSprite.png")
);
this.frameWidth = 64; // Width of each sprite frame/image in the sprite sheet
this.frameHeight = 64; // Height of each sprite frame/image in the sprite sheet
this.frameCount = 4; // The number of frames/images in the sprite sheet
this.frameIndex = 0; // To keep track of which frame of the animation is in focus
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed (ActionEvent event) { // Needs to implement ActionListener for this, executes when timer ticks
frameIndex++; // To focus the viewer on next frame in spriteSheet
if (frameIndex >= frameCount) frameIndex = 0; // Restart at the first frame if past the last one
repaint();
}
public void paint (Graphics gra) {
Graphics2D g = (Graphics2D) gra;
super.paint(g);
BufferedImage currentFrame = spriteSheet.getSubimage(
frameWidth*frameIndex, // X coordinate of top left corner of frame viewer on image (measured from the top left of the image)
0, // Y coordinate of top left corner of frame viewer on image
frameWidth,
frameHeight
);
g.drawImage(currentFrame,10,20,null);
}
}
- You will need to import java.awt.image.BufferedImage, java.io.File, and javax.imageio.ImageIO for the new classes used here
- This class is accessed the same as was the MyAnimationPanel class for a simple animation, packing it into a JFrame
- For this code demo to work, you will need a sprite sheet file called 'demoSprite.png' on which is drawn four 64x64 pixel images in a row (a total width of 256px)
- Instead of only moving an image's position, this code cycles through frames of a sprite sheet to animate the object
- The frameIndex variable cycles from 0-3, so the top left x coordinate of the viewer frame cycles between 0, 64, 128, and 192
Object-oriented Sprite Animations
public class Sprite {
private BufferedImage spriteSheetImage;
private int frameWidth, frameHeight, frameCount, frameIndex, x, y;
public Sprite (BufferedImage spriteSheetImage, int frameWidth, int frameHeight, int frameCount) {
try {
this.spriteSheetImage = spriteSheetImage;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
this.frameCount = frameCount;
this.frameIndex = 0;
}
catch (Exception e) {
e.printStackTrace();
}
}
public void update() {
frameIndex += 1;
if (frameIndex == frameCount) frameIndex = 0;
}
public BufferedImage getFrame() {
return spriteSheetImage.getSubimage( frameWidth*frameIndex, 0, frameWidth, frameHeight);
}
}
public class AnimatableObject {
private Sprite sprite;
private int x = 0;
private int y = 0;
private int xVelocity = 0;
private int yVelocity = 0;
public AnimatableObject(Sprite sprite, int x, int y) {
this.sprite = sprite;
this.x = x;
this.y = y;
this.xVelocity = 1 + (int)(Math.random()*4);
this.yVelocity = 1 + (int)(Math.random()*4);
}
public void update () {
this.sprite.update();
this.x += xVelocity;
this.y += yVelocity;
}
public int getX() { return this.x; }
public int getY() { return this.y; }
public void setXVel(int setTo) { this.xVelocity = setTo; }
public void setYVel(int setTo) { this.yVelocity = setTo; }
public Sprite getSprite() { return this.sprite; }
}
public class MyAnimatablePanel extends JPanel implements ActionListener {
private ArrayList<AnimatableObject> objects; // Stores objects to be updated and displayed
public MyAnimatablePanel(ArrayList<AnimatableObject> objects) {
this.objects = objects;
try {
this.setBackground(Color.blue);
Timer timer = new Timer (100, this);
timer.start();
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed (ActionEvent event) {
// Update the objects (position and sprite frame)
for (AnimatableObject obj : objects) {
obj.update();
}
repaint();
}
public void paint (Graphics gra) {
Graphics2D g = (Graphics2D) gra;
super.paint(g);
for (AnimatableObject obj : objects) {
g.drawImage(obj.getSprite().getFrame(), obj.getX(), obj.getY(), null);
}
}
}
try {
BufferedImage spriteSheetImage = ImageIO.read(new File("demoSprite.png"));
// Make objects designed to be displayed by our panel
ArrayList<AnimatableObject> objects = new ArrayList<AnimatableObject>();
Sprite sprite = new Sprite(spriteSheetImage,64,64,4);
objects.add( new AnimatableObject(sprite,40,30));
objects.add( new AnimatableObject(sprite,140,230));
MyAnimatablePanel myPanel = new MyAnimatablePanel (objects);
myPanel.setPreferredSize( new Dimension(400,400) );
JFrame myFrame = new JFrame();
myFrame.add(myPanel);
myFrame.pack();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setLocationRelativeTo(null);
}
catch (Exception e) {
e.printStackTrace();
}
- See non-object-oriented animation instructions above for imports
- The benefit of making it object oriented is that you can then add each new animated object with a single line of code