溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》
  • 首頁(yè) > 
  • 教程 > 
  • 開發(fā)技術(shù) > 
  • 【移動(dòng)開發(fā)】Android游戲開發(fā)SurfaceView應(yīng)用----手指發(fā)動(dòng)小球(小球碰撞檢測(cè)例子)

【移動(dòng)開發(fā)】Android游戲開發(fā)SurfaceView應(yīng)用----手指發(fā)動(dòng)小球(小球碰撞檢測(cè)例子)

發(fā)布時(shí)間:2020-07-05 07:24:19 來(lái)源:網(wǎng)絡(luò) 閱讀:3178 作者:zhf651555765 欄目:開發(fā)技術(shù)

     為了復(fù)習(xí)一下SurfaceView的使用,在此寫了一個(gè)經(jīng)典的小球碰撞檢測(cè)例子程序,希望能夠夠幫助正在學(xué)習(xí)游戲的人。

先看一下效果圖:

【移動(dòng)開發(fā)】Android游戲開發(fā)SurfaceView應(yīng)用----手指發(fā)動(dòng)小球(小球碰撞檢測(cè)例子)

   下面我們就來(lái)逐一分析一下它的實(shí)現(xiàn)過(guò)程:

1.啟動(dòng)入口:

import android.os.Bundle;
import android.app.Activity;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //全屏設(shè)置
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //將畫布放進(jìn)去
        GameView gameView = new GameView(this);
        setContentView(gameView);
    }
}


2.小球類

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
/**
 * 小球?qū)嵗? * @author ZHF
 *
 */
public class Ball {
    int x, y;  //小球的實(shí)時(shí)位置
    int startX, startY;   //小球的初始位置
    float vX, vY;    //小球的速度
    int r; //小球的半徑
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    double startTimeX;  //開始時(shí)間
    double startTimeY;  //開始時(shí)間
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    BallThread ballThread;  //小球移動(dòng)線程
    Paint paint = new Paint();  //畫筆
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    public Ball(int x, int y, float vX, float vY, int r) {
        this.x = x;
        this.y = y;
        this.startX = x;
        this.startY = y;
        this.vX = vX;
        this.vY = vY;
        this.r = r;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
        //為每個(gè)小球?qū)嵗粋€(gè)獨(dú)立的線程,在抬手時(shí)開啟線程
        ballThread = new BallThread(this);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
        paint.setColor(Color.RED);  //小球?yàn)榧t色實(shí)心
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    /**繪畫方法**/
    public void drawSelf(Canvas canvas) {
        canvas.drawCircle(x, y, r, paint);
    }
}


3.障礙物類:

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
/**
 * 障礙物
 *
 * @author ZHF
 *
 */
public class Obstruction {
    int x, y;
    int hWeight;  //寬度和高度一樣
    Paint paint = new Paint();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
    public Obstruction(int x, int y, int hWeight) {
        this.x = x;
        this.y = y;
        this.hWeight = hWeight;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
        paint.setColor(Color.GREEN);  //設(shè)置畫筆顏色
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    }
    public void drawSelf(Canvas canvas) {
        canvas.drawRect(x - hWeight, y - hWeight, x + hWeight, y + hWeight, paint);
    }
}

以上代碼比較簡(jiǎn)單,在此不多做解釋,下面主要來(lái)看一下兩個(gè)主要線程類:


4.小球移動(dòng)線程(碰撞檢測(cè)):

/**
 * 小球移動(dòng)和碰撞檢測(cè)線程
 * @author ZHF
 *
 */
public class BallThread extends Thread {
    boolean flag;   //標(biāo)記線程是否開啟
    Ball ball;  //小球
    double currentTime;  //當(dāng)前時(shí)間
                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    public BallThread(Ball ball) {
        flag = true;
        this.ball = ball;
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    @Override
    public void run() {
        while(flag) {
            //調(diào)試:碰撞檢測(cè)開始時(shí)間
            long startTime = System.currentTimeMillis();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
             //計(jì)算出小球移動(dòng)的時(shí)間片:將每次刷新分成若干時(shí)間小片段,用于計(jì)算每次時(shí)間小片段小球移動(dòng)的距離
            currentTime = System.nanoTime();
            double timeSpanX = (currentTime - ball.startTimeX) /1000 /1000 /1000;
            double timeSpanY = (currentTime - ball.startTimeY) /1000 /1000 /1000;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            int xBackup = ball.x; //保存小球的碰撞前的位置
            int yBackup = ball.y;
            ball.x = (int) (ball.startX + ball.vX * timeSpanX);//小球移動(dòng)的距離
            ball.y = (int) (ball.startY + ball.vY * timeSpanY);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            //邊界碰撞檢測(cè)
            if((ball.vX > 0 && (ball.x + ball.r) >= 479) || (ball.vX < 0 && (ball.x - ball.r) <= 0)) {
                ball.x = xBackup;
                ball.vX = 0 - ball.vX;  //速度反向
                ball.startTimeX = System.nanoTime();  //重新記錄開始時(shí)間
                ball.startX = ball.x;  //重新記錄開始位置
            }
            if((ball.vY > 0 && (ball.y + ball.r) >= 799) || (ball.vY < 0 && (ball.y - ball.r) <= 0)) {
                ball.y = yBackup;
                ball.vY = 0 - ball.vY;   //速度反向
                ball.startTimeY = System.nanoTime();   //重新記錄開始時(shí)間
                ball.startY = ball.y;   //重新記錄開始位置
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            //障礙物碰撞檢測(cè)
            for(int i = 0; i < GameView.obstructList.size(); i++) {
                Obstruction o = GameView.obstructList.get(i);
                if(Math.abs(ball.x - o.x) < (ball.r + o.hWeight) && Math.abs(ball.y - o.y) < (ball.r + o.hWeight)){
                    if(Math.abs(xBackup - o.x) >= (ball.r + o.hWeight)) {
                        ball.x = xBackup;
                        ball.vX =  0 - ball.vX;
                        ball.startTimeX = System.nanoTime();
                        ball.startX = ball.x;
                    }
                    if(Math.abs(yBackup - o.y) >= (ball.r + o.hWeight)) {
                        ball.y = yBackup;
                        ball.vY = 0 - ball.vY;
                        ball.startTimeY = System.nanoTime();
                        ball.startY = ball.y;
                    }
                    break; //跳出循環(huán)
                }
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            //調(diào)試:碰撞檢測(cè)結(jié)束時(shí)間     實(shí)驗(yàn)證明碰撞加測(cè)基本不耗時(shí)間
            long endTime = System.currentTimeMillis();
            System.out.println(endTime + "----" + startTime + "= " +(endTime - startTime));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

分析:

   1.我們將刷新時(shí)間分割成:將每次刷新時(shí)間分成若干時(shí)間小片段timeSpanX和timeSpanY,用于計(jì)算每次時(shí)間小片段小球移動(dòng)的距離.

   2.我們?cè)谛∏蚺c邊界碰撞之前,記錄一下時(shí)間startTime,在其與邊界碰撞之后,我們將其x軸、y軸方向上做一系列的操作(方向取反,回到碰撞前位置,重新記錄開始時(shí)間)其實(shí),我通過(guò)調(diào)試發(fā)現(xiàn)碰撞時(shí)間基本可以忽略.

【移動(dòng)開發(fā)】Android游戲開發(fā)SurfaceView應(yīng)用----手指發(fā)動(dòng)小球(小球碰撞檢測(cè)例子)

   3.我們這里的碰撞檢測(cè)是邊界檢測(cè),只考慮小球與障礙物、邊界的碰撞,沒(méi)有考慮小球之間的碰撞,有興趣的同學(xué)可以自行研究一下。


5.繪畫線程:

2

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
/**
 * 繪畫主界面線程
 * @author ZHF
 *
 */
public class DrawThread extends Thread {
                                                                                                                                                                                                                                                                                             
    boolean flag;  //標(biāo)記線程是否開啟
    GameView gameView;
    SurfaceHolder holder;
    Canvas canvas;
                                                                                                                                                                                                                                                                                             
    public DrawThread(GameView gameView) {
        flag = true;
        this.gameView = gameView;
        holder = gameView.getHolder();  //獲取畫布鎖
    }
    @Override
    public void run() {
        while(flag) {
            //獲取當(dāng)前繪畫開始時(shí)間
            long startTime = System.currentTimeMillis();
            synchronized(holder) {
                canvas = holder.lockCanvas(); //獲取當(dāng)前被鎖住的畫布
                if(canvas != null) {
                    gameView.draw(canvas); //對(duì)畫布進(jìn)行操作
                    holder.unlockCanvasAndPost(canvas); //釋放畫布
                }
            }
            long endTime = System.currentTimeMillis();
            int diffTime = (int) (endTime - startTime);
            Log.d("DrawTime", diffTime+"");
                                                                                                                                                                                                                                                                                                     
            while(diffTime <= 2) {
                diffTime = (int) (System.currentTimeMillis() - startTime);
                Thread.yield(); //將線程的所有權(quán)交給另一個(gè)線程
            }
        }
    }
}

   分析:

     1. 首先,我們將畫布鎖住之后,對(duì)其進(jìn)行繪畫,畫完之后自然要釋放畫布啦

2. 為了優(yōu)化程序,我們計(jì)算出繪畫所用時(shí)間,當(dāng)繪畫時(shí)間過(guò)長(zhǎng)時(shí),暫停當(dāng)前正在執(zhí)行的線程對(duì)象,通知CPU來(lái)執(zhí)行其他線程(注意:這里的其他也包含當(dāng)前線程)

6.主界面:

import java.util.ArrayList;
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
 * 游戲主界面
 * @author ZHF
 *
 */
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
    SurfaceHolder holder;
    DrawThread drawThread;  //繪畫線程
                                                                                                                                                                                               
    Ball[] ballArray = new Ball[5];  //裝小球的數(shù)組
    int ballPointer = 0;  //當(dāng)前指向數(shù)組中第幾個(gè)球
                                                                                                                                                                                               
    static ArrayList<Obstruction> obstructList = new ArrayList<Obstruction>();   //裝障礙物的集合
                                                                                                                                                                                               
    int xDown, yDown;  //記錄手指按下時(shí)的坐標(biāo)
                                                                                                                                                                                               
    public GameView(Context context) {
        super(context);
        holder = getHolder();  //獲取畫布鎖
        holder.addCallback(this);  //添加回調(diào)
                                                                                                                                                                                                   
        //初始化障礙物
        Random random = new Random();
        for(int i = 0; i < 3; i++) {
            Obstruction o = new Obstruction(random.nextInt(380) + 50, random.nextInt(700) + 50, 50);
            obstructList.add(o); //將創(chuàng)出的障礙物對(duì)象添加到集合中去
        }
    }
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        drawThread = new DrawThread(this);
        drawThread.start();    //開啟繪畫線程
    }
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        //畫布發(fā)生變化,eg:轉(zhuǎn)屏操作,處理畫布操作
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //銷毀畫布操作
        drawThread.flag = false;  //停掉線程
        drawThread = null; //GC會(huì)及時(shí)發(fā)現(xiàn)并處理掉該對(duì)象
    }
    public void draw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);  //背景顏色
        Paint paint = new Paint();
        paint.setTextSize(25);
        paint.setColor(Color.WHITE);  //文字顏色
        canvas.drawText("小球碰撞檢測(cè)", 50, 20, paint);
                                                                                                                                                                                                   
        //畫出小球
        for(int i = 0; i < 5; i++) {
            if(ballArray[i] != null) {
                ballArray[i].drawSelf(canvas);  //當(dāng)前小球繪畫出自己
            }
        }
        //畫出障礙物
        for(int i = 0; i < obstructList.size(); i++) {
            obstructList.get(i).drawSelf(canvas);
        }
    }
                                                                                                                                                                                               
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
                                                                                                                                                                                                   
        if(event.getAction() == 0) {  //按下
            //記錄按下時(shí)X,Y的坐標(biāo)
            xDown = x;
            yDown = y;
            //生成第一個(gè)球
            Ball ball = new Ball(x, y, 0, 0, 20);
            if(ballArray[ballPointer] != null) {
                ballArray[ballPointer].ballThread.flag = false;  //關(guān)閉小球移動(dòng)線程
                ballArray[ballPointer].ballThread = null;
            }
            ballArray[ballPointer] = ball;
                                                                                                                                                                                                       
        } else if(event.getAction() == 1) { //抬起
            int xOffset = x - xDown;
            int yOffset = y - yDown;
            double sin = yOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);
            double cos = xOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);
                                                                                                                                                                                                       
            ballArray[ballPointer].startTimeX = System.nanoTime(); //當(dāng)前小球開始時(shí)間
            ballArray[ballPointer].startTimeY = System.nanoTime();
            ballArray[ballPointer].vX = (float) (500 * cos);  //當(dāng)前小球的速度
            ballArray[ballPointer].vY = (float) (500 * sin);
            ballArray[ballPointer].ballThread.start();   //開啟小球移動(dòng)線程
                                                                                                                                                                                                       
            ballPointer ++;  //下一個(gè)小球
            if(ballPointer >= 5) {
                ballPointer = 0;
            }
        }
        return true;
    }
}

分析:

   1.這里我們啟動(dòng)小球移動(dòng)線程方式:采用手指觸屏滑動(dòng),記錄按下、抬起位置,通過(guò)計(jì)算角度得出算出發(fā)射方向。

   2.每次發(fā)出小球后下標(biāo)ballPointer ++指向下一個(gè)小球,當(dāng)?shù)竭_(dá)數(shù)組上限后,重新返回到下標(biāo)0.



   ok! 到此功能已經(jīng)實(shí)現(xiàn),想要完整源碼在此下載:http://download.csdn.net/detail/zhf651555765/5775035


附件:http://down.51cto.com/data/2363159
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI