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> {


    }
}