天天看點

【移動開發】Android遊戲開發SurfaceView應用----手指發動小球(小球碰撞檢測例子)

     為了複習一下SurfaceView的使用,在此寫了一個經典的小球碰撞檢測例子程式,希望能夠夠幫助正在學習遊戲的人。

先看一下效果圖:

<a target="_blank" href="http://blog.51cto.com/attachment/201307/095724635.png"></a>

   下面我們就來逐一分析一下它的實作過程:

1.啟動入口:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code>import</code> <code>android.os.Bundle;</code>

<code>import</code> <code>android.app.Activity;</code>

<code>import</code> <code>android.view.Window;</code>

<code>import</code> <code>android.view.WindowManager;</code>

<code>public</code> <code>class</code> <code>MainActivity </code><code>extends</code> <code>Activity {</code>

<code>    </code><code>@Override</code>

<code>    </code><code>protected</code> <code>void</code> <code>onCreate(Bundle savedInstanceState) {</code>

<code>        </code><code>super</code><code>.onCreate(savedInstanceState);</code>

<code>        </code><code>//全屏設定</code>

<code>        </code><code>requestWindowFeature(Window.FEATURE_NO_TITLE);</code>

<code>        </code><code>this</code><code>.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,</code>

<code>                </code><code>WindowManager.LayoutParams.FLAG_FULLSCREEN);</code>

<code>        </code><code>//将畫布放進去</code>

<code>        </code><code>GameView gameView = </code><code>new</code> <code>GameView(</code><code>this</code><code>);</code>

<code>        </code><code>setContentView(gameView);</code>

<code>    </code><code>}</code>

<code>}</code>

2.小球類

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

<code>import</code> <code>android.graphics.Canvas;</code>

<code>import</code> <code>android.graphics.Color;</code>

<code>import</code> <code>android.graphics.Paint;</code>

<code>/**</code>

<code> </code><code>* 小球執行個體</code>

<code> </code><code>* @author ZHF</code>

<code> </code><code>*</code>

<code> </code><code>*/</code>

<code>public</code> <code>class</code> <code>Ball {</code>

<code>    </code><code>int</code> <code>x, y;  </code><code>//小球的實時位置</code>

<code>    </code><code>int</code> <code>startX, startY;   </code><code>//小球的初始位置</code>

<code>    </code><code>float</code> <code>vX, vY;    </code><code>//小球的速度</code>

<code>    </code><code>int</code> <code>r; </code><code>//小球的半徑</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     </code> 

<code>    </code><code>double</code> <code>startTimeX;  </code><code>//開始時間</code>

<code>    </code><code>double</code> <code>startTimeY;  </code><code>//開始時間</code>

<code>    </code><code>BallThread ballThread;  </code><code>//小球移動線程</code>

<code>    </code><code>Paint paint = </code><code>new</code> <code>Paint();  </code><code>//畫筆</code>

<code>    </code><code>public</code> <code>Ball(</code><code>int</code> <code>x, </code><code>int</code> <code>y, </code><code>float</code> <code>vX, </code><code>float</code> <code>vY, </code><code>int</code> <code>r) {</code>

<code>        </code><code>this</code><code>.x = x;</code>

<code>        </code><code>this</code><code>.y = y;</code>

<code>        </code><code>this</code><code>.startX = x;</code>

<code>        </code><code>this</code><code>.startY = y;</code>

<code>        </code><code>this</code><code>.vX = vX;</code>

<code>        </code><code>this</code><code>.vY = vY;</code>

<code>        </code><code>this</code><code>.r = r;</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         </code> 

<code>        </code><code>//為每個小球執行個體化一個獨立的線程,在擡手時開啟線程</code>

<code>        </code><code>ballThread = </code><code>new</code> <code>BallThread(</code><code>this</code><code>);</code>

<code>        </code><code>paint.setColor(Color.RED);  </code><code>//小球為紅色實心</code>

<code>    </code><code>/**繪畫方法**/</code>

<code>    </code><code>public</code> <code>void</code> <code>drawSelf(Canvas canvas) {</code>

<code>        </code><code>canvas.drawCircle(x, y, r, paint);</code>

3.障礙物類:

<code> </code><code>* 障礙物</code>

<code>public</code> <code>class</code> <code>Obstruction {</code>

<code>    </code><code>int</code> <code>x, y;</code>

<code>    </code><code>int</code> <code>hWeight;  </code><code>//寬度和高度一樣</code>

<code>    </code><code>Paint paint = </code><code>new</code> <code>Paint();</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  </code> 

<code>    </code><code>public</code> <code>Obstruction(</code><code>int</code> <code>x, </code><code>int</code> <code>y, </code><code>int</code> <code>hWeight) {</code>

<code>        </code><code>this</code><code>.hWeight = hWeight;</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      </code> 

<code>        </code><code>paint.setColor(Color.GREEN);  </code><code>//設定畫筆顔色</code>

<code>        </code><code>canvas.drawRect(x - hWeight, y - hWeight, x + hWeight, y + hWeight, paint);</code>

以上代碼比較簡單,在此不多做解釋,下面主要來看一下兩個主要線程類:

4.小球移動線程(碰撞檢測):

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

<code> </code><code>* 小球移動和碰撞檢測線程</code>

<code>public</code> <code>class</code> <code>BallThread </code><code>extends</code> <code>Thread {</code>

<code>    </code><code>boolean</code> <code>flag;   </code><code>//标記線程是否開啟</code>

<code>    </code><code>Ball ball;  </code><code>//小球</code>

<code>    </code><code>double</code> <code>currentTime;  </code><code>//目前時間</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                     </code> 

<code>    </code><code>public</code> <code>BallThread(Ball ball) {</code>

<code>        </code><code>flag = </code><code>true</code><code>;</code>

<code>        </code><code>this</code><code>.ball = ball;</code>

<code>    </code><code>public</code> <code>void</code> <code>run() {</code>

<code>        </code><code>while</code><code>(flag) {</code>

<code>            </code><code>//調試:碰撞檢測開始時間</code>

<code>            </code><code>long</code> <code>startTime = System.currentTimeMillis();</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                             </code> 

<code>             </code><code>//計算出小球移動的時間片:将每次重新整理分成若幹時間小片段,用于計算每次時間小片段小球移動的距離</code>

<code>            </code><code>currentTime = System.nanoTime();</code>

<code>            </code><code>double</code> <code>timeSpanX = (currentTime - ball.startTimeX) /</code><code>1000</code> <code>/</code><code>1000</code> <code>/</code><code>1000</code><code>;</code>

<code>            </code><code>double</code> <code>timeSpanY = (currentTime - ball.startTimeY) /</code><code>1000</code> <code>/</code><code>1000</code> <code>/</code><code>1000</code><code>;</code>

<code>            </code><code>int</code> <code>xBackup = ball.x; </code><code>//儲存小球的碰撞前的位置</code>

<code>            </code><code>int</code> <code>yBackup = ball.y;</code>

<code>            </code><code>ball.x = (</code><code>int</code><code>) (ball.startX + ball.vX * timeSpanX);</code><code>//小球移動的距離</code>

<code>            </code><code>ball.y = (</code><code>int</code><code>) (ball.startY + ball.vY * timeSpanY);</code>

<code>            </code><code>//邊界碰撞檢測</code>

<code>            </code><code>if</code><code>((ball.vX &gt; </code><code>0</code> <code>&amp;&amp; (ball.x + ball.r) &gt;= </code><code>479</code><code>) || (ball.vX &lt; </code><code>0</code> <code>&amp;&amp; (ball.x - ball.r) &lt;= </code><code>0</code><code>)) {</code>

<code>                </code><code>ball.x = xBackup;</code>

<code>                </code><code>ball.vX = </code><code>0</code> <code>- ball.vX;  </code><code>//速度反向</code>

<code>                </code><code>ball.startTimeX = System.nanoTime();  </code><code>//重新記錄開始時間</code>

<code>                </code><code>ball.startX = ball.x;  </code><code>//重新記錄開始位置</code>

<code>            </code><code>}</code>

<code>            </code><code>if</code><code>((ball.vY &gt; </code><code>0</code> <code>&amp;&amp; (ball.y + ball.r) &gt;= </code><code>799</code><code>) || (ball.vY &lt; </code><code>0</code> <code>&amp;&amp; (ball.y - ball.r) &lt;= </code><code>0</code><code>)) {</code>

<code>                </code><code>ball.y = yBackup;</code>

<code>                </code><code>ball.vY = </code><code>0</code> <code>- ball.vY;   </code><code>//速度反向</code>

<code>                </code><code>ball.startTimeY = System.nanoTime();   </code><code>//重新記錄開始時間</code>

<code>                </code><code>ball.startY = ball.y;   </code><code>//重新記錄開始位置</code>

<code>            </code><code>//障礙物碰撞檢測</code>

<code>            </code><code>for</code><code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; GameView.obstructList.size(); i++) {</code>

<code>                </code><code>Obstruction o = GameView.obstructList.get(i);</code>

<code>                </code><code>if</code><code>(Math.abs(ball.x - o.x) &lt; (ball.r + o.hWeight) &amp;&amp; Math.abs(ball.y - o.y) &lt; (ball.r + o.hWeight)){</code>

<code>                    </code><code>if</code><code>(Math.abs(xBackup - o.x) &gt;= (ball.r + o.hWeight)) {</code>

<code>                        </code><code>ball.x = xBackup;</code>

<code>                        </code><code>ball.vX =  </code><code>0</code> <code>- ball.vX;</code>

<code>                        </code><code>ball.startTimeX = System.nanoTime();</code>

<code>                        </code><code>ball.startX = ball.x;</code>

<code>                    </code><code>}</code>

<code>                    </code><code>if</code><code>(Math.abs(yBackup - o.y) &gt;= (ball.r + o.hWeight)) {</code>

<code>                        </code><code>ball.y = yBackup;</code>

<code>                        </code><code>ball.vY = </code><code>0</code> <code>- ball.vY;</code>

<code>                        </code><code>ball.startTimeY = System.nanoTime();</code>

<code>                        </code><code>ball.startY = ball.y;</code>

<code>                    </code><code>break</code><code>; </code><code>//跳出循環</code>

<code>                </code><code>}</code>

<code>            </code><code>//調試:碰撞檢測結束時間     實驗證明碰撞加測基本不耗時間</code>

<code>            </code><code>long</code> <code>endTime = System.currentTimeMillis();</code>

<code>            </code><code>System.out.println(endTime + </code><code>"----"</code> <code>+ startTime + </code><code>"= "</code> <code>+(endTime - startTime));</code>

<code>            </code><code>try</code> <code>{</code>

<code>                </code><code>Thread.sleep(</code><code>10</code><code>);</code>

<code>            </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

<code>                </code><code>e.printStackTrace();</code>

<code>        </code><code>}</code>

分析:

   1.我們将重新整理時間分割成:将每次重新整理時間分成若幹時間小片段timeSpanX和timeSpanY,用于計算每次時間小片段小球移動的距離.

   2.我們在小球與邊界碰撞之前,記錄一下時間startTime,在其與邊界碰撞之後,我們将其x軸、y軸方向上做一系列的操作(方向取反,回到碰撞前位置,重新記錄開始時間)其實,我通過調試發現碰撞時間基本可以忽略.

<a target="_blank" href="http://blog.51cto.com/attachment/201307/110014205.png"></a>

   3.我們這裡的碰撞檢測是邊界檢測,隻考慮小球與障礙物、邊界的碰撞,沒有考慮小球之間的碰撞,有興趣的同學可以自行研究一下。

5.繪畫線程:

<code>import</code> <code>android.util.Log;</code>

<code>import</code> <code>android.view.SurfaceHolder;</code>

<code> </code><code>* 繪畫主界面線程</code>

<code>public</code> <code>class</code> <code>DrawThread </code><code>extends</code> <code>Thread {</code>

<code>                                                                                                                                                                                                                                                                                             </code> 

<code>    </code><code>boolean</code> <code>flag;  </code><code>//标記線程是否開啟</code>

<code>    </code><code>GameView gameView;</code>

<code>    </code><code>SurfaceHolder holder;</code>

<code>    </code><code>Canvas canvas;</code>

<code>    </code><code>public</code> <code>DrawThread(GameView gameView) {</code>

<code>        </code><code>this</code><code>.gameView = gameView;</code>

<code>        </code><code>holder = gameView.getHolder();  </code><code>//擷取畫布鎖</code>

<code>            </code><code>//擷取目前繪畫開始時間</code>

<code>            </code><code>synchronized</code><code>(holder) {</code>

<code>                </code><code>canvas = holder.lockCanvas(); </code><code>//擷取目前被鎖住的畫布</code>

<code>                </code><code>if</code><code>(canvas != </code><code>null</code><code>) {</code>

<code>                    </code><code>gameView.draw(canvas); </code><code>//對畫布進行操作</code>

<code>                    </code><code>holder.unlockCanvasAndPost(canvas); </code><code>//釋放畫布</code>

<code>            </code><code>int</code> <code>diffTime = (</code><code>int</code><code>) (endTime - startTime);</code>

<code>            </code><code>Log.d(</code><code>"DrawTime"</code><code>, diffTime+</code><code>""</code><code>);</code>

<code>                                                                                                                                                                                                                                                                                                     </code> 

<code>            </code><code>while</code><code>(diffTime &lt;= </code><code>2</code><code>) {</code>

<code>                </code><code>diffTime = (</code><code>int</code><code>) (System.currentTimeMillis() - startTime);</code>

<code>                </code><code>Thread.yield(); </code><code>//将線程的所有權交給另一個線程</code>

   分析:

     1. 首先,我們将畫布鎖住之後,對其進行繪畫,畫完之後自然要釋放畫布啦

2. 為了優化程式,我們計算出繪畫所用時間,當繪畫時間過長時,暫停目前正在執行的線程對象,通知CPU來執行其他線程(注意:這裡的其他也包含目前線程)

6.主界面:

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

<code>import</code> <code>java.util.ArrayList;</code>

<code>import</code> <code>java.util.Random;</code>

<code>import</code> <code>android.content.Context;</code>

<code>import</code> <code>android.view.MotionEvent;</code>

<code>import</code> <code>android.view.SurfaceView;</code>

<code> </code><code>* 遊戲主界面</code>

<code>public</code> <code>class</code> <code>GameView </code><code>extends</code> <code>SurfaceView </code><code>implements</code> <code>SurfaceHolder.Callback {</code>

<code>    </code><code>DrawThread drawThread;  </code><code>//繪畫線程</code>

<code>                                                                                                                                                                                               </code> 

<code>    </code><code>Ball[] ballArray = </code><code>new</code> <code>Ball[</code><code>5</code><code>];  </code><code>//裝小球的數組</code>

<code>    </code><code>int</code> <code>ballPointer = </code><code>0</code><code>;  </code><code>//目前指向數組中第幾個球</code>

<code>    </code><code>static</code> <code>ArrayList&lt;Obstruction&gt; obstructList = </code><code>new</code> <code>ArrayList&lt;Obstruction&gt;();   </code><code>//裝障礙物的集合</code>

<code>    </code><code>int</code> <code>xDown, yDown;  </code><code>//記錄手指按下時的坐标</code>

<code>    </code><code>public</code> <code>GameView(Context context) {</code>

<code>        </code><code>super</code><code>(context);</code>

<code>        </code><code>holder = getHolder();  </code><code>//擷取畫布鎖</code>

<code>        </code><code>holder.addCallback(</code><code>this</code><code>);  </code><code>//添加回調</code>

<code>                                                                                                                                                                                                   </code> 

<code>        </code><code>//初始化障礙物</code>

<code>        </code><code>Random random = </code><code>new</code> <code>Random();</code>

<code>        </code><code>for</code><code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; </code><code>3</code><code>; i++) {</code>

<code>            </code><code>Obstruction o = </code><code>new</code> <code>Obstruction(random.nextInt(</code><code>380</code><code>) + </code><code>50</code><code>, random.nextInt(</code><code>700</code><code>) + </code><code>50</code><code>, </code><code>50</code><code>);</code>

<code>            </code><code>obstructList.add(o); </code><code>//将創出的障礙物對象添加到集合中去</code>

<code>    </code><code>public</code> <code>void</code> <code>surfaceCreated(SurfaceHolder holder) {</code>

<code>        </code><code>drawThread = </code><code>new</code> <code>DrawThread(</code><code>this</code><code>);</code>

<code>        </code><code>drawThread.start();    </code><code>//開啟繪畫線程</code>

<code>    </code><code>public</code> <code>void</code> <code>surfaceChanged(SurfaceHolder holder, </code><code>int</code> <code>format, </code><code>int</code> <code>width,</code>

<code>            </code><code>int</code> <code>height) {</code>

<code>        </code><code>//畫布發生變化,eg:轉屏操作,處理畫布操作</code>

<code>    </code><code>public</code> <code>void</code> <code>surfaceDestroyed(SurfaceHolder holder) {</code>

<code>        </code><code>//銷毀畫布操作</code>

<code>        </code><code>drawThread.flag = </code><code>false</code><code>;  </code><code>//停掉線程</code>

<code>        </code><code>drawThread = </code><code>null</code><code>; </code><code>//GC會及時發現并處理掉該對象</code>

<code>    </code><code>public</code> <code>void</code> <code>draw(Canvas canvas) {</code>

<code>        </code><code>canvas.drawColor(Color.BLACK);  </code><code>//背景顔色</code>

<code>        </code><code>Paint paint = </code><code>new</code> <code>Paint();</code>

<code>        </code><code>paint.setTextSize(</code><code>25</code><code>);</code>

<code>        </code><code>paint.setColor(Color.WHITE);  </code><code>//文字顔色</code>

<code>        </code><code>canvas.drawText(</code><code>"小球碰撞檢測"</code><code>, </code><code>50</code><code>, </code><code>20</code><code>, paint);</code>

<code>        </code><code>//畫出小球</code>

<code>        </code><code>for</code><code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; </code><code>5</code><code>; i++) {</code>

<code>            </code><code>if</code><code>(ballArray[i] != </code><code>null</code><code>) {</code>

<code>                </code><code>ballArray[i].drawSelf(canvas);  </code><code>//目前小球繪畫出自己</code>

<code>        </code><code>//畫出障礙物</code>

<code>        </code><code>for</code><code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; obstructList.size(); i++) {</code>

<code>            </code><code>obstructList.</code><code>get</code><code>(i).drawSelf(canvas);</code>

<code>    </code><code>public</code> <code>boolean onTouchEvent(MotionEvent event) {</code>

<code>        </code><code>int</code> <code>x = (</code><code>int</code><code>) event.getX();</code>

<code>        </code><code>int</code> <code>y = (</code><code>int</code><code>) event.getY();</code>

<code>        </code><code>if</code><code>(event.getAction() == </code><code>0</code><code>) {  </code><code>//按下</code>

<code>            </code><code>//記錄按下時X,Y的坐标</code>

<code>            </code><code>xDown = x;</code>

<code>            </code><code>yDown = y;</code>

<code>            </code><code>//生成第一個球</code>

<code>            </code><code>Ball ball = </code><code>new</code> <code>Ball(x, y, </code><code>0</code><code>, </code><code>0</code><code>, </code><code>20</code><code>);</code>

<code>            </code><code>if</code><code>(ballArray[ballPointer] != </code><code>null</code><code>) {</code>

<code>                </code><code>ballArray[ballPointer].ballThread.flag = </code><code>false</code><code>;  </code><code>//關閉小球移動線程</code>

<code>                </code><code>ballArray[ballPointer].ballThread = </code><code>null</code><code>;</code>

<code>            </code><code>ballArray[ballPointer] = ball;</code>

<code>                                                                                                                                                                                                       </code> 

<code>        </code><code>} </code><code>else</code> <code>if</code><code>(event.getAction() == </code><code>1</code><code>) { </code><code>//擡起</code>

<code>            </code><code>int</code> <code>xOffset = x - xDown;</code>

<code>            </code><code>int</code> <code>yOffset = y - yDown;</code>

<code>            </code><code>double sin = yOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);</code>

<code>            </code><code>double cos = xOffset / Math.sqrt(xOffset * xOffset + yOffset * yOffset);</code>

<code>            </code><code>ballArray[ballPointer].startTimeX = System.nanoTime(); </code><code>//目前小球開始時間</code>

<code>            </code><code>ballArray[ballPointer].startTimeY = System.nanoTime();</code>

<code>            </code><code>ballArray[ballPointer].vX = (float) (</code><code>500</code> <code>* cos);  </code><code>//目前小球的速度</code>

<code>            </code><code>ballArray[ballPointer].vY = (float) (</code><code>500</code> <code>* sin);</code>

<code>            </code><code>ballArray[ballPointer].ballThread.start();   </code><code>//開啟小球移動線程</code>

<code>            </code><code>ballPointer ++;  </code><code>//下一個小球</code>

<code>            </code><code>if</code><code>(ballPointer &gt;= </code><code>5</code><code>) {</code>

<code>                </code><code>ballPointer = </code><code>0</code><code>;</code>

<code>        </code><code>return</code> <code>true</code><code>;</code>

   1.這裡我們啟動小球移動線程方式:采用手指觸屏滑動,記錄按下、擡起位置,通過計算角度得出算出發射方向。

   2.每次發出小球後下标ballPointer ++指向下一個小球,當到達數組上限後,重新傳回到下标0.

<a href="http://down.51cto.com/data/2363159" target="_blank">附件:http://down.51cto.com/data/2363159</a>

     本文轉自zhf651555765 51CTO部落格,原文連結:http://blog.51cto.com/smallwoniu/1251882,如需轉載請自行聯系原作者