游戏编程需要一些经常使用的数学和物理知识,下面演示一下:
1、开发运动体Movable类代码
- package wyf.wpf; //声明包语句
- import android.graphics.Bitmap; //引入相关类
- import android.graphics.Canvas; //引入相关类
- //代表可移动物体的Movable类
- 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; //初始化X坐标
- this.x = x; //初始化X坐标
- this.startY = y; //初始化Y坐标
- this.y = 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();
- }
- //方法:绘制自己到屏幕上
- public void drawSelf(Canvas canvas){
- canvas.drawBitmap(this.bitmap,x, y, null);
- }
- }
2、开发物理引擎BallThread类的代码
这次物理引擎的工作机制:
- 坐标系统
- 运动阶段
- 数值计算
- 为零判断
- package wyf.wpf; //声明包语句
- //继承自Thread的线程类,负责修改球的位置坐标
- public class BallThread extends Thread{
- Movable father; //Movable对象引用
- boolean flag = false; //线程执行标志位
- int sleepSpan = 30; //休眠时间
- float g = 200; //球下落的加速度
- double current; //记录当前时间
- //构造器:初始化Movable对象引用及线程执行标志位
- public BallThread(Movable father){
- this.father = father;
- this.flag = true; //设置线程执行的标志位为true
- }
- //方法:负责根据物理公式修改小球位置,并检测和处理小球达到最高点以及撞击地面的事件
- 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.startVY * timeSpanY + timeSpanY*timeSpanY*g/2);
- father.v_y = (float)(father.startVY + g*timeSpanY);
- //判断小球是否到达最高点
- if(father.startVY < 0 && Math.abs(father.v_y) <= BallView.UP_ZERO){
- father.timeY = System.nanoTime(); //设置新的运动阶段竖直方向上的开始时间
- father.v_y = 0; //设置新的运动阶段竖直方向上的实时速度
- father.startVY = 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.startVY = father.v_y; //设置新的运动阶段竖直方向上的初速度
- }
- }
- }
- else if(father.x + father.r/2 >= BallView.WOOD_EDGE){//判断球是否移出了挡板
- father.timeY = System.nanoTime(); //记录球竖直方向上的开始运动时间
- father.bFall = true; //设置表示是否开始下落标志位
- }
- try{
- Thread.sleep(sleepSpan); //休眠一段时间
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- }
3、视图类--开发BallView类的代码
- package wyf.wpf; //声明包语句
- import java.util.ArrayList; //引入相关类
- 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; //引入相关类
- //继承自SurfaceView的子类 ,需要实现SurfaceHolder.callback接口
- public class BallView extends SurfaceView implements SurfaceHolder.Callback{
- public static final int V_MAX=35; //小球水平速度的最大值
- public static final int V_MIN=15; //小球竖直速度的最大值
- public static final int WOOD_EDGE = 60; //木板的右边沿的x坐标
- public static final int GROUND_LING = 450;//游戏中代表地面y坐标,小球下落到此会弹起
- public static final int UP_ZERO = 30; //小球在上升过程中,如果速度大小小于该值就算为0
- public static final int DOWN_ZERO = 60; //小球在撞击地面后,如果速度大小小于该值就算为0
- Bitmap [] bitmapArray = new Bitmap[6]; //各种颜色形状的小球图片引用
- Bitmap bmpBack; //背景图片对象
- Bitmap bmpWood; //木板图片对象
- String fps="FPS:N/A"; //用于显示帧速率的字符串,调试使用
- int ballNumber =8; //小球数目
- ArrayList<Movable> alMovable = new ArrayList<Movable>(); //小球对象数组
- DrawThread dt; //后台屏幕绘制线程
- public BallView(Context activity){
- super(activity); //调用父类构造器
- getHolder().addCallback(this);
- initBitmaps(getResources()); //初始化图片
- initMovables(); //初始化小球
- dt = new DrawThread(this,getHolder()); //初始化重绘线程
- }
- //方法:初始化图片
- public 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); //木板
- }
- //方法:初始化小球
- public void initMovables(){
- Random r = new Random(); //创建一个Random对象
- for(int i=0;i<ballNumber;i++){
- int index = r.nextInt(32); //产生随机数
- Bitmap tempBitmap=null; //声明一个Bitmap图片引用
- 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对象
- alMovable.add(m); //将新建的Movable对象添加到ArrayList列表中
- }
- }
- //方法:绘制程序中所需要的图片等信息
- public void doDraw(Canvas canvas) {
- canvas.drawBitmap(bmpBack, 0, 0, null); //绘制背景图片
- canvas.drawBitmap(bmpWood, 0, 60, null);//绘制木板图片
- for(Movable m:alMovable){ //遍历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 holder, int format, int width,
- int height) {//重写surfaceChanged方法
- }
- @Override
- public void surfaceCreated(SurfaceHolder holder) {//从写surfaceCreated方法
- if(!dt.isAlive()){ //如果DrawThread没有启动,就启动这个线程
- dt.start();
- }
- }
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {//重写surfaceDestroyed方法
- dt.flag = false; //停止线程的执行
- dt = null; //将dt指向的对象声明为垃圾
- }
- }
4、绘制线程--DrawThread类
主要负责定时重绘屏幕和计算帧速率
- package wyf.wpf; //声明包语句
- 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; //为BallView对象应用赋值
- this.surfaceHolder = 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){ //如果canvas不为空
- 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(); //捕获并打印异常
- }
- }
- }
- }
5、Activity代码
- package wyf.wpf; //声明包语句
- import android.app.Activity; //引入相关类
- import android.os.Bundle; //引入相关类
- import android.view.Window;//引入相关类
- import android.view.WindowManager;//引入相关类
- //继承自Activity的引用
- public class Sample_7_1 extends Activity {
- BallView bv; //BallView对象引用
- @Override
- public 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); //创建BallView对象
- setContentView(bv); //将屏幕设置为BallView对象
- }
- }