我绝望,因为我的动画并不像我预期的那样顺畅。所以我知道,围绕这个问题有很多东西,但我的不同之处在于,我知道常见的问题,我认为,我做得对。首先,这是我的代码:
Game.java
import de.lwerner.collisions.CircleCollision;
import de.lwerner.collisions.math.Vector2D;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
public class Game extends JPanel implements ActionListener {
private static final Color BACKGROUND_COLOR = new Color(0, 0, 25);
private static final Color TEXT_COLOR = Color.WHITE;
private static final int TARGET_FPS = 60;
private javax.swing.Timer swingTimer;
private JFrame window;
private Timer timer;
private Timer fpsTimer;
private int counter;
private long[] diffs;
private int fps;
private LinkedList<Circle> circles;
private LinkedList<Sprite> sprites;
public Game() {
timer = new Timer();
fpsTimer = new Timer(System.currentTimeMillis());
diffs = new long[TARGET_FPS];
circles = new LinkedList<>();
circles.add(new Circle(100, 100, 50, 300, 300, Color.WHITE));
// circles.add(new Circle(250, 250, 50, 300, 300, Color.WHITE));
// circles.add(new Circle(400, 400, 50, 300, 300, Color.WHITE));
// circles.add(new Circle(550, 550, 50, 300, 300, Color.WHITE));
// circles.add(new Circle(700, 700, 50, 300, 300, Color.WHITE));
// circles.add(new Circle(100, 700, 50, -300, 300, Color.WHITE));
// circles.add(new Circle(250, 550, 50, 300, -300, Color.WHITE));
// circles.add(new Circle(550, 250, 50, 300, -300, Color.WHITE));
// circles.add(new Circle(700, 100, 50, -300, 300, Color.WHITE));
sprites = new LinkedList<>();
sprites.addAll(circles);
window = new JFrame("Game");
window.setContentPane(this);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.setUndecorated(true);
window.setResizable(false);
window.validate();
window.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
stop();
}
});
window.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
stop();
}
});
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, getWidth(), getHeight());
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Sprite s: sprites) {
s.render(g);
}
g.setColor(TEXT_COLOR);
g.drawString(fps + " FPS", 10, 20);
diffs[counter % TARGET_FPS] = fpsTimer.tick();
if (++counter % TARGET_FPS == 0) {
int sum = 0;
for (long diff: diffs) {
sum += diff;
}
fps = 1000 / (sum / TARGET_FPS);
}
}
private void step() {
long tick = timer.tick();
for (Sprite s: sprites) {
s.update(tick);
}
repaint();
checkCollisions();
}
private void checkCollisions() {
for (Sprite s1: sprites) {
BoundingBox bb = s1.getBoundingBox();
int offset = 0;
if (s1 instanceof Circle) {
offset = ((Circle) s1).getRadius();
}
if (bb.getLeft() < 0 || bb.getRight() >= getWidth()) {
s1.setVelX(-s1.getVelX());
if (bb.getLeft() < 0) {
s1.setX(0 + offset);
} else {
s1.setX(getWidth() - 1 - offset);
}
}
if (bb.getTop() < 0 || bb.getBottom() >= getHeight()) {
s1.setVelY(-s1.getVelY());
if (bb.getTop() < 0) {
s1.setY(0 + offset);
} else {
s1.setY(getHeight() - 1 - offset);
}
}
}
// Set<Set<Circle>> collidingPairs = new HashSet<>();
// for (Circle c1: circles) {
// for (Circle c2: circles) {
// if (c1 == c2) {
// continue;
// } else {
// if (c1.intersects(c2)) {
// Set<Circle> pair = new HashSet<>(Arrays.asList(c1, c2));
// if (!collidingPairs.contains(pair)) {
// collidingPairs.add(pair);
// de.lwerner.collisions.Circle cc1 = new de.lwerner.collisions.Circle(c1.getX(), c1.getY(), c1.getRadius(), new Vector2D(c1.getVelX(), c1.getVelY()));
// de.lwerner.collisions.Circle cc2 = new de.lwerner.collisions.Circle(c2.getX(), c2.getY(), c2.getRadius(), new Vector2D(c2.getVelX(), c2.getVelY()));
// CircleCollision collision = new CircleCollision(cc1, cc2);
// collision.resolveCollision();
// Vector2D v1New = cc1.getV();
// Vector2D v2New = cc2.getV();
// c1.setVelX((int) v1New.getX());
// c1.setVelY((int) v1New.getY());
// c2.setVelX((int) v2New.getX());
// c2.setVelY((int) v2New.getY());
// }
// }
// }
// }
// }
}
private void stop() {
if (swingTimer.isRunning()) {
swingTimer.stop();
window.dispose();
}
}
public void start() {
swingTimer = new javax.swing.Timer(1000 / TARGET_FPS, this);
swingTimer.start();
}
@Override
public void actionPerformed(ActionEvent e) {
step();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Game().start());
}
}
Sprite.java
import java.awt.*;
public abstract class Sprite {
protected double x;
protected double y;
protected int width;
protected int height;
protected int velX;
protected int velY;
public Sprite(int x, int y, int width, int height) {
this(x, y, width, height, 0, 0);
}
public Sprite(int x, int y, int width, int height, int velX, int velY) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.velX = velX;
this.velY = velY;
}
public int getX() {
return (int)Math.round(x);
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return (int)Math.round(y);
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getVelX() {
return velX;
}
public void setVelX(int velX) {
this.velX = velX;
}
public int getVelY() {
return velY;
}
public void setVelY(int velY) {
this.velY = velY;
}
public void update(long tick) {
x += tick * velX / 1000.0;
y += tick * velY / 1000.0;
}
public BoundingBox getBoundingBox() {
return new BoundingBox(getY(), getX() + width, getY() + height, getX());
}
public boolean intersects(Sprite other) {
return getBoundingBox().intersects(other.getBoundingBox());
}
public abstract void render(Graphics g);
}
Circle.java
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Circle extends Sprite {
private Color color;
private BufferedImage image;
public Circle(int x, int y, int r, Color c) {
super(x, y, r * 2, r * 2);
color = c;
// try {
// loadImage();
// } catch (Exception e) {
//
// }
}
public Circle(int x, int y, int r, int velX, int velY, Color c) {
super(x, y, r * 2, r * 2, velX, velY);
color = c;
// try {
// loadImage();
// } catch (Exception e) {
//
// }
}
private void loadImage() throws IOException {
image = ImageIO.read(getClass().getResource("/bubble.png"));
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getRadius() {
return width / 2;
}
public boolean intersects(Circle other) {
int dx = Math.abs(getX() - other.getX());
int dy = Math.abs(getY() - other.getY());
int r1 = getRadius();
int r2 = other.getRadius();
return dx * dx + dy * dy < (r1 + r2) * (r1 + r2);
}
@Override
public BoundingBox getBoundingBox() {
return new BoundingBox(getY() - getRadius(), getX() + getRadius(), getY() + getRadius(), getX() - getRadius());
}
@Override
public void render(Graphics g) {
if (image == null) {
g.setColor(color);
g.fillOval(getX() - getRadius(), getY() - getRadius(), width, height);
} else {
g.drawImage(image, getX() - getRadius(), getY() - getRadius(), null);
}
}
}
Timer.java
public class Timer {
private long lastTime;
public Timer() {
this(0L);
}
public Timer(long initialTime) {
lastTime = initialTime;
}
public long tick() {
if (lastTime == 0) {
lastTime = System.currentTimeMillis();
return 0;
}
long current = System.currentTimeMillis();
long diff = current - lastTime;
lastTime = current;
return diff;
}
}
BoundingBox.java
import java.awt.*;
public class BoundingBox {
private int top;
private int right;
private int bottom;
private int left;
public BoundingBox(int top, int right, int bottom, int left) {
this.top = top;
this.right = right;
this.bottom = bottom;
this.left = left;
}
public int getTop() {
return top;
}
public int getRight() {
return right;
}
public int getBottom() {
return bottom;
}
public int getLeft() {
return left;
}
public Rectangle getRectangle() {
return new Rectangle(left, top, right - left, bottom - top);
}
public boolean intersects(BoundingBox other) {
return getRectangle().intersects(other.getRectangle());
}
@Override
public String toString() {
return "BoundingBox{" +
"top=" + top +
", right=" + right +
", bottom=" + bottom +
", left=" + left +
'}';
}
}
所以我不知道,我做错了什么。常见的问题,如调用paint(),使用非双缓冲组件,使用错误的方法定期运行代码等等都不存在,但它是闪烁的。 FPS是不变的。
所以,请帮助我!我很乐意过来这个......
谢谢!