2011-12-15 17 views
2

我對android和遊戲開發都很陌生,並且一直試圖創建一個pong克隆來掌握一切。我有一個擴展SurfaceView的「PongView」類和一個擴展了線程的「PongThread」。當我的遊戲重新啓動時,我應該如何處理重置SurfaceView和線程?

我發現了一種方法來檢測球或「炸彈」是否已經通過槳併到達了它後面的牆。我不確定我的方法是否是最好的(意見表示歡迎),但它似乎工作,因爲如果它發生,我有一點敬酒信息顯示遊戲結束。 現在我想設置它,除了顯示遊戲結束後,視圖重新開始(或更合適的術語),以使炸彈再次位於屏幕的中心,用戶可以再玩一次。

由於經驗不足,我不太確定我應該如何處理線程以及在視圖中調用哪些方法來實現此目的。

我已經試圖找出它的幾個刺,但我不知道如果我在正確的軌道上,它可能會很清楚從我的代碼,我不知道我是什麼,在做。 (此時我的線程停止,我的表面停留在顯示最後一幀)。歡迎任何建議!

PongView.java:(向下滾動到更新方法來獲得部分我試圖找出)

package biz.hireholly.pirateponggame; 

import android.content.Context; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.os.Handler; 
import android.view.MotionEvent; 
import android.view.SurfaceHolder; 
import android.view.SurfaceHolder.Callback; 
import android.view.SurfaceView; 
import android.widget.Toast; 


public class PongView extends SurfaceView implements Callback{ 

    /*GLOBALS*/ 
    private static final String TAG = PongView.class.getSimpleName(); 
    private PongThread pongThread; 
    private Bomb bomb; 
    private Paddle paddleA; 
    private int viewWidth; 
    private int viewHeight; 
    Handler handler; 
    boolean gameOver; 

    public PongView(Context context) { 


     super(context); 
     // sets current class as the handler for the events happening on the surface 
     getHolder().addCallback(this); 


     //instantiate thread, pass the current holder and view so that the thread can access them 
     pongThread = new PongThread(getHolder(), this); 

     //make the GameView focusable so it can handle events 
     setFocusable(true); 

     handler = new Handler(); 
    } 

    //CURRENTLY WHERE IM INITIALISING SPRITES 
    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 

     //INITIALISE viewWidth and viewHeight here 
     //so that they can be passed as parameters 
     viewWidth = getWidth(); 
     viewHeight = getHeight(); 


     //NEW BOMB, INITIAL BITMAP 
     bomb = new Bomb(BitmapFactory.decodeResource(
       getResources(), R.drawable.bombsprite), 
       getWidth() /2, getHeight() /2); //draws in middle 
     //bombs random starting direction 
     bomb.getSpeed().randomiseDirection(); 

     //NEW PADDLE, INITIAL BITMAP 
     paddleA = new Paddle(BitmapFactory.decodeResource(
         getResources(), R.drawable.paddlesprite), 
         getWidth() /2, getHeight() -(getHeight()/30)); //middle bottom screen 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     pongThread = new PongThread(getHolder(), this); //needed if user exits and returns 
     pongThread.setRunning(true); 
     //.start() == PongThread.run() except PongThread does all the work 
     pongThread.start(); 
    } 


    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     boolean retry = true; 
     while(retry){ 
      try{ 
       //tells thread to shut down and waits for it to finish. Clean shutdown 
       pongThread.setRunning(false); 
       pongThread.join(); 
       retry = false; 
      } catch(InterruptedException e){ 
       //try again shutting down the thread 
      } 
     } 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) 
    { 
     paddleA.onTouchEvents(event, viewWidth); 

     return true; 
    } 


    /** 
    * CHECKS FOR COLLISIONS, CALLS OBJECT UPDATE METHODS 
    */ 
    public void update(){ 

     gameOver = false; 

     //CHECK IF NULL as objects aren't created till surface change 
     if(bomb != null && paddleA != null){ 

      bomb.handlePaddleCollision(paddleA); 
      gameOver = bomb.handleWallCollision(viewWidth, viewHeight); 

      //object physics updates 
      bomb.update(); 
      //paddleA.update(); 
     } 

     //WHEN handleWallColision returns true, one player scored and its game over. 
     if(gameOver){ 


      //handler is needed as alerts and toasts can only be made in the ui thread, 
      handler.post(new Runnable(){ 
       public void run(){ 
        Toast.makeText(getContext(), "GAME OVER", Toast.LENGTH_LONG).show(); 
       } 
      }); 

      /*THIS IS WHERE I'M TRYING TO RESTART THE GAME*/ 
      /////////////////////////////////////////////////////// 
      //could maybe call surfaceDestroyed here instead? 
      boolean retry = true; 
      while(retry){ 
       try{ 
        //tells thread to shut down and waits for it to finish. Clean shutdown 
        pongThread.setRunning(false); 
        pongThread.join(); 
        retry = false; 
       } catch(InterruptedException e){ 
        //try again shutting down the thread 
       } 
      } 

      //copied from surfaceCreated 
      pongThread = new PongThread(getHolder(), this); //needed if user exits and returns 
      pongThread.setRunning(true); 
      //.start() == PongThread.run() except PongThread does all the work 
      pongThread.start(); 

      //////////////////////////////////////////////////////// 
     } 
    } 

    protected void render(Canvas canvas) 
    { 
     canvas.drawColor(Color.GREEN); 
     bomb.draw(canvas); 
     paddleA.draw(canvas); 
    } 

} 

PongThread.java:

package biz.hireholly.pirateponggame; 

import android.graphics.Canvas; 
import android.util.Log; 
import android.view.SurfaceHolder; 

public class PongThread extends Thread { 

    /*GLOBALS*/ 
    private static final String TAG = PongThread.class.getSimpleName(); 
    //desired frames per second 
    private final static int MAX_FPS = 30; 
    //maximum number of frames to be skipped if drawing took too long last cycle 
    private final static int MAX_FRAME_SKIPS = 5; 
    //cycle period (cycle = update,draw,if excess time sleep) 
    private final static int CYCLE_PERIOD = 1000/MAX_FPS; 
    //Surface holder that can access the physical surface 
    private SurfaceHolder surfaceHolder; 
    //the view that actually handles inputs and draws to the surface 
    private PongView pongView; 
    //flag to hold game state 
    private boolean running;  

    public void setRunning(boolean running){ 
      this.running = running; 
    } 

    /**Takes PongView instance and surfaceHolder as parameters 
    * so that we can lock the surface when drawing. 
    * @param surfaceHolder 
    * @param pongView 
    */ 
    public PongThread(SurfaceHolder surfaceHolder, PongView pongView){ 
     super(); 
     this.surfaceHolder = surfaceHolder; 
     this.pongView = pongView; 
    } 

    @Override 
    public void run(){ 
     //canvas is a surface bitmap onto which we can draw/edit its pixels 
     Canvas canvas; 
     Log.i(TAG, "Starting pong thread"); 

     long beginTime =0; // time when cycle began 
     long timeDiff =0; // time it took for the cycle to execute 
     int sleepTime =0; // milliseconds to sleep (<0 if drawing behind schedule) 
     int framesSkipped =0; // number of frames skipped 

     while (running) { 
      canvas = null; 
      //try locking canvas, so only we can edit pixels on surface 
      try{ 
       canvas = this.surfaceHolder.lockCanvas(); 
       //sync so nothing else can modify while were using it 
       synchronized (surfaceHolder){ 
        beginTime = System.currentTimeMillis(); 
        framesSkipped = 0; //reset frame skips 

        //UPDATE game state 
        this.pongView.update(); 
        //RENDER state to screen, draws the canvas on the view 
        this.pongView.render(canvas); 

        //calculate how long cycle took 
        timeDiff = System.currentTimeMillis() - beginTime; 
        //calculate potential sleep time 
        sleepTime = (int)(CYCLE_PERIOD - timeDiff); 

        //sleep for remaining cycle 
        if (sleepTime >0){ 
         try{ 
          //saves battery 
          Thread.sleep(sleepTime); 
         } catch (InterruptedException e){} 
        } 

        //if there's no leftover cycle time then we're running behind 
        while (sleepTime < 0 
          && framesSkipped < MAX_FRAME_SKIPS){ 
         //we need to catch up so we update without rendering 
         this.pongView.update(); 
         sleepTime += CYCLE_PERIOD; 
         framesSkipped++; 
        } 
       } 
      } finally{ 
       //in case of exception, 
       //surface is not left in an inconsistent state 
       if (canvas != null){ 
        surfaceHolder.unlockCanvasAndPost(canvas); 
       } 
      }//end finally 
     } 
    } 
} 
+0

難道你不能簡單地開始一個新的線程再次執行相同的代碼? – Tudor

+0

這就是我試圖在我的更新方法結束時做的事情「pongThread = new PongThread(getHolder(),this); //如果用戶退出並返回,則需要 pongThread.setRunning(true); //。start() == PongThread.run()除了PongThread做所有的工作 pongThread.start();「。 – Holly

+0

它不工作?另請參閱我的答案。 – Tudor

回答

1

如果你想「重新啓動」這個話題,你有兩種選擇。 每次啓動一個新線程遊戲應該重啓或者無限期地構建您的初始線程至循環,並等待它能夠運行時通知。像這樣:

public void run() { 

    while(true) { 
     signal.wait(); 

     // your previous run code here 
    } 
} 

在這種情況下,您最初啓動線程,然後信號,它實際上與signal.notify()開始(信號必須是可見的目的是兩個線程)。然後,當遊戲結束時,線程將返回至wait的呼叫。然後再次通知它,以便從頭開始重新啓動。

+0

感謝您的信息。如果我認爲上述代碼在我的線程運行()方法是正確的,我仍然有點不確定我會在哪裏使用signal.notify ..如果我只能使用線程內的信號,那麼我仍然有一個線程和實際檢測遊戲結束的視圖之間的通信問題。我會對此有一個思考和嘗試,但我想我想知道如何從我的surfaceview開始一個新的線程..感謝您的幫助,任何進一步的建議也將非常感謝:) – Holly

+0

您可以從以前不得不開始新線程的視圖中調用notify。只需將其替換爲對signal.notify()的調用即可讓線程再次移動。 – Tudor

相關問題