Как мне справиться со сбросом SurfaceView и Thread, когда моя игра должна перезапуститься? - PullRequest
2 голосов
/ 15 декабря 2011

Я новичок в разработке как для Android, так и для игр, и пытался создать клон понга, чтобы справиться со всем.У меня есть класс «PongView», который расширяет SurfaceView и «PongThread», который расширяет поток.

Я нашел способ определить, прошел ли мяч или «бомба» мимо весла и достиг ли стены за ним.Я не уверен, что мой подход лучший (мнения приветствуются), но, похоже, он работает, так как у меня есть небольшое сообщение с тостами, показывающее игру, если это произойдет. Теперь я хочу настроить его так, чтобы, наряду с отображением игры, перезапускалось представление (или любой более подходящий термин), чтобы бомба снова находилась в центре экрана, и пользователь мог играть в другую.round.

Из-за неопытности я не совсем уверен, как мне обращаться с потоком и какие методы вызывать в представлении для достижения этой цели.

I 'У меня было несколько попыток понять это, но я понятия не имею, нахожусь ли я на правильном пути, из моего кода, вероятно, будет довольно ясно, что я не знаю, что делаю. (в данный момент моя нить останавливается и моя поверхность просто застревает с отображением последнего кадра). Любой совет будет приветствоваться!

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 Ответ

1 голос
/ 15 декабря 2011

Если вы хотите, так сказать, «перезагрузить» поток, у вас есть два варианта. Либо запускайте новый поток каждый раз, когда предполагается, что игра перезапускается, либо создайте исходный поток с циклом на неопределенный срок и ожидайте уведомления , когда он сможет работать. Вот так:

public void run() {

    while(true) {
        signal.wait();

        // your previous run code here
    }
}

В этом сценарии вы изначально запускаете поток, а затем сигнализируете, что он фактически начинается с signal.notify() (сигнал должен быть объектом, видимым для обоих потоков). Затем, когда игра закончится, поток вернется к вызову wait. Затем просто сообщите об этом снова, чтобы начать сначала.

...