为了复习一下SurfaceView的使用,在此写了一个经典的小球碰撞检测例子程序,希望能够够帮助正在学习游戏的人。

先看一下效果图:

   下面我们就来逐一分析一下它的实现过程:

1.启动入口:

import android.os.Bundle;import android.app.Activity;import android.view.Window;import android.view.WindowManager;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //全屏设置        requestWindowFeature(Window.FEATURE_NO_TITLE);        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                WindowManager.LayoutParams.FLAG_FULLSCREEN);        //将画布放进去        GameView gameView = new GameView(this);        setContentView(gameView);    }}


2.小球类

import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;/** * 小球实例 * @author ZHF * */public class Ball {    int x, y;  //小球的实时位置    int startX, startY;   //小球的初始位置    float vX, vY;    //小球的速度    int r; //小球的半径                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         double startTimeX;  //开始时间    double startTimeY;  //开始时间                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         BallThread ballThread;  //小球移动线程    Paint paint = new Paint();  //画笔                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         public Ball(int x, int y, float vX, float vY, int r) {        this.x = x;        this.y = y;        this.startX = x;        this.startY = y;        this.vX = vX;        this.vY = vY;        this.r = r;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 //为每个小球实例化一个独立的线程,在抬手时开启线程        ballThread = new BallThread(this);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 paint.setColor(Color.RED);  //小球为红色实心    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         /**绘画方法**/    public void drawSelf(Canvas canvas) {        canvas.drawCircle(x, y, r, paint);    }}


3.障碍物类:

import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;/** * 障碍物 * * @author ZHF * */public class Obstruction {    int x, y;    int hWeight;  //宽度和高度一样    Paint paint = new Paint();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      public Obstruction(int x, int y, int hWeight) {        this.x = x;        this.y = y;        this.hWeight = hWeight;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              paint.setColor(Color.GREEN);  //设置画笔颜色                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          }    public void drawSelf(Canvas canvas) {        canvas.drawRect(x - hWeight, y - hWeight, x + hWeight, y + hWeight, paint);    }}

以上代码比较简单,在此不多做解释,下面主要来看一下两个主要线程类:


4.小球移动线程(碰撞检测):

/** * 小球移动和碰撞检测线程 * @author ZHF * */public class BallThread extends Thread {    boolean flag;   //标记线程是否开启    Ball ball;  //小球    double currentTime;  //当前时间                                                                                                                                                                                                                                                                                                                                                                                                                                                                         public BallThread(Ball ball) {        flag = true;        this.ball = ball;    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                         @Override    public void run() {        while(flag) {            //调试:碰撞检测开始时间            long startTime = System.currentTimeMillis();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          //计算出小球移动的时间片:将每次刷新分成若干时间小片段,用于计算每次时间小片段小球移动的距离            currentTime = System.nanoTime();            double timeSpanX = (currentTime - ball.startTimeX) /1000 /1000 /1000;            double timeSpanY = (currentTime - ball.startTimeY) /1000 /1000 /1000;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         int xBackup = ball.x; //保存小球的碰撞前的位置            int yBackup = ball.y;            ball.x = (int) (ball.startX + ball.vX * timeSpanX);//小球移动的距离            ball.y = (int) (ball.startY + ball.vY * timeSpanY);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         //边界碰撞检测            if((ball.vX > 0 && (ball.x + ball.r) >= 479) || (ball.vX < 0 && (ball.x - ball.r) <= 0)) {                ball.x = xBackup;                ball.vX = 0 - ball.vX;  //速度反向                ball.startTimeX = System.nanoTime();  //重新记录开始时间                ball.startX = ball.x;  //重新记录开始位置            }            if((ball.vY > 0 && (ball.y + ball.r) >= 799) || (ball.vY < 0 && (ball.y - ball.r) <= 0)) {                ball.y = yBackup;                ball.vY = 0 - ball.vY;   //速度反向                ball.startTimeY = System.nanoTime();   //重新记录开始时间                ball.startY = ball.y;   //重新记录开始位置            }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         //障碍物碰撞检测            for(int i = 0; i < GameView.obstructList.size(); i++) {                Obstruction o = GameView.obstructList.get(i);                if(Math.abs(ball.x - o.x) < (ball.r + o.hWeight) && Math.abs(ball.y - o.y) < (ball.r + o.hWeight)){                    if(Math.abs(xBackup - o.x) >= (ball.r + o.hWeight)) {                        ball.x = xBackup;                        ball.vX =  0 - ball.vX;                        ball.startTimeX = System.nanoTime();                        ball.startX = ball.x;                    }                    if(Math.abs(yBackup - o.y) >= (ball.r + o.hWeight)) {                        ball.y = yBackup;                        ball.vY = 0 - ball.vY;                        ball.startTimeY = System.nanoTime();                        ball.startY = ball.y;                    }                    break; //跳出循环                }            }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         //调试:碰撞检测结束时间     实验证明碰撞加测基本不耗时间            long endTime = System.currentTimeMillis();            System.out.println(endTime + "----" + startTime + "= " +(endTime - startTime));                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         try {                Thread.sleep(10);            } catch (Exception e) {                e.printStackTrace();            }        }    }}

分析:

   1.我们将刷新时间分割成:将每次刷新时间分成若干时间小片段timeSpanX和timeSpanY,用于计算每次时间小片段小球移动的距离.

   2.我们在小球与边界碰撞之前,记录一下时间startTime,在其与边界碰撞之后,我们将其x轴、y轴方向上做一系列的操作(方向取反,回到碰撞前位置,重新记录开始时间)其实,我通过调试发现碰撞时间基本可以忽略.

   3.我们这里的碰撞检测是边界检测,只考虑小球与障碍物、边界的碰撞,没有考虑小球之间的碰撞,有兴趣的同学可以自行研究一下。


5.绘画线程:

2

import android.graphics.Canvas;import android.util.Log;import android.view.SurfaceHolder;/** * 绘画主界面线程 * @author ZHF * */public class DrawThread extends Thread {                                                                                                                                                                                                                                                                                                 boolean flag;  //标记线程是否开启    GameView gameView;    SurfaceHolder holder;    Canvas canvas;                                                                                                                                                                                                                                                                                                 public DrawThread(GameView gameView) {        flag = true;        this.gameView = gameView;        holder = gameView.getHolder();  //获取画布锁    }    @Override    public void run() {        while(flag) {            //获取当前绘画开始时间            long startTime = System.currentTimeMillis();            synchronized(holder) {                canvas = holder.lockCanvas(); //获取当前被锁住的画布                if(canvas != null) {                    gameView.draw(canvas); //对画布进行操作                    holder.unlockCanvasAndPost(canvas); //释放画布                }            }            long endTime = System.currentTimeMillis();            int diffTime = (int) (endTime - startTime);            Log.d("DrawTime", diffTime+"");                                                                                                                                                                                                                                                                                                                 while(diffTime <= 2) {                diffTime = (int) (System.currentTimeMillis() - startTime);                Thread.yield(); //将线程的所有权交给另一个线程            }        }    }}

   分析:

     1. 首先,我们将画布锁住之后,对其进行绘画,画完之后自然要释放画布啦

2. 为了优化程序,我们计算出绘画所用时间,当绘画时间过长时,暂停当前正在执行的线程对象,通知CPU来执行其他线程(注意:这里的其他也包含当前线程)

6.主界面:

import java.util.ArrayList;import java.util.Random;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * 游戏主界面 * @author ZHF * */public class GameView extends SurfaceView implements SurfaceHolder.Callback {    SurfaceHolder holder;    DrawThread drawThread;  //绘画线程                                                                                                                                                                                                   Ball[] ballArray = new Ball[5];  //装小球的数组    int ballPointer = 0;  //当前指向数组中第几个球                                                                                                                                                                                                   static ArrayList obstructList = new ArrayList();   //装障碍物的集合                                                                                                                                                                                                   int xDown, yDown;  //记录手指按下时的坐标                                                                                                                                                                                                   public GameView(Context context) {        super(context);        holder = getHolder();  //获取画布锁        holder.addCallback(this);  //添加回调                                                                                                                                                                                                           //初始化障碍物        Random random = new Random();        for(int i = 0; i < 3; i++) {            Obstruction o = new Obstruction(random.nextInt(380) + 50, random.nextInt(700) + 50, 50);            obstructList.add(o); //将创出的障碍物对象添加到集合中去        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        drawThread = new DrawThread(this);        drawThread.start();    //开启绘画线程    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width,            int height) {        //画布发生变化,eg:转屏操作,处理画布操作    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        //销毁画布操作        drawThread.flag = false;  //停掉线程        drawThread = null; //GC会及时发现并处理掉该对象    }    public void draw(Canvas canvas) {        canvas.drawColor(Color.BLACK);  //背景颜色        Paint paint = new Paint();        paint.setTextSize(25);        paint.setColor(Color.WHITE);  //文字颜色        canvas.drawText("小球碰撞检测", 50, 20, paint);                                                                                                                                                                                                           //画出小球        for(int i = 0; i < 5; i++) {            if(ballArray[i] != null) {                ballArray[i].drawSelf(canvas);  //当前小球绘画出自己            }        }        //画出障碍物        for(int i = 0; i < obstructList.size(); i++) {            obstructList.get(i).drawSelf(canvas);        }    }                                                                                                                                                                                                   @Override    public boolean onTouchEvent(MotionEvent event) {        int x = (int) event.getX();        int y = (int) event.getY();                                                                                                                                                                                                           if(event.getAction() == 0) {  //按下            //记录按下时X,Y的坐标            xDown = x;            yDown = y;            //生成第一个球            Ball ball = new Ball(x, y, 0, 0, 20);            if(ballArray[ballPointer] != null) {                ballArray[ballPointer].ballThread.flag = false;  //关闭小球移动线程                ballArray[ballPointer].ballThread = null;            }            ballArray[ballPointer] = ball;                                                                                                                                                                                                               } else if(event.getAction() == 1) { //抬起            int xOffset = x - xDown;            int yOffset = y - yDown;            double sin = yOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);            double cos = xOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);                                                                                                                                                                                                                   ballArray[ballPointer].startTimeX = System.nanoTime(); //当前小球开始时间            ballArray[ballPointer].startTimeY = System.nanoTime();            ballArray[ballPointer].vX = (float) (500 * cos);  //当前小球的速度            ballArray[ballPointer].vY = (float) (500 * sin);            ballArray[ballPointer].ballThread.start();   //开启小球移动线程                                                                                                                                                                                                                   ballPointer ++;  //下一个小球            if(ballPointer >= 5) {                ballPointer = 0;            }        }        return true;    }}

分析:

   1.这里我们启动小球移动线程方式:采用手指触屏滑动,记录按下、抬起位置,通过计算角度得出算出发射方向。

   2.每次发出小球后下标ballPointer ++指向下一个小球,当到达数组上限后,重新返回到下标0.



   ok! 到此功能已经实现,想要完整源码在此下载:http://download.csdn.net/detail/zhf651555765/5775035


更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. Android后台任务(HandlerThread、AsyncTask、IntentService)
  3. Android(安卓)常用知识点整理(含链接)
  4. IOS多线程开发之GCD
  5. Android实现网络访问
  6. Android(安卓)性能优化、内存优化
  7. Android性能分析和优化之traces.txt(ANR分析)
  8. android之Service(2)IntentService
  9. Android(安卓)线程以及提示消息框

随机推荐

  1. eclipse android插件
  2. android 中文api (62) ―― ViewSwitcher
  3. Android工程的gradle版本和gradle plugin
  4. android google map的使用
  5. android apk dex odex jar 等文件的 反编
  6. 细数Android(安卓)Bug
  7. Android(安卓)OpenGLES 实现结构
  8. android自学笔记《二》——开发环境的搭
  9. 通过Titanium Studio为Android(安卓)APK
  10. Android(安卓)StatusBar相关设置