1

複数のスレッドを使用して、opengl-esを使用してAndroidでゲームを作成しています:

class World{

    protected static final AtomicInteger entityLock = new AtomicInteger();

    private GameEntity entities[];

    public World(){
        // populate game world with entities
        // executed on main thread
        addEntity(new GameEntity("tank"));
        addEntity(new GameEntity("rifleman"));
        addEntity(new GameEntity("rifleman"));
    }

    void update(){
        synchronized(entityLock){
           for(int i = 0;i<entities.length;i++){
                // move entity to new position
                // executed on PhysThread
                entities[i].updatePosition();                    
            }
        }
        if(entity.isDead(){
            // remove entity. Enter sync block inside removeEntity() method
            removeEntity(entity);                  
        }                      
    }

    void draw(GL10 gl){
        synchronized(entityLock){
            for(int i = 0;i<entites.length;i++){
                // draw models
                // executed on GLThread
                Vector3 entityPosition = entities[i].getPosition();
                gl.glTranslatef(entityPosition.x, entityPosition.y, entityPosition.z);
                entities[i].draw();
            }
        }
    }

    public void addEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

    public void removeEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

} 

class MyRenderer implements GLSurfaceView.Renderer{

    World world;

    public MyRenderer(World world){
        this.world = world;
    }


    public void onDrawFrame(GL10 gl) {
        // executed on GLThread
        world.draw(gl);             
    }


}

class PhysThreadRunnable implements Runnable{

    private long tickRate = 30;

    private World world;

    private PhysThreadRunnable(World world){
        this.world = world;
    }

    protected void setTickRate(long tickRate){
        this.tickRate = tickRate;
    }

    public void run() {
        while(true){                
            try {
                // executed on PhysThread
                world.update();
                Thread.sleep(1000/tickRate);
            } catch (InterruptedException e) {
                return;
            }

        }
    }
}

MyActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        World world = new World(); 
        // sets up the game world, populates it with entities

        // set up GLSurfaceView (simplified)
        setContentView(R.layout.main);
        GLSurfaceView mGLView = findViewById(R.id.myGLSurfaceView);
        mGLView.setRenderer(new MyRenderer(world));

        // start phys thread
        PhysThreadRunnable physThreadRunnable = new PhysThreadRunnable(world);
        Thread physThread = new Thread(physThreadRunnable);
        physThread.start();
    }
}

ゲームを開始すると、(毎回ではありませんが) ときどき PhysThread がロックが解放されるのを待ってスタックするという問題があります (つまり、スレッドをデバッグして一時停止すると、スレッドがsynchronized(entityLock)内部に留まっているだけです)。update()

本当に奇妙なのは、しばらくすると (2 秒から 1 分の間)、PhysThread のブロックが解除され、スレッド ループの数回以上の反復でどちらのスレッドもロックアウトされることなくゲームが続行されることです。(つまり、ゲームは正常に実行されます)

編集:それが問題の原因である場合に備えて、例にいくつかの追加のものを追加しました。基本的に、単一のエンティティではなく、エンティティの配列を更新して描画する

4

2 に答える 2

2

ここでの問題は、おそらく「同期」ブロックによって保証される公平性がないことだと思います。

OpenGLスレッドは常に継続的にレンダリングされるため、終了するとすぐにonDrawに再入ろうとします。同期ブロックに入ることができるスレッドの選択は任意であるため、OpenGLスレッドが物理スレッドに解放される前にロックを再取得しようとし、任意の基準に基づいてロックオーバーが与えられる可能性があります。物理スレッドが入らないようにします。

これは、恣意的な決定であるため、他の場合ではなく、いつか発生する理由を説明している可能性があります。

同期ブロックの代わりにフェアロックを実装するか、OpenGLが最後の物理更新以降にシーンを2回以上再描画しないようにする(更新が行われるまでレンダリングスレッドをスリープ状態にする)ようにすることができます。

于 2012-07-25T05:57:13.253 に答える
0

結局、私はチートソリューションに行きました。エンティティ配列へのアクセスの周りに同期ブロックを配置し、ArrayIndexOutOfBounds を使用して、for ループを try/catch 内に配置します。

void update(){
    try{
        for(int i = 0;i<entities.length;i++){               
            GameEntity entity = entities[i];                
            synchrnonized(entity){
                entity.updatePosition();
            }               
        }
    }catch(ArrayIndexOutOfBoundsException aioob){
        if(tryAgain){
            update();
        } else {
            return;
        }
    }
}

この解決策の問題点は、がまったく関係のないものからentity.updateposition()をスローするArrayIndexOutOfBoundsExceptionと、それをキャッチして誤って解釈してしまうことです。さらに、全体が少し面倒で、たまにフレームや更新がスキップされます

これで問題が解決したように見えるので、元の原因はおそらく私のコードのどこか深いところにあるのではないかと思います. .

他の誰かがより良い解決策を持っている場合に備えて、私は数日間質問に答えないままにします

于 2012-07-26T15:23:50.827 に答える