我创建了一个将图像绘制到JPanel的游戏。这与JTimer一起运行,每隔14毫秒执行一次以移动背景,移动障碍物并移动玩家(直升机)。偶尔会出现背景和障碍物的闪烁,而且运行速度不是特别顺畅。 有没有办法解决这个问题?我是否需要在不同的类中使用线程和动画? 我也使用了双缓冲,但后来我发现JPanels是双缓冲的,所以我把它删除了。
以下是Panel类的代码。
class ImagePanel extends JPanel implements KeyListener, MouseListener,
MouseMotionListener {
/**
*
*/
private static final long serialVersionUID = -6096603231469523786L;
Plane o1 = new Plane();
Tank o2 = new Tank();
ArrayList<Plane> planeArray;
ArrayList<Tank> tankArray;
ArrayList<Missile> missileArray;
int bgx;
int bgxTwo;
int time;
int y;
int score;
int obsNum;
int obsNum2;
int planeSpeed;
int missileSpeed;
int count;
boolean holding;
boolean gameOver;
boolean startscreen;
boolean shooting;
boolean obsOne = false;
boolean obsTwo = false;
Color colorone = Color.WHITE;
Color colortwo = Color.WHITE;
Color restartColor = Color.white;
Color menuColor = Color.white;
Color bulletColor = Color.white;
Font f1;
Font f2;
Font f3;
String version = "Get To The Chopper Beta 1.2";
int level = 1;
java.util.Timer movementtimer;
TimerTask task;
java.util.Timer obstacletimer;
TimerTask taskTwo;
private Image image;
private Image imageTwo;
private Image chopper;
private Image chopper2;
private Image tank;
private Image plane;
private Image missile;
private Image explosion;
BufferedImage bufferedImage;
Graphics buffer;
java.net.URL bgurl;
java.net.URL curl;
java.net.URL c2url;
java.net.URL turl;
java.net.URL purl;
java.net.URL murl;
java.net.URL eurl;
// ///////// CONSTRUCTOR SETS NON-CHANGABLE VARIABLES ////////////////
public ImagePanel() {
setFocusable(true);
addKeyListener(this);
addMouseListener(this);
addMouseMotionListener(this);
f1 = new Font("Helvetica", Font.BOLD, 10);
f2 = new Font("Helvetica", Font.PLAIN, 30);
f3 = new Font("Helvetica", Font.PLAIN, 25);
startscreen = true;
getImages();
planeArray = new ArrayList<Plane>();
tankArray = new ArrayList<Tank>();
missileArray = new ArrayList<Missile>();
repaint();
}
public void getImages() {
bgurl = getClass().getResource("images/background.png");
switch (level) {
case (1):
bgurl = getClass().getResource("images/grassbackground.png");
bulletColor = Color.black;
break;
case (2):
bgurl = getClass().getResource("images/snowbackground.png");
bulletColor = Color.red;
break;
}
curl = getClass().getResource("images/heli.png");
c2url = getClass().getResource("images/heli2.png");
turl = getClass().getResource("images/tank.png");
purl = getClass().getResource("images/plane.png");
murl = getClass().getResource("images/missile.png");
eurl = getClass().getResource("images/explosion.png");
try {
image = Toolkit.getDefaultToolkit().getImage(bgurl);
imageTwo = Toolkit.getDefaultToolkit().getImage(bgurl);
chopper = Toolkit.getDefaultToolkit().getImage(curl);
chopper2 = Toolkit.getDefaultToolkit().getImage(c2url);
tank = Toolkit.getDefaultToolkit().getImage(turl);
plane = Toolkit.getDefaultToolkit().getImage(purl);
missile = Toolkit.getDefaultToolkit().getImage(murl);
explosion = Toolkit.getDefaultToolkit().getImage(eurl);
} catch (Exception ex) {
System.out.println("File Not Found");
}
}
// //////// SETS VARIABLES WHEN GAME IS STARTED OR RESTARTED
// //////////////////
public void startGame() {
bgx = 0;
bgxTwo = 800;
y = 50;
score = 0;
holding = false;
gameOver = false;
time = (int) (Math.random() * 500 + 500);
obsNum2 = 0;
shooting = false;
count = 1;
planeSpeed = -6;
missileSpeed = -7;
planeArray.clear();
tankArray.clear();
missileArray.clear();
colorone = Color.WHITE;
colortwo = Color.WHITE;
restartColor = Color.white;
menuColor = Color.white;
movementtimer = new java.util.Timer();
task = new TimerTask() {
public void run() {
moveImage();repaint();
}
};
movementtimer.schedule(task, 0, 13);
obstacletimer = new java.util.Timer();
taskTwo = new TimerTask() {
public void run() {
generateObstacle();
}
};
obstacletimer.schedule(taskTwo, 2000, time);
}
// ////////// COLLISION DETECTION //////////////////
public void moveImage() {
moveBackground();
addToScore();
if (holding) {
y += -3;
} else {
y += 3;
}
for (int i = 0; i < planeArray.size(); i++) {
Plane plane = planeArray.get(i);
if (y + 30 >= plane.yPos && y + 30 <= plane.yPos + 30
&& plane.xPos >= 200 && plane.xPos <= 250
|| y >= plane.yPos && y <= plane.yPos + 30
&& plane.xPos >= 200 && plane.xPos + 50 <= 250) {
gameOver = true;
System.out.println("Crash with plane!");
}
}
for (int i = 0; i < tankArray.size(); i++) {
Tank tank = tankArray.get(i);
if (y + 30 >= tank.yPos && y + 30 <= tank.yPos + 30
&& tank.xPos >= 200 && tank.xPos <= 250
|| y >= tank.yPos && y <= tank.yPos + 30
&& tank.xPos >= 200 && tank.xPos + 50 <= 250) {
gameOver = true;
System.out.println("Crash with a tank");
}
if (shooting) {
if (y + 30 >= tank.bulletYPos && y <= tank.bulletYPos
&& tank.bulletXPos >= 200 && tank.bulletXPos <= 250) {
gameOver = true;
System.out.println("Shot down");
}
}
}
for (int i = 0; i < missileArray.size(); i++) {
Missile missile = missileArray.get(i);
if (y + 30 >= missile.yPos && y + 30 <= missile.yPos + 30
&& missile.xPos >= 200 && missile.xPos <= 250
|| y >= missile.yPos && y <= missile.yPos + 30
&& missile.xPos >= 200 && missile.xPos + 50 <= 250) {
gameOver = true;
System.out.println("Crash with a missile");
}
}
if (y >= 300) {
gameOver = true;
} else if (y <= -15) {
gameOver = true;
}
}
// //////// MOVES BACKGROUND //////////////
public void moveBackground() {
bgx += -5;
if (bgx == -800) {
bgx = 800;
}
bgxTwo += -5;
if (bgxTwo == -800) {
bgxTwo = 800;
}
for (int i = 0; i < planeArray.size(); i++) {
Plane obs = planeArray.get(i);
obs.xPos = obs.xPos - 6;
}
for (int i = 0; i < tankArray.size(); i++) {
Tank obs = tankArray.get(i);
obs.xPos = obs.xPos - 5;
if (shooting) {
obs.bulletYPos = obs.bulletYPos - 2;
obs.bulletXPos = obs.bulletXPos - obs.angle;
}
}
for (int i = 0; i < missileArray.size(); i++) {
Missile obs = missileArray.get(i);
obs.xPos = obs.xPos - 7;
}
}
public void addToScore() {
score += 5;
increaseDiff();
}
public void increaseDiff() {
if (score == 10000) {
shooting = true;
for (int i = 0; i < tankArray.size(); i++) {
Tank tank = tankArray.get(i);
tank.bulletXPos = tank.xPos;
}
}
if (score > 5000 && score < 9999) {
time = (int) (Math.random() * 400 + 250);
planeSpeed = -7;
missileSpeed = -8;
} else if (score > 10000 && score < 14999) {
time = (int) (Math.random() * 400 + 150);
planeSpeed = -8;
missileSpeed = -10;
} else if (score > 15000 && score < 19999) {
time = (int) (Math.random() * 400 + 150);
planeSpeed = -9;
missileSpeed = -11;
obsNum2 = (int) (Math.random() * 2 + 1);
} else if (score > 20000 && score < 29999) {
time = (int) (Math.random() * 300 + 100);
planeSpeed = -10;
missileSpeed = -13;
obsNum2 = (int) (Math.random() * 2 + 1);
} else if (score > 30000) {
time = (int) (Math.random() * 300 + 100);
planeSpeed = -12;
missileSpeed = -15;
obsNum2 = (int) (Math.random() * 2 + 1);
}
}
public void generateObstacle() {
obsNum = (int) (Math.random() * 5 + 1);
if (obsNum == 1 || obsNum == 4 || obsNum2 == 1) {
planeArray.add(new Plane());
}
if (obsNum == 2) {
tankArray.add(new Tank());
}
if (obsNum == 3 || obsNum == 5 || obsNum2 == 2) {
missileArray.add(new Missile());
}
}
public void update(Graphics g) {
paint(g);
}
// /////////////// MAIN PAINT METHOD //////////////////////
@Override
public void paint(Graphics g) {
g.clearRect(0,0,800,400);
if (!startscreen) {
g.drawImage(image, bgx, 0, null);
g.drawImage(imageTwo, bgxTwo, 0, null);
for (int i = 0; i < planeArray.size(); i++) {
Plane obs = planeArray.get(i);
if (obs.xPos <= -50) {
planeArray.remove(i);
} else {
g.drawImage(plane, obs.xPos, obs.yPos, null);
}
}
for (int i = 0; i < tankArray.size(); i++) {
Tank obs = tankArray.get(i);
if (obs.xPos <= -50) {
tankArray.remove(i);
} else {
g.drawImage(tank, obs.xPos, obs.yPos, null);
if (shooting) {
g.setColor(bulletColor);
g.fillOval(obs.bulletXPos, obs.bulletYPos, 5,
5);
}
}
}
for (int i = 0; i < missileArray.size(); i++) {
Missile obs = missileArray.get(i);
if (obs.xPos <= -50) {
missileArray.remove(i);
} else {
g.drawImage(missile, obs.xPos, obs.yPos, null);
}
}
if (gameOver) {
movementtimer.cancel();
obstacletimer.cancel();
g.setColor(Color.WHITE);
g.setFont(f2);
g.drawString("GAME OVER", 250, 150);
g.setFont(f3);
g.drawString("Your score: " + score, 250, 200);
g.setColor(restartColor);
g.drawString("Click to restart!", 250, 250);
g.setColor(menuColor);
g.drawString("Back to menu", 250, 300);
g.drawImage(explosion, 200, y, this);
}
} else if (startscreen) {
g.drawImage(image, 0, 0, this);
g.drawImage(imageTwo, 0, 0, this);
g.drawImage(chopper, 200, 50, this);
g.setColor(Color.WHITE);
g.setFont(f2);
g.drawString("Choose a level:", 200, 150);
g.setFont(f3);
g.setColor(colorone);
g.drawString("Grass Level!", 250, 200);
g.setColor(colortwo);
g.drawString("Snow Level!", 250, 250);
}
if (count <= 9 && !gameOver && !startscreen) {
g.drawImage(chopper, 200, y, this);
count++;
} else if (count >= 10 && count < 20 && !gameOver && !startscreen) {
g.drawImage(chopper2, 200, y, this);
count++;
} else if (count == 20 && !gameOver && !startscreen) {
count = 1;
g.drawImage(chopper, 200, y, this);
}
g.setFont(f1);
g.setColor(Color.WHITE);
g.drawString(version, 10, 10);
g.drawString("Score: " + score + "", 700, 10);
}
// ///////////////////OVERRIDE KEYPRESSED EVENTS//////////////////////
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == 32) {
holding = true;
repaint();
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == 32) {
holding = false;
repaint();
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public void mousePressed(MouseEvent e) {
if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 180
&& e.getY() <= 200 && startscreen) {
startscreen = false;
level = 1;
getImages();
startGame();
}
if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 230
&& e.getY() <= 250 && startscreen) {
startscreen = false;
level = 2;
getImages();
startGame();
}
if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 230
&& e.getY() <= 250 && gameOver) {
startGame();
}
if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 280
&& e.getY() <= 320 && gameOver) {
gameOver = false;
startscreen = true;
repaint();
}
}
@Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseMoved(MouseEvent e) {
if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 180
&& e.getY() <= 200 && startscreen) {
colorone = Color.RED;
repaint();
} else if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 230
&& e.getY() <= 250 && startscreen) {
colortwo = Color.RED;
repaint();
} else if (startscreen) {
colorone = Color.white;
colortwo = Color.white;
repaint();
}
if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 230
&& e.getY() <= 250 && gameOver) {
restartColor = Color.red;
repaint();
} else if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 280
&& e.getY() <= 320 && gameOver) {
menuColor = Color.red;
repaint();
} else if (gameOver) {
restartColor = Color.white;
menuColor = Color.white;
repaint();
}
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseDragged(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
对于所有代码感到抱歉,正如您可能会说的那样,我是Java和编程的新手(从去年大学开始),但这是我希望进步并从事事业的事情。
由于 汤姆
答案 0 :(得分:2)
你试图在Swing程序中做AWT图形 - 不要这样做。相反,在Swing图形程序中没有更新的地方,你不应该重写paint方法,而应该重写JPanel的paintComponent方法。这样你就可以利用Swing的双重缓冲。
这是一个显示AWT与Swing动画的SSCCE,不幸的是,我并没有像我希望的那样看到两者之间存在太大差异。
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
@SuppressWarnings("serial")
public class PaintVsPaintComponent extends JPanel {
public static final String DUKE_WAVE = "http://duke.kenai.com/iconSized/duke4.gif";
private static final int PREF_WIDTH = 300;
private static final int PREF_HEIGHT = 250;
private static final int TIMER_DELAY = 20;
private static final int DELTA_X = 1;
private static final int DELTA_Y = DELTA_X;
private boolean awtDrawing;
private BufferedImage image;
private int x = 0;
private int y = 0;
public PaintVsPaintComponent(boolean awtDrawing, BufferedImage image) {
this.awtDrawing = awtDrawing;
this.image = image;
new Timer(TIMER_DELAY, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
timerActionPerformed(ae);
}
}).start();
}
private void timerActionPerformed(ActionEvent ae) {
x += DELTA_X;
y += DELTA_Y;
if (x >= getWidth()) {
x = 0;
}
if (y >= getHeight()) {
y = 0;
}
repaint();
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (awtDrawing) {
drawImage(g);
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (!awtDrawing) {
drawImage(g);
}
}
private void drawImage(Graphics g) {
g.drawImage(image, x, y, null);
}
@Override
public void update(Graphics g) {
if (awtDrawing) {
paint(g);
} else {
super.update(g);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(PREF_WIDTH, PREF_HEIGHT);
}
private static void createAndShowGui() {
BufferedImage dukeWaveImage = null;
URL dukeWaveUrl;
try {
dukeWaveUrl = new URL(DUKE_WAVE);
dukeWaveImage = ImageIO.read(dukeWaveUrl);
PaintVsPaintComponent awtPanel = new PaintVsPaintComponent(true, dukeWaveImage);
PaintVsPaintComponent swingPanel = new PaintVsPaintComponent(false, dukeWaveImage);
awtPanel.setBorder(BorderFactory.createTitledBorder("AWT Panel"));
swingPanel.setBorder(BorderFactory.createTitledBorder("Swing Panel"));
JPanel gridPanel = new JPanel(new GridLayout(1, 0));
gridPanel.add(awtPanel);
gridPanel.add(swingPanel);
JFrame frame = new JFrame("PaintVsPaintComponent");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gridPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
答案 1 :(得分:2)
不要使用TimerTask。
你应该使用Swing Timer。然后,对GUI的所有更新都将在Event Dispatch Thread上完成。