Android小程序-模拟小球平抛落地反弹到静止过程

一、开发运动体Movable类
   我们将小球视为一个可移动物体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;
    ArrayList<Movable> alMovable = new ArrayList<Movable>();
    //后台屏幕绘制线程
    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);
    }
}

   至此整个程序代码开发完毕。该代码可直接运行。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值