Java Applet每帧调用方法咨询:GLFW窗口持续更新方案
解决Java Applet中LWJGL 3每帧更新的问题
首先,你遇到的核心问题是Applet的paint()方法是被动触发的——它只会在组件需要重绘时被调用(比如首次加载、窗口尺寸变化、被遮挡后恢复显示),并非主动的循环调用,所以仅靠它来触发window.update()肯定无法满足每帧更新的需求。
接下来给你几个可行的解决方案,结合LWJGL 3的上下文规则来设计:
方案1:后台线程实现游戏循环(推荐)
LWJGL 3的GLFW要求所有窗口操作(创建、事件处理、渲染)必须在同一个线程中执行,所以我们可以把窗口初始化、游戏循环都放在一个后台线程里,然后将渲染结果(BufferedImage)同步到AWT的事件调度线程(EDT)中绘制。
修改你的Main类如下:
package net.rabbit; import java.applet.Applet; import java.awt.Graphics; import java.awt.image.BufferedImage; import javax.swing.SwingUtilities; import xyz.rabbit.lib.framework.Window; public class Main extends Applet { private static final long serialVersionUID = 6092259155082054438L; private Window window; private BufferedImage currentFrame; @Override public void init() { super.init(); // 启动后台线程处理GLFW和游戏循环 new Thread(() -> { // 在该线程中初始化窗口(确保GLFW上下文归属此线程) window = new Window(); window.setParent(this); window.open(); // 游戏主循环 while (!window.shouldClose()) { // 处理GLFW事件、执行OpenGL渲染 window.update(); // 通过glReadPixels获取帧缓冲区并转换为BufferedImage currentFrame = window.captureFrame(); // 你需要在Window类中实现这个方法 // 切换到EDT线程触发重绘(AWT组件必须在EDT中绘制) SwingUtilities.invokeLater(this::repaint); // 控制帧率(示例为60帧/秒) try { Thread.sleep(1000 / 60); } catch (InterruptedException e) { e.printStackTrace(); break; } } // 清理资源 window.close(); }).start(); } @Override public void paint(Graphics g) { super.paint(g); // 绘制最新的帧图像 if (currentFrame != null) { g.drawImage(currentFrame, 0, 0, getWidth(), getHeight(), this); } } }
关键注意点:
Window类的captureFrame()方法需要实现:用glReadPixels读取当前帧缓冲区的像素数据,然后转换为BufferedImage(注意像素格式的匹配,比如GL_RGB对应BufferedImage.TYPE_INT_RGB)。- 所有GLFW/OpenGL操作必须在初始化窗口的后台线程中执行,不能跨线程调用,否则会触发上下文错误。
- 必须通过
SwingUtilities.invokeLater()在EDT中执行AWT的绘制操作,避免线程安全问题。
方案2:使用Swing Timer触发更新
如果不想手动管理线程,可以用javax.swing.Timer来定时触发更新,但要注意GLFW操作的线程归属:
@Override public void init() { super.init(); window = new Window(); window.setParent(this); window.open(); // 每16ms触发一次(约60帧/秒) new javax.swing.Timer(16, e -> { // 注意:Timer的回调在EDT中执行,如果window.update()包含GLFW操作,可能会阻塞EDT // 所以更推荐把GLFW操作放在后台线程,这里只负责触发重绘 window.update(); currentFrame = window.captureFrame(); repaint(); }).start(); }
不过这种方案的缺点是:EDT是负责UI交互的线程,如果window.update()耗时较长,会导致UI卡顿,所以方案1是更稳定的选择。
额外提示
Java Applet已经被官方废弃,现代浏览器大多不再支持,如果你的项目有迁移空间,建议考虑改用Java Web Start或者独立的Swing/AWT应用程序,这样会有更灵活的线程控制和更好的兼容性。
内容的提问来源于stack exchange,提问作者scewps
相关产品推荐
相关产品推荐

