Reputation: 1
I wish to animate my pac-man sprite however when my timer calls actionPerformed
to update the image, it executes twice and skips over an animation.
Here is the code for the main class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.concurrent.TimeUnit;
import javax.swing.Timer;
public class Pacman extends JFrame implements ActionListener, KeyListener{
public static Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
public static int FRAME_HEIGHT = (int)(dim.height-dim.height/5);
public static int FRAME_WIDTH = (int)(dim.width-dim.width/5);
public static Pacman frame = new Pacman();
public static Game gameInterface = new Game();
public static Timer timer;
public Pacman() {
super("Pac-man");
timer = new Timer(175, this);
timer.start();
addKeyListener(this);
}
public static void main(String[] args) {
ImageIcon frameIcon = new ImageIcon("Icon.png");
frame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
frame.setIconImage(frameIcon.getImage());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.setContentPane(gameInterface);
gameInterface.requestFocus();
gameInterface.setFocusable(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int JframeX = (dim.width-FRAME_WIDTH)/2;
int JframeY = (dim.height-FRAME_HEIGHT)/2;
frame.setLocation(JframeX, JframeY);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void paint(Graphics g) {
super.paint(g);
}
public void actionPerformed(ActionEvent ev){
repaint();
}
}
Here is the code for the game class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.concurrent.TimeUnit;
import javax.swing.Timer;
public class Game extends JPanel implements ActionListener, KeyListener{
public static Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
public static int FRAME_HEIGHT = (int)(dim.height-dim.height/5);
public static int FRAME_WIDTH = (int)(dim.width-dim.width/5);
public static Game gamePanel = new Game();
public static Timer timer;
public static boolean gameExit = false;
public static int animCounter = 0, imgPacmanX1 = 840, imgPacmanY1 = 0, imgPacmanX2 = 890, imgPacmanY2 = 50;
public Game() {
timer = new Timer(500, this);
timer.start();
addKeyListener(this);
}
public static void main(String[] args) {
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon animations = new ImageIcon("Pacman_Animations.png");
g.setColor(Color.BLUE);
g.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
g.drawImage(animations.getImage(), 150, 200, 200, 250, imgPacmanX1, imgPacmanY1, imgPacmanX2, imgPacmanY2, null);
}
public void actionPerformed(ActionEvent ev){
if(animCounter == 2){
imgPacmanY1 = 0;
imgPacmanY2 = 50;
animCounter = 0;
}
else{
imgPacmanY1 += 50;
imgPacmanY2 += 50;
animCounter += 1;
}
repaint();
System.out.print(imgPacmanY1 + " " + imgPacmanY2 + " ");
}
}
If I run the game class separately, the action listener works fine and only executes once, however called from my main class the action listener executes twice and the animations starts skipping frames.
Upvotes: 0
Views: 78
Reputation: 347194
You have two Timer
's each one is trigger a repaint
and ... welcome to the wonderful world of why your architecture is wrong.
JFrame
, in fact, extending from JFrame
is generally discouraged as you're not actually adding a new functionality to the class and they are a jumble of components already.static
. If you find you're using static
to allow you to reference other objects, then your design is wrong. You should be passing information to your class via the constructor or getters. This is where things like "model-view-controller" come in and are importantKeyListener
is a poor choice, you should be using the Key bindings API instead.The first thing you should focus on is decoupling the game core logic from the rendering work flow. This means you'd have a "main loop" (in this case, the Swing Timer
) which would update the mode (or state) and then trigger a paint cycle. The rendering work flow would then use the current "state" for its rendering.
Upvotes: 2