当我的游戏重启时,我该如何处理重置SurfaceView和Thread?

时间:2011-12-15 13:22:33

标签: java android multithreading surfaceview

我是Android和游戏开发的新手,并且一直在尝试创建一个pong克隆来掌握一切。我有一个“PongView”类,它扩展了SurfaceView和一个扩展线程的“PongThread”。

我找到了一种方法来检测球或“炸弹”是否已经越过桨并到达其后面的墙壁。我不确定我的方法是否是最好的(意见欢迎),但它似乎工作,因为我有一个小的祝酒消息显示游戏结果,如果它发生。 现在我想设置它,以便显示游戏结束,视图重新启动(或任何更合适的术语),以便炸弹再次位于屏幕的中心,用户可以播放另一个轮。

由于经验不足,我不确定我应该如何处理该线程以及在视图中调用哪些方法来实现此目的。

我试图解决这个问题时有几次尝试,但我不知道我是否在正确的轨道上,从我的代码中可能很清楚,我不知道我在做什么。 (此刻我的线程停止,表面刚刚停留显示最后一帧)。欢迎任何建议!

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

1 个答案:

答案 0 :(得分:1)

如果你想“重新启动”线程可以这么说,你有两个选择。 每次游戏都要重启或者构建初始线程无限循环并等待通知才能运行。像这样:

public void run() {

    while(true) {
        signal.wait();

        // your previous run code here
    }
}

在这种情况下,您最初启动线程,然后发信号通知实际以signal.notify()开头(信号必须是两个线程都可见的对象)。然后,当游戏结束时,线程将返回到wait的调用。然后再次通知它,以便从头重新开始。