LibGDX 繁重处理期间加载屏幕
LibGDX loading screen during heavy processing
长话短说:
我正在尝试在 LibGDX 中制作加载屏幕,不仅需要加载纹理和音频等资产,还需要创建世界对象(超过 1000 万个对象)——我可能想研究减少未来的对象数量,但我的问题仍然适用,无论是一个对象还是数万亿。
在对象初始化期间,GDX render()
方法停止,我认为是由于延迟。它会滞后整个应用程序进入 "Not responding" 几秒钟。
我已经研究了几个月,但没有找到太多东西。我发现的话题或我问的人中,90% 的人总是说同样的话;使用资产管理器。我试过这个,但它似乎只支持资产,而不是繁重的处理世界对象。有人告诉我它可以支持自定义 类,但是由于缺少文档,我从来没有使用它。
与我最相似的最佳主题是这个 ,它让我想到了在 Gdx.app.postRunnable() 中使用 Gdx.app.postRunnable()。结果是这样的:
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
// do some heavy processing
// set loading screen percent to x%
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
// do some more heavy processing
// set loading screen percent to y%
}
});
}
});
解决方案效果很好。它做了繁重的处理,设置加载屏幕的百分比,然后绘制它。所以为了展示,这个解决方案解决了我从未绘制百分比的问题。但是,这个方案还是把应用变成了"Not responding"之间的重进程;这会冻结最终的音乐播放。
通过在 postRunnables 中添加足够的 postRunnables,将不会有延迟,因为繁重的进程不再存在 - 相反,它建立在繁重的进程拆分成小进程的基础上,解决"Not responding" 状态。虽然这么多postRunnable对于"clean code"来说不太实用,因为需要30多个postRunnable,而上面的代码只有2个,代码转起来很容易丑陋,因此我寻求替代方案。
This post 也很有趣,解释了我面临的完全相同的问题,但是结果没有成功。
我在 Java Swing 中通过两个线程实现了这一点;一个主线程和一个 loadingScreen 线程 (LCThread)。当进入 loadingScreen 时,LCThread 开始绘制 loadingScreen,而主线程进行繁重的处理。完成后,主线程使用它之前处理的对象。遗憾的是我无法将其转换为 LibGDX,因为两个线程无法单独绘制。
短篇小说: 我需要编写一个加载屏幕,在绘制进度(在 render()
方法中处理)时加载繁重的后台处理(初始化大量对象) ) 并在显示加载屏幕时播放音乐,所有这些都不会延迟应用程序进入 "Not responding".
你有什么建议吗?
您可以拆分 GameObject 以释放处理器。希望这会分块加载并保持渲染循环自由播放音乐。基本上所有加载和创建资产都通过 AssetManager 并在渲染循环中检查游戏处于什么状态并采取相应的行动。
自定义游戏对象加载器。 GameObject 只是一个通用的 class 将其应用于您的项目规范。
在 AssetManger update( int millis ) 方法中,它将 CPU 产生到指定的毫秒数。如果您分解所有处理并将其放入他们自己的 AssetLoaders 中,AssetManager 会在该时间更新并且不会阻止 cpu.
public class GameObjectLoader extends SynchronousAssetLoader<GameObject, GameObjectLoader.GameObjectParameters> {
public GameObjectLoader( FileHandleResolver resolver ) {
super( resolver );
}
@Override
public GameObject load( AssetManager assetManager, String fileName, FileHandle file, GameObjectParameters parameter ) {
TextureAtlas atlas = assetManager.get( parameter.src, TextureAtlas.class );
ShaderProgram shaderProgram = assetManager.get( parameter.shaderSrc, ShaderProgram.class );
JsonValue json = assetManager.get( parameter.jsonSrc, JsonValue.class );
Calculation calculation = assetManager.get( parameter.id, Calculation.class );
GameObject gameObject = new GameObject(
atlas.findRegion( parameter.name ),
shaderProgram,
json,
calculation
);
assetManager.unload( parameter.id ); // unload it otherwise it stays in memory
return gameObject;
}
@Override
public Array<AssetDescriptor> getDependencies( String fileName, FileHandle file, GameObjectParameters parameter ) {
Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
dependencies.add( new AssetDescriptor<TextureAtlas>( parameter.src, TextureAtlas.class ) );
dependencies.add( new AssetDescriptor<ShaderProgram>( parameter.shaderSrc, ShaderProgram.class, parameter.shaderParameter ) );
dependencies.add( new AssetDescriptor<JsonValue>( parameter.jsonSrc, JsonValue.class ) );
dependencies.add( new AssetDescriptor<Calculation>( parameter.id, Calculation.class ) );
return dependencies;
}
public static class GameObjectParameters extends AssetLoaderParameters<GameObject> {
// maybe you have a lot of game logic and dont need to load everything from disk make a custom loader for that too
public String id = "";
public String src = "";
public String name = "";
public String jsonSrc = "";
public String shaderSrc = "";
public ShaderProgramLoader.ShaderProgramParameter shaderParameter = null;
}
}
AssetLoaders 不需要有文件就可以使用它仍然可以工作。
class CalculationLoader extends SynchronousAssetLoader<Calculation, AssetLoaderParameters<Calculation>> {
public CalculationLoader( FileHandleResolver resolver ) {
super( resolver );
}
@Override
public Calculation load( AssetManager assetManager, String fileName, FileHandle file, AssetLoaderParameters<Calculation> parameter ) {
// this is the heavy processing
// the AssetManager dictates how many of these per cycle will be calculated
return new Calculation();
}
@Override
public Array<AssetDescriptor> getDependencies( String fileName, FileHandle file, AssetLoaderParameters<Calculation> parameter ) {
return null;
}
public static class CalculationParameters extends AssetLoaderParameters<Calculation> {
}
}
长话短说: 我正在尝试在 LibGDX 中制作加载屏幕,不仅需要加载纹理和音频等资产,还需要创建世界对象(超过 1000 万个对象)——我可能想研究减少未来的对象数量,但我的问题仍然适用,无论是一个对象还是数万亿。
在对象初始化期间,GDX render()
方法停止,我认为是由于延迟。它会滞后整个应用程序进入 "Not responding" 几秒钟。
我已经研究了几个月,但没有找到太多东西。我发现的话题或我问的人中,90% 的人总是说同样的话;使用资产管理器。我试过这个,但它似乎只支持资产,而不是繁重的处理世界对象。有人告诉我它可以支持自定义 类,但是由于缺少文档,我从来没有使用它。
与我最相似的最佳主题是这个
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
// do some heavy processing
// set loading screen percent to x%
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
// do some more heavy processing
// set loading screen percent to y%
}
});
}
});
解决方案效果很好。它做了繁重的处理,设置加载屏幕的百分比,然后绘制它。所以为了展示,这个解决方案解决了我从未绘制百分比的问题。但是,这个方案还是把应用变成了"Not responding"之间的重进程;这会冻结最终的音乐播放。
通过在 postRunnables 中添加足够的 postRunnables,将不会有延迟,因为繁重的进程不再存在 - 相反,它建立在繁重的进程拆分成小进程的基础上,解决"Not responding" 状态。虽然这么多postRunnable对于"clean code"来说不太实用,因为需要30多个postRunnable,而上面的代码只有2个,代码转起来很容易丑陋,因此我寻求替代方案。
This post 也很有趣,解释了我面临的完全相同的问题,但是结果没有成功。
我在 Java Swing 中通过两个线程实现了这一点;一个主线程和一个 loadingScreen 线程 (LCThread)。当进入 loadingScreen 时,LCThread 开始绘制 loadingScreen,而主线程进行繁重的处理。完成后,主线程使用它之前处理的对象。遗憾的是我无法将其转换为 LibGDX,因为两个线程无法单独绘制。
短篇小说: 我需要编写一个加载屏幕,在绘制进度(在 render()
方法中处理)时加载繁重的后台处理(初始化大量对象) ) 并在显示加载屏幕时播放音乐,所有这些都不会延迟应用程序进入 "Not responding".
你有什么建议吗?
您可以拆分 GameObject 以释放处理器。希望这会分块加载并保持渲染循环自由播放音乐。基本上所有加载和创建资产都通过 AssetManager 并在渲染循环中检查游戏处于什么状态并采取相应的行动。 自定义游戏对象加载器。 GameObject 只是一个通用的 class 将其应用于您的项目规范。
在 AssetManger update( int millis ) 方法中,它将 CPU 产生到指定的毫秒数。如果您分解所有处理并将其放入他们自己的 AssetLoaders 中,AssetManager 会在该时间更新并且不会阻止 cpu.
public class GameObjectLoader extends SynchronousAssetLoader<GameObject, GameObjectLoader.GameObjectParameters> {
public GameObjectLoader( FileHandleResolver resolver ) {
super( resolver );
}
@Override
public GameObject load( AssetManager assetManager, String fileName, FileHandle file, GameObjectParameters parameter ) {
TextureAtlas atlas = assetManager.get( parameter.src, TextureAtlas.class );
ShaderProgram shaderProgram = assetManager.get( parameter.shaderSrc, ShaderProgram.class );
JsonValue json = assetManager.get( parameter.jsonSrc, JsonValue.class );
Calculation calculation = assetManager.get( parameter.id, Calculation.class );
GameObject gameObject = new GameObject(
atlas.findRegion( parameter.name ),
shaderProgram,
json,
calculation
);
assetManager.unload( parameter.id ); // unload it otherwise it stays in memory
return gameObject;
}
@Override
public Array<AssetDescriptor> getDependencies( String fileName, FileHandle file, GameObjectParameters parameter ) {
Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
dependencies.add( new AssetDescriptor<TextureAtlas>( parameter.src, TextureAtlas.class ) );
dependencies.add( new AssetDescriptor<ShaderProgram>( parameter.shaderSrc, ShaderProgram.class, parameter.shaderParameter ) );
dependencies.add( new AssetDescriptor<JsonValue>( parameter.jsonSrc, JsonValue.class ) );
dependencies.add( new AssetDescriptor<Calculation>( parameter.id, Calculation.class ) );
return dependencies;
}
public static class GameObjectParameters extends AssetLoaderParameters<GameObject> {
// maybe you have a lot of game logic and dont need to load everything from disk make a custom loader for that too
public String id = "";
public String src = "";
public String name = "";
public String jsonSrc = "";
public String shaderSrc = "";
public ShaderProgramLoader.ShaderProgramParameter shaderParameter = null;
}
}
AssetLoaders 不需要有文件就可以使用它仍然可以工作。
class CalculationLoader extends SynchronousAssetLoader<Calculation, AssetLoaderParameters<Calculation>> {
public CalculationLoader( FileHandleResolver resolver ) {
super( resolver );
}
@Override
public Calculation load( AssetManager assetManager, String fileName, FileHandle file, AssetLoaderParameters<Calculation> parameter ) {
// this is the heavy processing
// the AssetManager dictates how many of these per cycle will be calculated
return new Calculation();
}
@Override
public Array<AssetDescriptor> getDependencies( String fileName, FileHandle file, AssetLoaderParameters<Calculation> parameter ) {
return null;
}
public static class CalculationParameters extends AssetLoaderParameters<Calculation> {
}
}