Pacman open/close mouth animation
Solution 1:
Something like this might work for PacMan images. It uses a Java 2D based Shape
instance to represent the form, and an AffineTransform
to produce the different orientations.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.*;
import javax.imageio.ImageIO;
class PacManShape {
private double size;
private double rotation;
final int maxSize = 4;
static File home = new File(System.getProperty("user.home"));
static File images = new File(home, "images");
PacManShape(int size, double rotation) {
this.size = size;
this.rotation = rotation;
}
public Area getPacManShape(double jaws) {
Area area = new Area(new Ellipse2D.Double(0d, 0d, size, size));
double x1 = size / 2 + (2d * size * Math.cos(jaws / 2d));
double y1 = size / 2 + (2d * size * Math.sin(jaws / 2d));
double x2 = x1;
double y2 = size / 2 - (2d * size * Math.sin(jaws / 2d));
Polygon mouth = new Polygon();
mouth.addPoint((int) (size / 2), (int) (size / 2));
mouth.addPoint((int) x1, (int) y1);
mouth.addPoint((int) x2, (int) y2);
mouth.addPoint((int) (size / 2), (int) (size / 2));
area.subtract(new Area(mouth));
return area;
}
public BufferedImage getPacManImage(double angle, Color color) {
BufferedImage bi = new BufferedImage(
(int) size, (int) size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(color);
g2.fillRect(0, 0, (int) size, (int) size);
AffineTransform rotate = AffineTransform.getRotateInstance(
rotation, size / 2, size / 2);
g2.setTransform(rotate);
Area pacMan = getPacManShape(angle);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.YELLOW);
float[] dist = {.15f, .9f};
Color[] colors = {Color.YELLOW, Color.ORANGE};
Point2D center = new Point2D.Double(size / 2, size / 2);
RadialGradientPaint radial = new RadialGradientPaint(
center, (float) ((size / 2) - 2f), dist, colors);
g2.setPaint(radial);
g2.fill(pacMan);
GradientPaint gradient = new GradientPaint(
0, 0, new Color(255, 255, 225, 220),
(int) (size / 3), 0, new Color(255, 255, 255, 0));
g2.setPaint(gradient);
g2.fill(pacMan);
g2.dispose();
return bi;
}
public void savePacManImage(int q, int num) throws IOException {
double angle = Math.PI*2 / 3d * ((double) num / (double) maxSize);
BufferedImage bi = getPacManImage(angle, Color.WHITE);
images.mkdirs();
File img = new File(images, "PacMan-" + q + "x" + num + ".gif");
ImageIO.write(bi, "gif", img);
}
public static void main(String[] args) {
try {
for (int ii = 0; ii < 4; ii++) {
PacManShape pms = new PacManShape(100, (double) ii * Math.PI / 2d);
for (int jj = 0; jj <= pms.maxSize; jj++) {
pms.savePacManImage(ii, jj);
}
}
Desktop.getDesktop().open(images);
} catch (IOException ex) {
ex.printStackTrace();
}
Runnable r = new Runnable() {
@Override
public void run() {
JPanel gui = new JPanel(new BorderLayout());
gui.add(new PacManComponent());
JOptionPane.showMessageDialog(null, gui);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class PacManComponent extends JPanel {
double angle = 0d;
int preferredSize = 100;
double diff = Math.PI / 8;
boolean chomp = true;
Timer timer;
PacManComponent() {
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
};
timer = new Timer(180, listener);
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(preferredSize, preferredSize);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
//double size = (getWidth() < getHeight() ? getWidth() : getHeight());
if (angle > 2 * Math.PI / 3) {
chomp = true;
} else if (angle < 0.01) {
chomp = false;
}
if (chomp) {
angle -= diff;
} else {
angle += diff;
}
PacManShape pms = new PacManShape(100, 0d);
Image image = pms.getPacManImage(angle, new Color(0, 0, 0, 0));
g2.drawImage(image, 0, 0, this);
g2.dispose();
}
}
If you wanted to transform & render images at run-time, try starting with this series of PNG format images that uses partial transparency to soften the edges.
Solution 2:
For the animation, you could use a Swing Timer to move the Pacman graphic as well as adjusting the angle by which the "mouth" opens, by varying the parameters used by fillArc
.
Interaction with KeyEvents
for movement control can be acheived using Key Bindings.
Also, I would move the paint
functionality to a paintComponent
method in a sub-classed JComponent
for better paint performance.
Related: Painting with Swing
Edit:
As youre starting Java there are a number of tasks to get working first
- Create the
JComponent
based class with a staticPacman
graphic. Move your painting logic topaintComponent
- Get the Swing Timer functionality working. Follow the Oracle guide for Timers.
- Implement the
Key Bindings
- Other functionality such as score keeping, etc.
Solution 3:
2d animation: http://en.wikipedia.org/wiki/File:The_Horse_in_Motion.jpg
Pseudocode:
while programActive:
deltatime = get_time_since_last_call()
update_animation_frame(deltatime)
image = get_animation_frame()
draw_background()
draw(image)
enforce_framerate(24)
To learn and do this sort of thing in Pygame would be easy compared to Java, but unsuitable for larger projects