java - 当我的游戏应该重新启动时,我应该如何处理重置 SurfaceView 和 Thread?

标签 java android multithreading surfaceview

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

我找到了一种方法来检测球或“炸弹”是否已经越过 Racket 并到达 Racket 后的墙壁。我不确定我的方法是否最好(欢迎提出意见),但它似乎有效,因为如果发生这种情况,我会收到一条显示游戏结束的 toast 消息。 现在我想设置它,以便在显示游戏结束的同时,重新启动 View (或任何更合适的术语),以便炸弹再次位于屏幕中央,用户可以玩另一个圆。

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

我尝试过几次尝试弄明白,但我不知道我是否在正确的轨道上,从我的代码中可能会很清楚我不知道我在做什么. (此刻我的线程停止了,我的表面只是卡在显示最后一帧)。欢迎任何建议!

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

最佳答案

可以说,如果您想“重启”线程,您有两种选择。要么每次都启动一个新线程游戏应该重新启动,要么构建您的初始线程以无限循环并等待通知它可以运行。像这样:

public void run() {

    while(true) {
        signal.wait();

        // your previous run code here
    }
}

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

关于java - 当我的游戏应该重新启动时,我应该如何处理重置 SurfaceView 和 Thread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8520782/

相关文章:

java - 如果配置加载失败如何停止部署

java - 在自定义 Alert Builder View 布局中未从 EditText 获取文本

Android:替换 WebView 中的 HTML 标签

Java 和同步问题

python - python中跨模块和线程的全局变量

multithreading - 使用依赖注入(inject)时如何使共享资源线程安全?

java - jpa 非托管实体

java - android 中的 speex 支持

java <identifier> 期望在初始化变量时

android - 在Android中将 Canvas 保存为图像