我们将小球视为一个可移动物体Movable对象,该类中除了包含小球图片对象之外,还包括如位置坐标、水平竖直速度等一系列用于模拟小球运动的成员变量和一些方法。具体代码如下:
package xiao.fuyan.ball; import android.graphics.Bitmap; import android.graphics.Canvas; /** * Created by xiao on 2017/2/9. */ public class Movable { //初始X,Y坐标 int startX = 0, startY = 0; //实时X,Y坐标 int x = 0, y = 0; //初始水平、竖直方向的速度 float startV_X = 0f; float startV_Y = 0f; //实时水平、竖直方向的速度 float v_x = 0f; float v_y = 0f; //可移动物体半径 int r; //X,Y方向上运动的时间 double timeX, timeY; //可移动物体的图片 Bitmap bitmap = null; //负责小球运动的线程 BallThread bt = null; //小球是否从木板上落下 boolean bFall = false; //小球撞地后速度的损失系数 float impactFactor = 0.25f; //构造器,负责初始化主要的成员变量 public Movable(int x, int y, int r, Bitmap bitmap){ this.startX = x; this.x = x; this.startY = y; this.y = y; this.r = r; this.bitmap = bitmap; //获取系统时间来初始化timeX this.timeX = System.nanoTime(); this.v_x = BallView.V_MIN + (int)((BallView.V_MAX - BallView.V_MIN)*Math.random()); this.bt = new BallThread(this); this.bt.start(); } //将自己绘制到屏幕上 public void drawSelf(Canvas canvas){ canvas.drawBitmap(this.bitmap, x, y, null); } }
二、开发物理引擎BallThread类
该类继承自Thread类,在程序中,每一个Movable对象都会有一个独立的BallThread对象,其主要作用是改变小球的运动轨迹。具体代码如下:
package xiao.fuyan.ball; /** * Created by xiao on 2017/2/9. */ public class BallThread extends Thread { //Movable对象的引用 Movable father; //线程执行标志位 boolean flag = false; //休眠时间 int sleepSpan = 40; //小球下落的加速度 float g = 200; //记录当前的时间 double current; public BallThread(Movable father){ this.father = father; this.flag = true; } @Override public void run() { while(flag){ //获取当前时间,单位为纳秒 current = System.nanoTime(); //处理水平方向上的运动,首先获取水平运动的时间 double timeSpanX = (double) ((current - father.timeX)/1000/1000/1000); //获取水平方向移动的距离 father.x = (int) (father.startX + father.v_x * timeSpanX); //处理竖直方向上的运动 //判断小球是否移出挡板 if(father.bFall){ double timeSpanY = (double) ((current - father.timeY)/1000/1000/1000); father.y = (int) (father.startY + father.startV_Y * timeSpanY + timeSpanY * timeSpanY * g/2); //计算竖直方向速度 father.v_y = (float) (father.startV_Y + g * timeSpanY); //判断小球是否到达最高点 if(father.startV_Y < 0 && Math.abs(father.v_y) <= BallView.UP_ZERO){ //设置新的运动阶段竖直方向上的开始时间 father.timeY = System.nanoTime(); //设置新的运动阶段竖直方向上的实时速度 father.v_y = 0; //设置新的运动阶段竖直方向上的初始速度 father.startV_Y = 0; //设置新的运动阶段竖直方向上的初始位置 father.startY = father.y; } //判断小球是否撞地 if(father.y + father.r * 2 >= BallView.GROUND_LING && father.v_y > 0){ //衰减水平和竖直方向的速度 father.v_x = father.v_x * (1 - father.impactFactor); father.v_y = 0 - father.v_y * (1 - father.impactFactor); if(Math.abs(father.v_y) < BallView.DOWN_ZERO){ //撞地衰减后速度太小就直接停止 this.flag = false; }else{ father.startX = father.x; father.timeX = System.nanoTime(); father.startY = father.y; father.timeY = System.nanoTime(); father.startV_Y = father.v_y; } } }else if(father.x + father.r/2 >= BallView.WOOD_EDGE){ father.timeY = System.nanoTime(); father.bFall = true; } try{ sleep(sleepSpan); }catch (Exception e){ e.printStackTrace(); } } } }
三、开发视图BallView类
这次我们介绍的是负责画面渲染的视图类BallView,其中声明了一些物理计算时需要使用静态常量,同时还声明了程序中要绘制的图片资源以及要绘制的小球对象列表。具体代码如下:
package xiao.fuyan.ball; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.util.ArrayList; import java.util.Random; /** * Created by xiao on 2017/2/9. */ public class BallView extends SurfaceView implements SurfaceHolder.Callback { //小球水平速度的最值 public static final int V_MAX = 65; public static final int V_MIN = 45; //木板右边沿的X坐标 public static final int WOOD_EDGE = 110; //游戏中代表地面的坐标,小球到此后会弹起 public static final int GROUND_LING = 1920; //小球上升时,如果速度小于该值就算0 public static final int UP_ZERO = 30; //小球撞地时,如果速度小于该值就算0 public static final int DOWN_ZERO = 60; //各种颜色形状的小球图片的引用 Bitmap[] bitmaps = new Bitmap[6]; //背景图片对象 Bitmap bmpBack; //木板图片对象 Bitmap bmpWood; //帧速率字符串 String fps = "FPS:N/A"; //小球数目 int ballNumber = 6; ArrayListalMovable = new ArrayList (); //后台屏幕绘制线程 DrawThread dt; //构造器 public BallView(Context context){ super(context); getHolder().addCallback(this); //初始化图片 initBitmaps(getResources()); //初始化小球 initMovables(); //初始化重绘线程 dt = new DrawThread(this, getHolder()); } //初始化图片 public void initBitmaps(Resources r){ bitmaps[0] = BitmapFactory.decodeResource(r, R.mipmap.red_small); bitmaps[1] = BitmapFactory.decodeResource(r, R.mipmap.red); bitmaps[2] = BitmapFactory.decodeResource(r, R.mipmap.yellow_small); bitmaps[3] = BitmapFactory.decodeResource(r, R.mipmap.yello); bitmaps[4] = BitmapFactory.decodeResource(r, R.mipmap.blue_small); bitmaps[5] = BitmapFactory.decodeResource(r, R.mipmap.blue); bmpBack = BitmapFactory.decodeResource(r, R.mipmap.wall); bmpWood = BitmapFactory.decodeResource(r, R.mipmap.wood); } public void initMovables(){ Random r = new Random(); for(int i = 0; i < ballNumber; i++){ int index = r.nextInt(32); Bitmap tmp = null; if(i < ballNumber/2){ tmp = bitmaps[3 + index % 3]; }else{ tmp = bitmaps[index % 3]; } //创建Movable对象 Movable m = new Movable(0, 110 - tmp.getHeight(), tmp.getWidth()/2, tmp); alMovable.add(m); } } public void doDraw(Canvas canvas){ canvas.drawBitmap(bmpBack, 0, 0, null); canvas.drawBitmap(bmpWood, 0, 100, null); for(Movable m : alMovable){ m.drawSelf(canvas); } Paint p = new Paint(); p.setColor(Color.BLUE); p.setTextSize(60); p.setAntiAlias(true); canvas.drawText(fps, 800, 80, p); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { if(!dt.isAlive()){ dt.start(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { dt.flag = false; dt = null; } }
四、开发绘制线程DrawThread类
该类的弄能比较简单,主要是负责定时重绘屏幕和计算帧速率。具体代码如下:
package xiao.fuyan.ball; import android.graphics.Canvas; import android.view.SurfaceHolder; /** * Created by xiao on 2017/2/9. */ public class DrawThread extends Thread { //BallView对象的引用 BallView bv; //SurfaceHolder对象的引用 SurfaceHolder surfaceHolder; //线程执行标志位 boolean flag = false; //休眠时间 int sleepSpan; //记录开始时刻,用于计算帧速率 long start = System.nanoTime(); //记录帧数 int count = 0; public DrawThread(BallView bv, SurfaceHolder surfaceHolder){ this.bv = bv; this.surfaceHolder = surfaceHolder; this.flag = true; } @Override public void run() { Canvas canvas = null; while(flag){ try { //获取BallView的画布 canvas = surfaceHolder.lockCanvas(null); synchronized (surfaceHolder){ bv.doDraw(canvas); } }catch (Exception e){ e.printStackTrace(); }finally { if(canvas != null){ surfaceHolder.unlockCanvasAndPost(canvas); } } count++; if(count == 20){ count = 0; long tempStamp = System.nanoTime(); long span = tempStamp - start; start = tempStamp; double fps = Math.round(100000000000.0/span*20)/100.0; bv.fps = "FPS:" + fps; } try{ sleep(sleepSpan); }catch (Exception e){ e.printStackTrace(); } } } }
五、开发主Activity类
该类用于加载界面组件。具体代码如下:
package xiao.fuyan.ball; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class MainActivity extends Activity { BallView bv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置不显示标题 requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); bv = new BallView(this); setContentView(bv); } }
至此整个程序代码开发完毕。该代码可直接运行。