Libgdx中render()方法重复调用问题的解决咨询
LibGDX中SplashScreen的render方法重复调用导致IllegalStateException及重复创建屏幕问题
在基于LibGDX开发的SplashScreen类中,出现render()方法在上一次调用完成前被几乎同时调用两次的情况,触发IllegalStateException异常。即使移除AssetManager相关逻辑,新屏幕仍会被创建两次。现寻求解决该重复渲染问题的方案。
相关代码
public class SplashScreen extends AbstractScreen { private float countDown; private final GameManager gameManager; boolean updating = false; public SplashScreen(Main main) { super(main); gameManager = GameManager.get(); Label loading = new Label("Loading", new Label.LabelStyle(new BitmapFont(), Color.BLACK)); Table table = new Table(); table.setFillParent(true); table.center(); table.add(loading).expand(); stage.addActor(table); countDown = 0.5f; } @Override public void show() { super.show(); main.loadManager(); } @Override public void render(float delta) { super.render(delta); if (updating) throw new IllegalStateException(); updating = true; countDown -= delta; if (countDown < 0.0f && gameManager.assetManager.update()) { main.onLoadManager(); ScreenManager.get().showScreen(ScreenManager.ScreenEnum.MAIN_MENU); } updating = false; } }
异常信息
Exception in thread "main" java.lang.IllegalStateException at avseygames.convey.screens.SplashScreen.render(SplashScreen.java:51) at com.badlogic.gdx.Game.render(Game.java:48) at avseygames.convey.Main.render(Main.java:41) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics.renderWindow(Lwjgl3Graphics.java:103) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics.access$000(Lwjgl3Graphics.java:45) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics$1.invoke(Lwjgl3Graphics.java:80) at org.lwjgl.glfw.GLFWFramebufferSizeCallbackI.callback(GLFWFramebufferSizeCallbackI.java:44) at org.lwjgl.system.JNI.invokePPV(Native Method) at org.lwjgl.glfw.GLFW.glfwSetWindowMonitor(GLFW.java:3017) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics.setFullscreenMode(Lwjgl3Graphics.java:446) at avseygames.convey.manage.GameManager.setScreenSize(GameManager.java:78) at avseygames.convey.Main.onLoadManager(Main.java:28) at avseygames.convey.screens.SplashScreen.render(SplashScreen.java:56) at com.badlogic.gdx.Game.render(Game.java:48) at avseygames.convey.Main.render(Main.java:41) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window.update(Lwjgl3Window.java:387) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.loop(Lwjgl3Application.java:193) at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.<init>(Lwjgl3Application.java:167) at avseygames.convey.DesktopLauncher.main(DesktopLauncher.java:15) FAILURE: Build failed with an exception.
问题分析与解决方案
从异常栈可以看出,问题根源在于:main.onLoadManager()中调用的setScreenSize最终触发了glfwSetWindowMonitor,而GLFW在窗口尺寸/显示器变更时会同步触发GLFWFramebufferSizeCallback回调,LibGDX的Lwjgl3后端会在该回调中额外调用一次render方法,导致当前SplashScreen的render被重复执行。
修改方案
新增一个标志位标记屏幕切换是否已触发,避免重复执行切换逻辑:
public class SplashScreen extends AbstractScreen { private float countDown; private final GameManager gameManager; private boolean hasTriggeredSwitch = false; // 新增标志位 public SplashScreen(Main main) { super(main); gameManager = GameManager.get(); Label loading = new Label("Loading", new Label.LabelStyle(new BitmapFont(), Color.BLACK)); Table table = new Table(); table.setFillParent(true); table.center(); table.add(loading).expand(); stage.addActor(table); countDown = 0.5f; } @Override public void show() { super.show(); main.loadManager(); } @Override public void render(float delta) { super.render(delta); if (hasTriggeredSwitch) return; // 已触发切换则直接返回 countDown -= delta; if (countDown < 0.0f && gameManager.assetManager.update()) { hasTriggeredSwitch = true; // 标记切换已启动 main.onLoadManager(); ScreenManager.get().showScreen(ScreenManager.ScreenEnum.MAIN_MENU); } } }
额外建议
- 尽量避免在
render方法中执行可能触发窗口变更的操作,此类操作可移至新屏幕的show方法中执行 - 如果必须在加载完成后调整窗口尺寸,可考虑将尺寸变更逻辑异步处理,避免同步触发渲染回调
内容的提问来源于stack exchange,提问作者Vyacheslav Adamovich
相关产品推荐
相关产品推荐

