【问题标题】:How should i handle resetting SurfaceView and Thread when my game should restart?当我的游戏应该重新启动时,我应该如何处理重置 SurfaceView 和 Thread?
【发布时间】:2011-12-15 13:22:33
【问题描述】:

我是安卓和游戏开发的新手,我一直在尝试创建一个 pong 克隆来掌握一切。我有一个扩展 SurfaceView 的“PongView”类和一个扩展线程的“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
        }
    }
}

【问题讨论】:

  • 你不能简单地启动一个新线程来再次执行相同的代码吗?
  • 这就是我在更新方法结束时尝试做的事情“ pongThread = new PongThread(getHolder(), this); //如果用户退出并返回 pongThread.setRunning(true);/ /.start() == PongThread.run() 除了 PongThread 完成所有工作 pongThread.start(); ".
  • 它不起作用?另见我的回答。
  • @Tudor,是的,它有点冻结,在我永久调用 pongThread.setRunning(false) 之前显示游戏的最后一帧。感谢您花时间为我考虑这个问题,我很困惑

标签: java android multithreading surfaceview


【解决方案1】:

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

public void run() {

    while(true) {
        signal.wait();

        // your previous run code here
    }
}

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

【讨论】:

  • 感谢您的信息。如果我认为上面的代码在我的线程 run() 方法中是正确的,我仍然有点不确定我将在哪里使用 signal.notify .. 如果我只能在线程中使用信号,那么我仍然有一个线程和实际检测到游戏结束的视图之间的通信问题。我将对此进行思考和实验,尽管我想我更愿意从我的表面视图中找出一个新线程。感谢您的帮助,任何进一步的建议也将不胜感激:)
  • 您可以从以前必须启动新线程的视图中调用 notify。只需将其替换为对 signal.notify() 的调用即可让线程再次移动。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-28
  • 2016-08-31
  • 2016-03-21
  • 1970-01-01
  • 2020-03-25
  • 2020-01-15
  • 1970-01-01
相关资源
最近更新 更多