遊戲編程需要一些經常使用的數學和物理知識,下面演示一下:
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對象
- }
- }