接下来将慢慢涉入游戏的开发,作为第一篇介绍一个模拟平抛运动的小DEMO。主要核心是两个线程类:BallThread负责改变小球的状态和坐标位置,会用到一些常用的物理公式,相信大家并不陌生。DrawThread负责绘制界面图片,包括背景,木板,小球。这两个线程相结合,就实现了模拟小球的平抛运动并将其绘制在界面上。BallThread就相当于一个物理引擎,对小球的运动进行分阶段处理,包括开始运动到滑至木板边缘、小球下落、小球反弹、小球达到最高点再下落、循环直至运动停止。每个阶段的开始都对小球位置和状态进行初始操作。保证小球坐标的准确性。
MainActivity:
package com.home.simulationthrow; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; import android.app.Activity; 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); } }
BallView:
package com.home.simulationthrow; import java.util.ArrayList; import java.util.List; import java.util.Random; 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; public class BallView extends SurfaceView implements SurfaceHolder.Callback { public static final int V_MAX = 45; // 小球水平速度的最大值 public static final int V_MIN = 20; // 小球竖直速度的最大值 private Bitmap[] bitmapArray = new Bitmap[6];// 6个小球图片 private Bitmap bmpBack; // 背景图片对象 private Bitmap bmpWood; // 木板图片对象 private int ballNumber = 6; // 小球数目 public static final int WOOD_EDGE = 60; // 木板的右边沿的x坐标 public static final int GROUND_LING = 600;// 游戏中代表地面y坐标,小球下落到此会弹起 public static final int UP_ZERO = 30; // 小球在上升过程中,如果速度大小小于该值就算为0 public static final int DOWN_ZERO = 60; // 小球在撞击地面后,如果速度大小小于该值就算为0 List<Movable> movables = new ArrayList<Movable>(); private DrawThread dt; // 后台屏幕绘制线程 String fps = "FPS:N/A"; // 用于显示帧速率的字符串,调试使用 public BallView(Context context) { super(context); getHolder().addCallback(this); initBitmaps(getResources()); initMovables(); dt = new DrawThread(this, getHolder()); } /** * 初始化图片 * * @param resources */ private void initBitmaps(Resources r) { bitmapArray[0] = BitmapFactory.decodeResource(r, R.drawable.ball_red_small); // 红色较小球 bitmapArray[1] = BitmapFactory.decodeResource(r, R.drawable.ball_purple_small); // 紫色较小球 bitmapArray[2] = BitmapFactory.decodeResource(r, R.drawable.ball_green_small); // 绿色较小球 bitmapArray[3] = BitmapFactory.decodeResource(r, R.drawable.ball_red); // 红色较大球 bitmapArray[4] = BitmapFactory .decodeResource(r, R.drawable.ball_purple); // 紫色较大球 bitmapArray[5] = BitmapFactory.decodeResource(r, R.drawable.ball_green); // 绿色较大球 bmpBack = BitmapFactory.decodeResource(r, R.drawable.back); // 背景砖墙 bmpWood = BitmapFactory.decodeResource(r, R.drawable.wood); } /** * 初始化小球集合 */ private void initMovables() { Random r = new Random(); for (int i = 0; i < ballNumber; i++) { int index = r.nextInt(32); // 产生随机数 Bitmap tempBitmap = null; if (i < ballNumber / 2) { tempBitmap = bitmapArray[3 + index % 3];// 如果是初始化前一半球,就从大球中随机找一个 } else { tempBitmap = bitmapArray[index % 3];// 如果是初始化后一半球,就从小球中随机找一个 } Movable m = new Movable(0, 70 - tempBitmap.getHeight(), tempBitmap.getWidth() / 2, tempBitmap); // 创建Movable对象 movables.add(m); // 将新建的Movable对象添加到ArrayList列表中 } } /** * 绘制程序中所需要的图片等信息 * * @param canvas */ public void doDraw(Canvas canvas) { canvas.drawBitmap(bmpBack, 0, 0, null); // 绘制背景图片 canvas.drawBitmap(bmpWood, 0, 60, null);// 绘制木板图片 for (Movable m : movables) { // 遍历Movable列表,绘制每个Movable对象 m.drawSelf(canvas); } Paint p = new Paint(); p.setColor(Color.BLUE); p.setTextSize(18); p.setAntiAlias(true); // 设置抗锯齿 canvas.drawText(fps, 30, 30, p); // 画出帧速率字符串 } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } @Override public void surfaceCreated(SurfaceHolder arg0) { if (dt != null && !dt.isAlive()) { // 如果DrawThread没有启动,就启动这个线程 dt.start(); } } @Override public void surfaceDestroyed(SurfaceHolder arg0) { if (dt != null) { dt.flag = false;// 停止线程的执行 dt = null; // 将dt指向的对象声明为垃圾 } } }
Movable实体类:
package com.home.simulationthrow; import android.graphics.Bitmap; import android.graphics.Canvas; /** * 小球实体类 * * @author Administrator * */ public class Movable { int startX = 0; // 初始X坐标 int startY = 0; // 初始Y坐标 int x; // 实时X坐标 int y; // 实时Y坐标 float startVX = 0f; // 初始水平方向的速度 float startVY = 0f; // 初始竖直方向的速度 float v_x = 0f; // 实时水平方向速度 float v_y = 0f; // 实时竖直方向速度 int r; // 小球半径 double timeX; // X方向上的运动时间 double timeY; // Y方向上的运动时间 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 = System.nanoTime(); // 获取系统时间初始化 // 随机获取小球初始时水平方向的速度(介于最大值和最小值之间) this.v_x = BallView.V_MIN + (int) ((BallView.V_MAX - BallView.V_MIN) * Math.random()); bt = new BallThread(this);// 创建并启动BallThread bt.start(); } /** * 把自己绘制在屏幕上 * * @param canvas */ public void drawSelf(Canvas canvas) { canvas.drawBitmap(bitmap, x, y, null); } }
BallThread:
package com.home.simulationthrow; /** * 负责修改小球位置坐标的线程 * @author Administrator * */ public class BallThread extends Thread{ private Movable movable; //Movable对象 private boolean flag = false; //线程执行标志位 private int sleepSpan = 30; //休眠时间 private float g = 200; //球下落的加速度 private double current; //记录当前时间 //构造器:初始化Movable对象引用及线程执行标志位 public BallThread(Movable movable){ this.movable = movable; this.flag = true; //设置线程执行的标志位为true } //方法:负责根据物理公式修改小球位置 public void run(){ while(flag){ current = System.nanoTime();//获取当前时间,单位为纳秒 double timeSpanX = (double)((current-movable.timeX)/1000/1000/1000);//获取从玩家开始到现在水平方向走过的时间 //处理水平方向上的运动(获得实时x坐标) movable.x = (int)(movable.startX + movable.v_x * timeSpanX); //处理竖直方向上的运动 if(movable.bFall){//判断球是否已经移出挡板 double timeSpanY = (double)((current - movable.timeY)/1000/1000/1000);//Y方向上走过的时间 //获得实时y坐标 movable.y = (int)(movable.startY + movable.startVY * timeSpanY + timeSpanY*timeSpanY*g/2); //获得实时Y方向上的速度 movable.v_y = (float)(movable.startVY + g*timeSpanY); //判断小球是否到达最高点(速度方向向上,并且小于了某个阀值,则认为达到了最高点) if(movable.startVY < 0 && Math.abs(movable.v_y) <= BallView.UP_ZERO){ movable.timeY = System.nanoTime(); //设置新的运动阶段竖直方向上的开始时间 movable.v_y = 0; //设置新的运动阶段竖直方向上的实时速度 movable.startVY = 0; //设置新的运动阶段竖直方向上的初始速度 movable.startY = movable.y; //设置新的运动阶段竖直方向上的初始位置 } //判断小球是否撞地 if(movable.y + movable.r*2 >= BallView.GROUND_LING && movable.v_y >0){//判断撞地条件 //改变水平方向的速度 movable.v_x = movable.v_x * (1-movable.impactFactor); //衰减水平方向上的速度 //改变竖直方向的速度 movable.v_y = 0 - movable.v_y * (1-movable.impactFactor); //衰减竖直方向上的速度并改变方向 if(Math.abs(movable.v_y) < BallView.DOWN_ZERO){ //判断撞地后的速度,太小就停止 this.flag = false; }else{ //撞地后的速度还可以弹起继续下一阶段的运动 //撞地之后水平方向的变化 movable.startX = movable.x; //设置新的运动阶段的水平方向的起始位置 movable.timeX = System.nanoTime(); //设置新的运动阶段的水平方向的开始时间 //撞地之后竖直方向的变化 movable.startY = movable.y; //设置新的运动阶段竖直方向上的起始位置 movable.timeY = System.nanoTime(); //设置新的运动阶段竖直方向开始运动的时间 movable.startVY = movable.v_y; //设置新的运动阶段竖直方向上的初速度 } } } else if(movable.x + movable.r/2 >= BallView.WOOD_EDGE){//判断球是否移出了挡板 movable.timeY = System.nanoTime(); //记录球竖直方向上的开始运动时间 movable.bFall = true; //设置表示是否开始下落标志位 } try{ Thread.sleep(sleepSpan); //休眠一段时间 } catch(Exception e){ e.printStackTrace(); } } } }
DrawThread:
package com.home.simulationthrow; import android.graphics.Canvas; import android.view.SurfaceHolder; public class DrawThread extends Thread { BallView bv; // BallView对象引用 SurfaceHolder surfaceHolder;// SurfaceHolder对象引用 boolean flag = false; // 线程执行标志位 int sleepSpan = 30; // 休眠时间 long start = System.nanoTime(); // 记录起始时间,该变量用于计算帧速率 int count = 0; // 记录帧数,该变量用于计算帧速率 // 构造器 public DrawThread(BallView bv, SurfaceHolder surfaceHolder) { this.bv = bv; this.surfaceHolder = surfaceHolder; this.flag = true; } // 方法:线程的执行方法,用于绘制屏幕和计算帧速率 public void run() { Canvas canvas = null;// 声明一个Canvas对象 while (flag) { try { canvas = surfaceHolder.lockCanvas(null);// 获取BallView的画布 synchronized (surfaceHolder) { bv.doDraw(canvas); // 调用BallView的doDraw方法进行绘制 } } catch (Exception e) { e.printStackTrace(); } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas);// surfaceHolder解锁并将画布对象传回 } } this.count++; if (count == 20) { // 如果计满20帧 count = 0; // 清空计数器 long tempStamp = System.nanoTime();// 获取当前时间 long span = tempStamp - start; // 获取时间间隔 start = tempStamp; // 为start重新赋值 double fps = Math.round(100000000000.0 / span * 20) / 100.0;// 计算帧速率 bv.fps = "FPS:" + fps;// 将计算出的帧速率设置到BallView的相应字符串对象中 } try { Thread.sleep(sleepSpan); } catch (Exception e) { e.printStackTrace(); } } } }
原文:http://blog.csdn.net/u010142437/article/details/20054519