2D Animations (with Swing and AWT)

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

Completed