我制作了一个基于Java的游戏,它利用了一个包含JApplet的JFrame,而JApplet又包含一个绘制图形的JPanel,但由于某种原因,图形的左侧偶尔会闪烁,我无法弄清楚为什么会这样。我想这可能是因为我实际上没有使用过EDT,后来我才意识到这一点,那么有人能够告诉我如何将EDT的使用正确地整合到程序中吗?
我的主要课程如下:
public class Main extends JFrame{
public static final int HEIGHT = 600;
public static final int WIDTH = 800;
public static RApplet app;
public Main(){
setTitle("RCrawl");
Container c = getContentPane();
c.setPreferredSize(new Dimension(WIDTH, HEIGHT));
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
final Player p = new Player(50, 70, true);
final Room r = new Room(0xff222244);
r.addEntity(new EntityRock(200, 150));
app = new RApplet(WIDTH, HEIGHT);
app.setRoom(r);
app.setPlayer(p);
app.setFps(25);
add(app);
setVisible(true);
new UpdateThread().start();
System.out.println("thread run");
}
public static void main(String[] args){
Main m = new Main();
}
class UpdateThread extends Thread{
public void run(){
while(true)update();
}
public void update(){
app.refresh();
}
}
}
虽然RApplet类看起来像:
public class RApplet extends JApplet{
public int width, height, fps;
public long curTime, delta;
public RenderPanel panel;
public Room curRoom;
public Player player;
public boolean isSingle;
public InputHandler input;
public RApplet(int x, int y){
width = x;
height = y;
panel = new RenderPanel(width, height);
fps = 30;
isSingle = true;
input = new InputHandler();
addKeyListener(input);
setFocusable(true);
add(panel);
}
public void setPlayer(Player p){
player = p;
curRoom.addPlayer(p);
}
public void setFps(int f){
fps = f;
}
public void initialize(){
curTime = System.currentTimeMillis();
delta = curTime;
}
public void refresh(){
delta = (int)(System.currentTimeMillis() - curTime);
if(delta > 1000 / fps){
curTime = System.currentTimeMillis();
render();
tick();
}
}
public void setRoom(Room r){
curRoom = r;
}
public void render(){
panel.renderStart(curRoom);
panel.renderRoom(curRoom);
panel.renderEnd();
panel.repaint();
}
public void tick(){
curRoom.tick(this);
}
}
那么为了正确使用EDT我想做什么?我使用invokeLater()
和invokeAndWait()
的不同组合尝试了一些事情,但无法让它们发挥作用。如果您能提供帮助,我们非常感谢您的帮助。
编辑:这是RenderPanel
:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
BufferedImage drawer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g1 = drawer.getGraphics();
g1.drawImage(canvas, 0, 0, this);
g2.drawImage(drawer, 0, 0, this);
g1.dispose();
g2.dispose();
}
那不是双缓冲吗?
答案 0 :(得分:1)
Graphics
个上下文,处理系统Graphics
上下文会影响其他组件的绘制方式,请记住,您可能不是唯一被绘制的内容绘制周期和Graphics
上下文是共享资源repaint
是一种线程安全的方法。 paint事件发布在Event Queue上,由Event Dispatching Thread处理,所以除非你做了一些可怕的错误(打印是规则的一个可能的例外),否则绘制将始终在事件调度的上下文中进行螺纹BufferedImage
内向paintComponent
呈现没有任何意义,Swing组件已经被双重缓冲。如果你想实现页面翻转,那么你应该绘制到多个BufferedImage
,但应该更新EDT之外的屏幕外缓冲区JApplet
和JFrame
给予了很多控制,但是它们都没有,它们应该只是容器的RenderPanel
首先简化渲染/更新过程。我考虑使用Swing Timer
作为更新过程的主要引擎,它很简单,它触发了EDT中的更新,这意味着可以安全地更改游戏状态而不必担心竞争条件和脏画。它允许您直接利用paintComponent
方法。
当您运行基本流程时,您可以探索更多高级更新引擎(例如使用Thread
)和页面翻转技术
更改程序的结构,将核心程序与上层视图要求分开(JApplet
和JFrame
应该只是实际程序的容器,并且只包含足够的逻辑来构造和显示该计划)
进一步将游戏逻辑分成它自己的没有渲染逻辑的类。将控制器与渲染视图分开。 API将向控制器提供信息(例如鼠标事件和关键事件),这将更新模型。控制器还可以作为主要更新引擎,安排更新周期,以便根据需要更新模型和视图。
有关详细信息,请参阅Model-View-Controller。
答案 1 :(得分:0)
调用重绘会在EDT上排队一个事件。
如果您遇到闪烁,我会看看渲染方法实际执行的时间长度(并且可能渲染到屏幕外的BufferedImage并让您的绘制方法只绘制它。),在您绘制的bufferedimage之间切换以及渲染完成后在屏幕上绘制的缓冲图像。 (这称为双缓冲。)
你可以使用BufferStrategy(p.s. im我不是100%肯定,如果它适用于applet或者它需要一个完整的窗口。)
这就是假设你没有做任何愚蠢的事情,例如抓住传递给paint方法的图形上下文,然后在其他方法中使用它(比如你的渲染室方法)或调用“getGraphics( “关于这些方法的任何组成部分。
一种检查你所在线程的懒惰方式是打印出来
Thread.currentThread().getName()
或
Thread.dumpStack();
在直接访问图形对象的方法中。