1

背景・概要

私のアプリはopenGLゲームです(AndroidおよびJavaで書かれています)。

リソースをどのようにロードすればよいか (つまり、ビットマップのロード、オブジェクトの作成など) がわかりません。

私は現在、onSurfaceCreated メソッドでこのすべての作業を行っています (たとえば、以下を参照)。これは、Nexus 10 タブレットで問題なく動作し、すべてが 1 秒未満で完了します。ただし、古いハンドセット (Galaxy Ace) で実行すると、何かが起こる前に約 3 ~ 4 秒間空白の画面が表示されます。これは受け入れがたいと思うので、それを回避する方法を見つけようとします。

単純なスプラッシュを配置できることはわかっていますが(「読み込み」を表示するだけでも)、それはビジネスですが、ここでの私の質問は次のとおりです。

1)すべてがロードされる前に何かを表示するにはどうすればよいですか? つまり、すべてが onSurfaceCreated にロードされており、onDrawFrame は onSurfaceCreated の後まで実行されません。では、リソースがまだロードされている間に何かを表示するにはどうすればよいでしょうか?

2)シンプルなスプラッシュを表示する場合。新しい/より高速なデバイスでは、それは少しゴミに見えませんか? つまり、新しい携帯電話/タブレットではすべてが非常に高速に読み込まれるため、スプラッシュは一瞬だけ表示されます。より高速なデバイスで必要以上に長く画面にとどまらせる必要がありますか?

3)最後に、関連する (そして非常に重要な) 問題 - 古いハンドセットで、ゲームが最終的に表示された後。約 10 ~ 15 秒間、すべてが断続的にぎくしゃくしたり、途切れたりします。電話は明らかにバックグラウンドで何かを行っています。(電話がグラフィックスを処理できないというわけではありません。10 ~ 15 秒後、すべてがバターのようにスムーズに実行されます) ここで何が起こっているのか、または問題を追跡する方法についての手がかりをいただければ幸いです!! :-)

補足:私は SO やより広いインターネットに関するさまざまな同様の質問を読みましたが、それらはすべて XML レイアウトの使用を伴うようです (そして、実装や成功が非常に簡単ではないようです)。私のアプリでは XMLを使用しておら、可能であれば XML を使用せずにコードでこの問題を解決したいと考えています。

コード例

public void onSurfaceChanged(GL10 gl, int width, int height) {

//The following are performed once at game load
//All resources are being loaded / created here
//Load graphics files
res.loadResources(view);
//Create game objects (sprites, collision detection etc)
res.createObjects(view, width, height);
//Recycle bitmaps as they are no longer required
res.Recycle();
//Set initial values variables (required for subsequent methods)
res.setInitialValues();
//Set level layout
res.setLevel();
}
4

2 に答える 2

5

1)リソースの読み込み中にスプラッシュ スクリーンを表示する場合は、バックグラウンド スレッドでできるだけ多くの読み込みを行う必要があります。GL スレッドはリソースの読み込みで忙しくないため、必要なことを何でもできるように GL スレッドを解放することをお勧めします。ただし、テクスチャのアップロードやシェーダーのコンパイルなど、OpenGL との直接的なやり取りはすべて GL スレッドで行う必要があります。

通常AsyncTask、UI スレッドと対話する必要があるバックグラウンド リソースの読み込みに を使用します。ただし、GLSurfaceViewGL のメイン UI スレッドを使用しないため、AsyncTaskここでは機能しません。ただし、他の方法で同様の動作をかなり簡単に取得できます。リソース コードを独自のバックグラウンド スレッドにロードし、GLSurface.queueEvent(). バックグラウンドでのリソースの読み込みは、さまざまな方法で実行できます。この例では、バックグラウンド スレッドが 1 つの Executor を使用してみましょう。すべてが にロードされonSurfaceChanged()、ユーザーがアプリケーションを一時停止/停止し、開始/再開した場合に再作成されることが保証されます。

private Executor mExecutor = Executors.newSingleThreadExecutor();

@Override
public void onSurfaceChanged(final int width, final int height) {
    // Let's define a few states for the application; NONE, SPLASH,
    // GAME.
    mRenderState = NONE;

    // Currently, the screen is black. Let's get something on screen as
    // quickly as possible. Create a thread and start loading resources.
    mExecutor.execute(new Runnable() {
        @Override
        public void run() {
            final Resources res = mContext.getResources();

            // Load splash screen resource on background thread.
            final Bitmap splashBitmap = BitmapFactory.decodeResource(res, R.drawable.splash);

            // Splash screen is loaded, post a Runnable that will run on the GL thread.
            mGLSurfaceView.queueEvent(new Runnable() {
                @Override
                public void run() {
                    // Upload splash texture to GL, on GL thread.
                    GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, splashBitmap, 0);
                    // Recycle bitmap. We'll create a lot of bitmaps during loading.
                    // Each bitmap is only needed until we have uploaded it to GL. After
                    // that it's just wasting memory. Especially on older phones.
                    splashBitmap.recycle();
                    // Notify application that the splash screen is loaded,
                    // and it's okay to render it.
                    mRenderState = SPLASH;

                    // Screen is no longer black. User sees a splash screen,
                    // perhaps with a progress bar or a nice animation.

                    // Start loading the other textures.
                    // ...
                    // Texture loading finished.
                    mRenderState = GAME;
                }
            });
        }
    });
}

@Override
public void onDrawFrame()
{
if (mRenderState == SPLASH) {
        // Render splash screen and a progress bar.
    } else if (mRenderState == GAME) {
        // Render game.
    }
}

2)スプラッシュ スクリーンの読み込みにかかる時間を測定し、高速デバイスと低速デバイスで比較できます。高速デバイスと同じくらい速くロードする場合は、mRenderState を SPLASH に設定しないでください。ユーザーには黒い画面が表示され続けます。うまくいけば、黒い画面は 1 秒以内にゲームに置き換えられます。必要に応じて、リソースの読み込み中にさらにタイミングを追加し、時間がかかりすぎる場合は SPLASH レンダリング ステートを有効にすることができます。

また、スプラッシュ スクリーンのフェード インとフェード アウトは、単に画面に表示されたり表示されなくなったりするだけでなく、ユーザーを不快にさせる可能性があります。

3)吃音につながる可能性のあることがいくつかありますが、あきらめる前に少なくとも次のことを確認します。

a)メモリ管理。Android ではプロセスごとのメモリ ヒープ サイズが制限されており、古いデバイスではプロセスごとのヒープ メモリが新しいデバイスよりも大幅に少なくなります (16 ~ 32 MB と 128 MB など)。つまり、多くの重いリソースをロードしている場合、ガベージ コレクターは古いデバイスでより多くの作業を行うことになります。次のような多くのメッセージについては、「logcat」を参照してください。

D/dalvikvm: GC_CONCURRENT freed 4027K, 30% free 29173K/41635K, paused 3ms+8ms

これらは単に、以前にロードされたビットマップがメモリからフラッシュされているか、古いデバイスで GC をトリガーする他の多くのオブジェクトを作成している可能性があります。ロード中にビットマップをリサイクルすることで、これが軽減されることを願っています。

b)以下を使用して、他のプロセスが CPU を占有しているかどうかを確認します。

adb shell top -m 10

これにより、上位 10 個のプロセスの CPU 使用率が表示されます。

c)「DDMS」からメソッド プロファイリングを実行します。ゲームがカクカクしている間に、「デバイス」タブの 3 つの矢印と赤い円のある小さなボタンを押します。数秒後にもう一度押すと、プロファイリング結果が得られます。グラフの読み方を学ぶには少し時間がかかりますが、基本的には、アプリケーションのどの部分が現在 CPU を使用しているかがわかります。おそらく、最初の 10 秒間は何かが懸命に働いているのでしょう。すべてがスムーズに実行されるプロファイリング結果と比較してください。

于 2013-06-16T17:39:30.610 に答える
0

1) 初期化の多くをAsyncTask. アクティビティが終了する前に、最初の画面は表示されませんonCreate()

2) はい、いいえ、どのように、何を、どこで行うかによって異なります。ロード情報が表示されたシンプルな画面は、ほとんどのユーザーにとって理解しやすいものです。とにかく、ほとんどのトップゲームには、何らかのブランド画面が付属しています。

3)コードをどのように実装したかはわかりませんが、この時点でライブラリまたはコードがまだ初期化を行っていると思います。

Romain Guy は、ここで初期読み込みを高速化するためのいくつかのトリックを共有しています。

于 2013-06-10T03:49:32.990 に答える