gpt4 book ai didi

java - 在 JApplet 中正确使用事件调度线程?

转载 作者:行者123 更新时间:2023-12-01 11:56:44 25 4
gpt4 key购买 nike

我制作了一个基于 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();
}

这不是双缓冲吗?

最佳答案

问题...

  • 从事件调度线程外部更新游戏状态时要小心,这可能会导致脏绘制,因为状态的一部分是在绘制过程中更新的
  • 不要处置不是您创建的Graphics上下文,处置系统Graphics上下文将影响其他组件的绘制方式,请记住,您可能不是绘制周期中唯一绘制的内容,并且 Graphics 上下文是共享资源
  • 由于其实现方式,repaint 是一种线程安全的方法。绘制事件发布在事件队列上,由事件调度线程处理,因此除非您做了一些严重错误的事情(打印可能是规则的异常(exception)),否则绘制将始终发生在事件调度的上下文中线程
  • paintComponent 中渲染到 BufferedImage 是没有意义的,Swing 组件已经是双缓冲的。如果您想实现页面翻转,那么您应该绘制多个 BufferedImage,但应该更新 EDT 之外的离屏缓冲区
  • 代码的基本结构没有意义,您给 JAppletJFrame 提供了很多控制权,但两者都不应该有任何控制,它们应该是无非是 RenderPanel
  • 的容器

可能的解决方案...

从简化渲染/更新过程开始。我会考虑使用 Swing Timer 作为更新过程的主要引擎,很简单,它会在 EDT 内触发更新,这意味着可以安全地更改游戏状态而无需担心关于竞争条件和肮脏的绘画。它允许您直接利用 paintComponent 方法。

当您运行基本流程时,您可以探索更高级的更新引擎(例如使用线程)和页面翻转技术

改变程序的结构,将核心程序与上层 View 需求分离(JAppletJFrame应该只是实际程序的容器,只包含足够的逻辑来构建和显示程序)

MVC

将游戏逻辑进一步分离到它自己的类中,该类没有渲染逻辑。还将 Controller 与渲染 View 分开。 API 将向 Controller 提供信息(例如鼠标事件和按键事件),从而更新模型。 Controller 还将充当主要更新引擎,调度更新周期,以便根据需要更新模型和 View 。

参见Model-View-Controller了解更多详情。

关于java - 在 JApplet 中正确使用事件调度线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28395526/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com