2012-07-24 37 views
1

我使用的OpenGL ES制作游戏中的Android与GLThread同步,使用多线程: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(); 
    } 
} 

我有一个有时(但不是每次),当我开始比赛的一个问题,物理线程被卡住等待锁被释放(即当我去调试并暂停线程时,它只是坐在synchronized(entityLock)里面update()

真奇怪的是,过了一会儿(2秒到1分钟之间),PhysThread将被解锁,并且游戏将继续进行而不会有任何线程被锁定超过几次线程循环的口粮。 (即游戏运行良好)

编辑:我在示例中添加了一些额外的东西,以防万一是导致问题的原因。基本上,更新和绘制一个实体阵列而不是一个实体

+0

改为使用LibGDX。它已经集成了openGL和box2d。它的水平也相当低。看看它,看看我刚刚救了你多少时间。 – 2012-07-24 21:44:26

+0

那么上面的代码只是示例代码。我已经花了大约一年时间制作我的引擎。所以重新开始并不是真的在卡片上。我只想知道这是一个普通的同步问题还是Android的特定问题GLSurfaceView – 2012-07-25 00:34:06

回答

0

最后,我去了作弊解决方案。我把同步块左右进入实体阵列,把for循环使用try/catch用的ArrayIndexOutOfBounds内:

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完全无关,那么我会抓住它并曲解它。此外,整个事情有点混乱,并在一段时间内,一帧或更新跳过

由于这似乎解决了这个问题,我怀疑最初的原因可能在于我的代码深处,在进入for循环和实际修改的实体,我真的不认为这是公平的转储我的整个代码。

如果其他人有更好的解决方案,我将不会回答这个问题

2

我认为这里的问题可能是没有公平性的'同步'块保证。

OpenGL线程将始终呈现不断,所以它会尝试重新进入图像,一旦它完成。由于允许哪个线程进入同步块的选择是任意的,因此OpenGL线程可能会尝试在锁释放到物理线程之前重新获取该锁,并且根据某些任意标准给出锁定并且不允许物理线程进入。

这也许可以解释为什么它会发生在某个时间而不是其他的,因为这是一个任意的决定。

你可能会尝试实现一个公平的锁,而不是同步块,或者使得OpenGL自上次物理更新后不会尝试重画场景多次(将渲染线程置于睡眠状态直到更新发生)。

+0

我不认为这是正确的。 1.等待同步(物理线程)将在ondraw完成之前获得锁定。2. gl线程需要一段时间才能再次调用ondrawframe,因为它正忙于渲染(发生在ondrawframe退出后)。 – Aert 2012-07-25 06:15:33

+0

我可以睡觉GLThread,直到物理更新完成。但是,我不妨将所有内容都放在一个线程中。不同的事情会在游戏的不同时间更新。例如,我可能会引入第三个线程来更新游戏中的工厂,以便每秒钟产生并消耗一次,并给它一个较低的优先级。或者当游戏暂停时,暂停物理线程很容易,但让GLThread保持运行,以便玩家仍然可以平移相机,缩放等 – 2012-07-25 09:14:52