问题
I am trying to make a 2 player Pong Applet where the user controls the paddle using mouse.
I have used the concept of Double Buffering, but the Applet still flickers !!! What is the mistake I am committing ?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.Timer;
import javax.swing.JApplet;
public class Pong extends JApplet implements Runnable, MouseMotionListener {
Ball ball;
Paddle left_paddle;
Paddle right_paddle;
Image image;
Graphics DoubleBuffered;
@Override
public void init() {
setSize(800, 600);
addMouseMotionListener(this);
setFocusable(true);
requestFocusInWindow();
}
@Override
public void start() {
left_paddle = new Paddle(5, 200, this);
right_paddle = new Paddle(this.getWidth() - 30, this.getHeight() / 2, this);
ball = new Ball(100, 100, left_paddle, right_paddle);
Thread thread = new Thread(this);
thread.start();
}
@Override
public void run() {
while (true) {
ball.update(this);
repaint();
try {
Thread.sleep(17); //Frame rate of approx 60 FPS
} catch (InterruptedException ex) {
}
}
}
@Override
public void stop() {
}
@Override
public void destroy() {
}
@Override
public void update(Graphics g) {
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
DoubleBuffered = image.getGraphics();
}
DoubleBuffered.setColor(getBackground());
DoubleBuffered.fillRect(0, 0, this.getWidth(), this.getHeight());
DoubleBuffered.setColor(getForeground());
paint (DoubleBuffered);
DoubleBuffered.drawImage(image, 0, 0, this);
}
@Override
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
ball.paint(g);
left_paddle.paint(g);
right_paddle.paint(g);
g.setColor(Color.BLACK);
g.drawLine(this.getWidth()/2, 0, this.getWidth()/2, this.getHeight());
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
int y = e.getY();
int x = e.getX();
if (x < this.getWidth()/2) { //Control the left paddle
if (y < 0) {
y = 0;
} else if (y + left_paddle.getHeight() >= this.getHeight()) {
y = this.getHeight() - left_paddle.getHeight();
}
left_paddle.mouse(y);
}
else {
if (y < 0) {
y = 0;
} else if (y + right_paddle.getHeight() >= this.getHeight()) {
y = this.getHeight() - right_paddle.getHeight();
}
right_paddle.mouse(y);
}
}
}
class Ball {
private int x;
private int y;
private float dx=4;
private float dy=5;
private int radius = 10;
private Paddle left, right;
Ball(int x, int y, Paddle x1, Paddle y1) {
this.x = x;
this.y = y;
left = x1;
right = y1;
}
public void reset ()
{
this.x = 400;
this.y = 200;
}
public void update(Pong ref)
{
if (x+dx < left.getX()+left.getWidth()+radius && y+dy+radius >= left.getY() && y+dy+radius <= left.getY()+left.getHeight()) {
x = left.getX()+left.getWidth()+radius;
dx *= (-1);
// checks the left paddle collision
}
else if (x+dx+radius >= right.getX() && y+dy+radius >= right.getY() && y+dy+radius <= right.getY()+right.getHeight() ) {
x = right.getX() - radius;
dx *= (-1);
//checks the right paddle collision
}
//if ball touches the boundary reset it at middle point
else if (x+dx+radius >= ref.getWidth()) {
reset();
}
else if (x + dx < radius) {
reset();
}
else {
x += dx;
}
if (y+dy +radius > ref.getHeight()) {
y = ref.getHeight() - radius - 1;
dy *= (-1);
}
else if (y + dy < radius) {
y = radius;
dy *= (-1);
}
else {
y += dy;
}
}
public void paint (Graphics g)
{
g.setColor(Color.RED);
g.fillOval(x-radius, y-radius, 2*radius, 2*radius);
}
}
class Paddle {
private int x;
private int y;
private int width = 25;
private int height = 150;
private Timer timer;
public Paddle(int x, int y, Pong ref) {
this.x = x;
this.y = y;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void paint (Graphics g)
{
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
//mouse lookup
void mouse(int y_final)
{
y = y_final;
}
}
回答1:
Generally speaking, you should avoid overriding the paint and update methods of top level containers like JApplet.
You must remember to call super.update, passing it the graphics context which you have created. You must also remember to call super.paint. There is a lot of important functionality going in here that you don't want to skip over.
You should also be disposing of any graphics you create, as they tend to hog memory ;)
But, seen as you're using Swing components anyway, you should create yourself a custom component (from something like JPanel) and use it's paintComponent method.
The main reason for this is, Swing components are double buffered by default...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.Timer;
import javax.swing.JApplet;
import javax.swing.JPanel;
public class Pong extends JApplet {
private GamePane gamePane;
@Override
public void init() {
setSize(800, 600);
gamePane = new GamePane();
setLayout(new BorderLayout());
add(gamePane);
}
@Override
public void start() {
gamePane.start();
}
@Override
public void stop() {
}
@Override
public void destroy() {
}
public class GamePane extends JPanel implements MouseMotionListener {
Ball ball;
Paddle left_paddle;
Paddle right_paddle;
public GamePane() {
setBackground(Color.WHITE);
addMouseMotionListener(this);
setFocusable(true);
requestFocusInWindow();
}
public void start() {
left_paddle = new Paddle(5, 200);
right_paddle = new Paddle(this.getWidth() - 30, this.getHeight() / 2);
ball = new Ball(100, 100, left_paddle, right_paddle);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ball.update(GamePane.this);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
ball.paint(g);
left_paddle.paint(g);
right_paddle.paint(g);
g.setColor(Color.BLACK);
g.drawLine(this.getWidth() / 2, 0, this.getWidth() / 2, this.getHeight());
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
int y = e.getY();
int x = e.getX();
if (x < this.getWidth() / 2) { //Control the left paddle
if (y < 0) {
y = 0;
} else if (y + left_paddle.getHeight() >= this.getHeight()) {
y = this.getHeight() - left_paddle.getHeight();
}
left_paddle.mouse(y);
} else {
if (y < 0) {
y = 0;
} else if (y + right_paddle.getHeight() >= this.getHeight()) {
y = this.getHeight() - right_paddle.getHeight();
}
right_paddle.mouse(y);
}
}
}
class Ball {
private int x;
private int y;
private float dx = 4;
private float dy = 5;
private int radius = 10;
private Paddle left, right;
Ball(int x, int y, Paddle x1, Paddle y1) {
this.x = x;
this.y = y;
left = x1;
right = y1;
}
public void reset() {
this.x = 400;
this.y = 200;
}
public void update(GamePane ref) {
if (x + dx < left.getX() + left.getWidth() + radius && y + dy + radius >= left.getY() && y + dy + radius <= left.getY() + left.getHeight()) {
x = left.getX() + left.getWidth() + radius;
dx *= (-1);
// checks the left paddle collision
} else if (x + dx + radius >= right.getX() && y + dy + radius >= right.getY() && y + dy + radius <= right.getY() + right.getHeight()) {
x = right.getX() - radius;
dx *= (-1);
//checks the right paddle collision
} //if ball touches the boundary reset it at middle point
else if (x + dx + radius >= ref.getWidth()) {
reset();
} else if (x + dx < radius) {
reset();
} else {
x += dx;
}
if (y + dy + radius > ref.getHeight()) {
y = ref.getHeight() - radius - 1;
dy *= (-1);
} else if (y + dy < radius) {
y = radius;
dy *= (-1);
} else {
y += dy;
}
}
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
}
public class Paddle {
private int x;
private int y;
private int width = 25;
private int height = 150;
private Timer timer;
public Paddle(int x, int y) {
this.x = x;
this.y = y;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void paint(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
//mouse lookup
void mouse(int y_final) {
y = y_final;
}
}
}
来源:https://stackoverflow.com/questions/15264373/applet-flickers-even-after-using-using-double-buffering