Java JPanel默认双缓冲不';我似乎无法摆脱颠簸的运动
我一直在做一个简单的2D Java游戏来提高我的编码技能。我已经读到,默认情况下,JPanel将是双缓冲的,因此我认为,如果我只是使用JPanel作为“画布”,我不应该有任何起伏运动的问题。不幸的是,这似乎不是100%的时间都有效。通常情况下,玩家的动作非常流畅,但在看似随机的情况下,动作会变得极其起伏。我试着给fps设定上限,但那没什么不同。我自己也尝试过创建缓冲区策略,但也没有解决这个问题。正如一条评论(谢谢!)所建议的,这里是一个简单的版本,它只显示了问题:Java JPanel默认双缓冲不';我似乎无法摆脱颠簸的运动,java,swing,game-loop,double-buffering,Java,Swing,Game Loop,Double Buffering,我一直在做一个简单的2D Java游戏来提高我的编码技能。我已经读到,默认情况下,JPanel将是双缓冲的,因此我认为,如果我只是使用JPanel作为“画布”,我不应该有任何起伏运动的问题。不幸的是,这似乎不是100%的时间都有效。通常情况下,玩家的动作非常流畅,但在看似随机的情况下,动作会变得极其起伏。我试着给fps设定上限,但那没什么不同。我自己也尝试过创建缓冲区策略,但也没有解决这个问题。正如一条评论(谢谢!)所建议的,这里是一个简单的版本,它只显示了问题: package main;
package main;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
final static private Canvas canvas = new Canvas();
static private Window gameWindow = new Window();
final static private GameLoop gameLoop = new GameLoop();
final static private Player player = new Player();
//getters/(setters)
public static Canvas canvas() {
return canvas;
}
public static Window gameWindow() {
return gameWindow;
}
public static Player player() {
return player;
}
public static GameLoop gameLoop() {
return gameLoop;
}
public static void main(String[] args) {
gameWindow().makeWindow(false);
gameLoop().startLoop(60.0);
gameWindow().setVisible(true);
}
}
class GameLoop {
private boolean gameIsRunning = true;
private double tps = 0;
public void startLoop(double ticksps){
tps = ticksps;
new Thread(new Runnable() {
@Override
public void run() {
loop();
}
}).start();
}
private void loop() {
//stolen from https://gamedev.stackexchange.com/questions/52841/the-most-efficient-and-accurate-game-loop
//and edited slightly for fps capping by using sleep
long lastTime = System.nanoTime();
double ns = 1000000000 / tps;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
long lastTimePainted = 0;
int ticks = 0;
long now = 0;
int timeToNextFrame = 0;
while(gameIsRunning) {
now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
ticks++;
updateGame();
delta--;
}
if (System.nanoTime() - lastTimePainted > 3000000) {
frames++;
Main.canvas().repaint();
lastTimePainted = System.nanoTime();
}
else {
timeToNextFrame = (int) (3000000 - (System.nanoTime() - lastTimePainted));
//System.out.println(timeToNextFrame);
try {
if (timeToNextFrame > 1999998) {
Thread.sleep(0, 999999);
}
else if (timeToNextFrame > 0) {
Thread.sleep(0, timeToNextFrame / 2);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (System.currentTimeMillis() - timer > 1000) {
System.out.println(frames + " " + ticks);
ticks = 0;
frames = 0;
timer += 1000;
}
}
}
private void updateGame() {
Main.player().move();
}
}
class Player{
private int x = 0;
private int y = 0;
private int velX = 2;
private int velY = 2;
public int getX() {
return x;
}
public int getY() {
return y;
}
public void move() {
if (x + 50 >= Main.gameWindow().getWidth() || x < 0) {
velX = -velX;
}
if (y + 50 >= Main.gameWindow().getHeight() || y < 0) {
velY = -velY;
}
x += velX;
y += velY;
}
}
class Window extends JFrame {
private static final long serialVersionUID = 1L;
public void makeWindow (boolean fullscreen) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Game Prototype");
setLayout(null);
setResizable(false);
getContentPane().add(Main.canvas());
Main.canvas().setLocation(0, 0);
setFocusable(true);
setIgnoreRepaint(true);
setSize(1024, 576);
Main.canvas().setSize(1024, 576);
setLocationRelativeTo(null);
validate();
repaint();
}
}
class Canvas extends JPanel {
private static final long serialVersionUID = 1L;
Canvas(){
setLayout(null);
setBackground(Color.WHITE);
setDoubleBuffered(true);
setIgnoreRepaint(true);
setVisible(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(Main.player().getX(), Main.player().getY(), 50, 50);
}
}
packagemain;
导入java.awt.Color;
导入java.awt.Graphics;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
公共班机{
最终静态私有画布=新画布();
静态私有窗口gameWindow=新窗口();
最终静态私有GameLoop GameLoop=新GameLoop();
最终静态私人玩家=新玩家();
//接受者/(接受者)
公共静态画布(){
返回画布;
}
公共静态窗口gameWindow(){
返回游戏窗口;
}
公共静态播放器(){
返回球员;
}
公共静态GameLoop GameLoop(){
返回游戏循环;
}
公共静态void main(字符串[]args){
gameWindow().makeWindow(false);
gameLoop().startoop(60.0);
gameWindow().setVisible(true);
}
}
类GameLoop{
私有布尔gamesrunning=true;
私人双tps=0;
公共无效STARTOOP(双滴答声){
tps=tps;
新线程(newrunnable()){
@凌驾
公开募捐{
loop();
}
}).start();
}
私有void循环(){
//偷自https://gamedev.stackexchange.com/questions/52841/the-most-efficient-and-accurate-game-loop
//并使用sleep对fps封顶进行了轻微编辑
long lastTime=System.nanoTime();
双ns=100000000/tps;
双增量=0;
长定时器=System.currentTimeMillis();
int帧=0;
长LastTimePaint=0;
int ticks=0;
long now=0;
int timeToNextFrame=0;
同时(游戏运行){
now=System.nanoTime();
增量+=(现在-上次)/ns;
上次=现在;
而(增量>=1){
ticks++;
updateGame();
三角洲--;
}
如果(System.nanoTime()-LastTimePaint>300000){
frames++;
Main.canvas().repaint();
LastTimePaint=System.nanoTime();
}
否则{
timeToNextFrame=(int)(3000000-(System.nanoTime()-lastTimePaint));
//System.out.println(timeToNextFrame);
试一试{
如果(timeToNextFrame>1999998){
睡眠(099999);
}
else if(timeToNextFrame>0){
睡眠(0,timeToNextFrame/2);
}
}捕捉(中断异常e){
e、 printStackTrace();
}
}
if(System.currentTimeMillis()-计时器>1000){
System.out.println(帧+“”+刻度);
滴答声=0;
帧=0;
定时器+=1000;
}
}
}
私有void updateGame(){
Main.player().move();
}
}
职业选手{
私有整数x=0;
私有整数y=0;
私有int velX=2;
私有int=2;
公共int getX(){
返回x;
}
公共int getY(){
返回y;
}
公开作废动议(){
如果(x+50>=Main.gameWindow().getWidth()| | x<0){
velX=-velX;
}
如果(y+50>=Main.gameWindow().getHeight()| | y<0){
velY=-velY;
}
x+=velX;
y+=0;
}
}
类窗口扩展JFrame{
私有静态最终长serialVersionUID=1L;
公共void生成窗口(布尔全屏){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(“游戏原型”);
setLayout(空);
可设置大小(假);
getContentPane().add(Main.canvas());
Main.canvas().setLocation(0,0);
设置聚焦(真);
setIgnoreRepaint(真);
设置大小(1024576);
Main.canvas().setSize(1024576);
setLocationRelativeTo(空);
验证();
重新油漆();
}
}
类Canvas扩展了JPanel{
私有静态最终长serialVersionUID=1L;
画布(){
setLayout(空);
挫折地面(颜色:白色);
setDoubleBuffered(真);
setIgnoreRepaint(真);
setVisible(真);
}
@凌驾
公共组件(图形g){
超级组件(g);
g、 setColor(Color.RED);
g、 fillOval(Main.player().getX(),Main.player().getY(),50,50);
}
}
非常感谢您的帮助。因为我只是从我的代码中复制粘贴了这些内容,所以完整代码中可能会有一些无用的剩余内容。不要对您未创建的图形对象调用dispose()。特别是,传递给绘画方法的图形对象是由Swing创建的,由Swing拥有,并且很可能用于绘画其他东西。为了更快地获得更好的帮助,请添加or。感谢@Andrew,我按照您所说的做了。我建议的第一件事是用每秒启动的Swing
计时器替换游戏循环。