【发布时间】:2015-08-13 12:20:49
【问题描述】:
我正在尝试学习 Java,来自 C/汇编嵌入式系统背景。经过几周的学习,我认为尝试制作游戏会很有趣,但是我遇到了一些问题,即以不一致的速度重新绘制 JPanel。
我的“游戏”GUI 由一个包含 JPanel 的 JFrame 组成。如您所见,JFrame 的主线程休眠 30 毫秒,然后更新“游戏”逻辑并重绘 JFrame 和 JPanel。每次重绘 JPanel 时,我都会检查它是否花费了大约 30 毫秒。如您所料,帧重绘之间的时间永远不会超过 32 毫秒。
尽管 JPanel 肯定每 30 毫秒左右重新绘制一次,但其中的动画可能非常生涩。在 Ubuntu 14.10 上,这一点非常明显,即使调用我的 JPanel 的 repaint() 之间的时间间隔仍然不会超过 32 毫秒。
最让人头疼的是,如果我取消注释这些行
//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;
在 SimFrame.java 中,一切都非常顺利,这意味着我的计算机具有能力以我想要的速度更新 JPanel。
有什么方法可以强制 JFrame 以我想要的速度更新,而无需进行荒谬的 .resize 调用?
Main.java
package jdemo;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main
{
public static void main(String[] args)
{
SimFrame s = new SimFrame();
Thread t = new Thread(s);
t.start();
}
}
Sim.java
package jdemo;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Sim extends JPanel implements KeyListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
private int keys[] = new int[1024];
double x = 100;
double y = 100;
double dx = 0;
double dy = 0;
double theta = 0.0;
private int size = 0;
private int dsize = 1;
public Sim()
{
this.addKeyListener(this);
this.setFocusable(true);
}
long prevmillis;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D drawing = (Graphics2D)g;
//clear the background.
drawing.setColor(Color.WHITE);
drawing.fillRect(0, 0, this.getWidth() - 1, this.getHeight() - 1);
//System.out.printf("dt = %d\n", System.currentTimeMillis() - prevmillis);
prevmillis = System.currentTimeMillis();
drawing.setColor(Color.BLACK);
drawing.drawRect(0, 0, size, size);
drawing.setColor(Color.BLACK);
int[] xpoints = {(int)(x + 10 * Math.cos(Math.toRadians(theta))),
(int)(x + 5 * Math.cos(Math.toRadians(theta - 150))),
(int)(x + 5 * Math.cos(Math.toRadians(theta + 150)))};
int[] ypoints = {(int)(y + 10 * Math.sin(Math.toRadians(theta))),
(int)(y + 5 * Math.sin(Math.toRadians(theta - 150))),
(int)(y + 5 * Math.sin(Math.toRadians(theta + 150)))};
drawing.drawPolygon(xpoints, ypoints, 3);
}
public void updateLogic()
{
if(keys[KeyEvent.VK_UP] == 1)
{
size++;
}
else if(keys[KeyEvent.VK_DOWN] == 1)
{
size--;
}
//update theta.
if(keys[KeyEvent.VK_LEFT] == 1)
{
theta += 5;
}
if(keys[KeyEvent.VK_RIGHT] == 1)
{
theta -= 5;
}
if(theta > 360.1)
{
theta -= 360;
}
if(theta < -0.1)
{
theta += 360;
}
//update acceleration
if(keys[KeyEvent.VK_SPACE] == 1)
{
dx += 0.08* Math.cos(Math.toRadians(theta));
dy += 0.08 * Math.sin(Math.toRadians(theta));
}
dx *= 0.99;
dy *= 0.99;
//update position
x = x + dx;
y = y + dy;
System.out.printf("%f, %f\n", dx, dy);
//update size
if(size > 150)
{
dsize = -1;
}
if(size < 10)
{
dsize = 1;
}
size += dsize;
}
@Override
public void keyPressed(KeyEvent arg0)
{
// TODO Auto-generated method stub
keys[arg0.getKeyCode()] = 1;
System.out.printf("%d\n", arg0.getKeyCode());
}
@Override
public void keyReleased(KeyEvent arg0)
{
// TODO Auto-generated method stub
keys[arg0.getKeyCode()] = 0;
}
@Override
public void keyTyped(KeyEvent arg0)
{
// TODO Auto-generated method stub
}
}
SimFrame.java
package jdemo;
import jdemo.Sim;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SimFrame extends JFrame implements Runnable
{
private Sim s;
public SimFrame()
{
this.s = new Sim();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(this.s);
this.pack();
this.setLocationRelativeTo(null);
this.setSize(200, 200);
}
@Override
public void run()
{
int a = 0;
this.setVisible(true);
while(true)
{
//repaint
s.updateLogic();
this.getContentPane().revalidate();
this.repaint();
//this.resize(300 + a, 300);
//a = (a == 1)?(0):1;
try
{
Thread.sleep(30);
}
catch(InterruptedException e)
{
System.out.printf("failed");
break;
}
}
}
}
非常感谢。
【问题讨论】:
-
为什么每30ms重绘时要调整面板大小
-
如果我这样做,帧中的动画就会变得平滑。
-
您从 200,200 的大小开始,但您的更新将其设置为 300,300,这本身就很奇怪。您可以改为设置 Jframe 的首选大小(不在循环中)
-
@Asura 我不认为你明白。如果我删除调整 JFrame 大小的行(我理解这是非常不正统的),我的 JPanel 中的动画会变得生涩。我不希望这些线条在那里,但它们是迄今为止我能够使我的 JPanel 中的动画平滑的唯一方法。我想知道当我删除对 JFrame.resize() 的调用时如何保持动画流畅。抱歉,如果我的问题不清楚。
标签: java swing paintcomponent keylistener frame-rate