李華明Himi 原創,轉載務必在明顯處註明:
轉載自【黑米GameDev街區】 原文連結: http://www.himigame.com/android-game/384.html
,
此章節為正在創作的遊戲開發書籍中的一部分,由於寫書的緣故很久沒有更新了,挺對不起大家的;那麼今天放出書中的一部分,讓大家先睹為快吧;
在Android系統的手機,有的根本沒有實體的上下左右導航按鍵,所以很多遊戲都會有利用Android手機都具有觸屏的特性,製作360度搖杆來取代遊戲方向鍵,這樣不僅能使介面UI變得很美觀,而且更加的方便操作;
下面先來看效果吧:
下面開始實現:
首先,肯定是繪製兩個圓形,無可置疑;圓心點重合,為了區分 ,所以設定了不同顏色;
灰色:固定不動的搖杆背景(也意味著搖杆的活動範圍);
紅色:搖杆;
然後考慮:紅色搖杆肯定跟隨手指觸屏的位置而移動,那麼這個很easy啦,只要在觸屏事件中處理,將擷取的觸屏XY座標賦值與搖杆XY座標即可;這個沒問題;但是緊接著在思考一個問題:
一般情況下,我們不可能希望搖杆一直跟隨手指位置,所以需要一個搖杆的即時區域,也就如同中的灰色地區,在灰色地區內搖杆可以隨著使用者的觸屏位置移動,但是一旦使用者觸屏位置在即時區域之外,搖杆就不應該跑出灰色地區;所以具體實現步驟如下:
1) 得到通過搖杆的座標與觸屏點的座標得到所形成的角度Angle
2) 根據Angle,以及已知所在圓的半徑,算出搖杆所在灰色圓形上做圓周運動的當前X,Y座標;
首先第一步: 算出搖杆座標與觸屏座標形成的角度
我們肯定已知搖杆當前座標,並且當使用者觸屏時的座標也可以在觸屏按鍵中得到,那麼擷取的方法就可以寫成一個方法,方法如下:
/***<br /> * 得到兩點之間的弧度<br /> */<br />public double getRad(float px1, float py1, float px2, float py2) {<br />//得到兩點X的距離<br />float x = px2 - px1;<br />//得到兩點Y的距離<br />float y = py1 - py2;<br />//算出斜邊長<br />float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));<br />//得到這個角度的餘弦值(通過三角函數中的定理 :鄰邊/斜邊=角度餘弦值)<br />float cosAngle = x / xie;<br />//通過反餘弦定理擷取到其角度的弧度<br />float rad = (float) Math.acos(cosAngle);<br />//注意:當觸屏的位置Y座標<搖杆的Y座標我們要取反值-0~-180<br />if (py2 < py1) {<br />rad = -rad;<br />}<br />return rad;<br />}
在Java中 Math類中的反餘弦函數返回的不是角度是弧度,這一點要格外注意;
另外一點就是,因為三角函數角度範圍是0~180度,所以反之應該是-0~-180度;
通過此函數擷取到搖杆與使用者觸屏位置所形成的角度之後,我們就可以通過圓周公式來得到其搖杆的XY座標了;方法如下:
/**<br /> *<br /> * @param R<br /> * 圓周運動的旋轉點<br /> * @param centerX<br /> * 旋轉點X<br /> * @param centerY<br /> * 旋轉點Y<br /> * @param rad<br /> * 旋轉的弧度<br /> */<br />public void getXY(float centerX, float centerY, float R, double rad) {<br />//擷取圓周運動的X座標<br />SmallRockerCircleX = (float) (R * Math.cos(rad)) + centerX;<br />//擷取圓周運動的Y座標<br />SmallRockerCircleY = (float) (R * Math.sin(rad)) + centerY;<br />}
圓周運動公式:通過三角函數定理得出:
X座標:所在圓的半徑*角度的餘弦值
Y座標:所在圓形半徑*角度的正弦值
圓周的大小,由所在圓的半徑R的大小來決定;
通過以上的公式我們就可以讓搖杆在灰色圓形上做圓周運動,當然除此之外我們還要注意三點:
1:做圓周運動的大小,應該跟灰色地區的半徑相同;
2:觸屏事件中應該首先判定使用者觸屏的位置是否在灰色地區中,如果不在,我們就應該擷取搖杆與觸屏點的角度然後擷取搖杆應該在圓周運動上的XY座標;如果在,就沒有處理了,只要將搖杆位置隨著使用者點擊位置就好了;
3:在觸屏事件中,當使用者手指離開螢幕後,應該讓搖杆的位置恢複到初始的位置狀態;
下面是整個項目的MySurfaceView中全部代碼:
package com.rp;<br />import android.content.Context;<br />import android.graphics.Canvas;<br />import android.graphics.Color;<br />import android.graphics.Paint;<br />import android.util.Log;<br />import android.view.MotionEvent;<br />import android.view.SurfaceHolder;<br />import android.view.SurfaceView;<br />import android.view.SurfaceHolder.Callback;<br />public class MySurfaceView extends SurfaceView implements Callback, Runnable {<br />private Thread th;<br />private SurfaceHolder sfh;<br />private Canvas canvas;<br />private Paint paint;<br />private boolean flag;<br />//固定搖杆背景圓形的X,Y座標以及半徑<br />private int RockerCircleX = 100;<br />private int RockerCircleY = 100;<br />private int RockerCircleR = 50;<br />//搖杆的X,Y座標以及搖杆的半徑<br />private float SmallRockerCircleX = 100;<br />private float SmallRockerCircleY = 100;<br />private float SmallRockerCircleR = 20;<br />public MySurfaceView(Context context) {<br />super(context);<br />Log.v("Himi", "MySurfaceView");<br />this.setKeepScreenOn(true);<br />sfh = this.getHolder();<br />sfh.addCallback(this);<br />paint = new Paint();<br />paint.setAntiAlias(true);<br />setFocusable(true);<br />setFocusableInTouchMode(true);<br />}<br />public void surfaceCreated(SurfaceHolder holder) {<br />th = new Thread(this);<br />flag = true;<br />th.start();<br />}<br />/***<br /> * 得到兩點之間的弧度<br /> */<br />public double getRad(float px1, float py1, float px2, float py2) {<br />//得到兩點X的距離<br />float x = px2 - px1;<br />//得到兩點Y的距離<br />float y = py1 - py2;<br />//算出斜邊長<br />float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));<br />//得到這個角度的餘弦值(通過三角函數中的定理 :鄰邊/斜邊=角度餘弦值)<br />float cosAngle = x / xie;<br />//通過反餘弦定理擷取到其角度的弧度<br />float rad = (float) Math.acos(cosAngle);<br />//注意:當觸屏的位置Y座標<搖杆的Y座標我們要取反值-0~-180<br />if (py2 < py1) {<br />rad = -rad;<br />}<br />return rad;<br />}<br />@Override<br />public boolean onTouchEvent(MotionEvent event) {<br />if (event.getAction() == MotionEvent.ACTION_DOWN ||<br />event.getAction() == MotionEvent.ACTION_MOVE) {<br />// 當觸屏地區不在活動範圍內<br />if (Math.sqrt(Math.pow((RockerCircleX - (int) event.getX()), 2)<br />+ Math.pow((RockerCircleY - (int) event.getY()), 2)) >= RockerCircleR) {<br />//得到搖杆與觸屏點所形成的角度<br />double tempRad = getRad(RockerCircleX, RockerCircleY, event.getX(), event.getY());<br />//保證內部小圓運動的長度限制<br />getXY(RockerCircleX, RockerCircleY, RockerCircleR, tempRad);<br />} else {//如果小球中心點小於即時區域則隨著使用者觸屏點移動即可<br />SmallRockerCircleX = (int) event.getX();<br />SmallRockerCircleY = (int) event.getY();<br />}<br />} else if (event.getAction() == MotionEvent.ACTION_UP) {<br />//當釋放按鍵時搖杆要恢複搖杆的位置為初始位置<br />SmallRockerCircleX = 100;<br />SmallRockerCircleY = 100;<br />}<br />return true;<br />}<br />/**<br /> *<br /> * @param R<br /> * 圓周運動的旋轉點<br /> * @param centerX<br /> * 旋轉點X<br /> * @param centerY<br /> * 旋轉點Y<br /> * @param rad<br /> * 旋轉的弧度<br /> */<br />public void getXY(float centerX, float centerY, float R, double rad) {<br />//擷取圓周運動的X座標<br />SmallRockerCircleX = (float) (R * Math.cos(rad)) + centerX;<br />//擷取圓周運動的Y座標<br />SmallRockerCircleY = (float) (R * Math.sin(rad)) + centerY;<br />}<br />public void draw() {<br />try {<br />canvas = sfh.lockCanvas();<br />canvas.drawColor(Color.WHITE);<br />//設定透明度<br />paint.setColor(0x70000000);<br />//繪製搖杆背景<br />canvas.drawCircle(RockerCircleX, RockerCircleY, RockerCircleR, paint);<br />paint.setColor(0x70ff0000);<br />//繪製搖杆<br />canvas.drawCircle(SmallRockerCircleX, SmallRockerCircleY,<br />SmallRockerCircleR, paint);<br />} catch (Exception e) {<br />// TODO: handle exception<br />} finally {<br />try {<br />if (canvas != null)<br />sfh.unlockCanvasAndPost(canvas);<br />} catch (Exception e2) {<br />}<br />}<br />}<br />public void run() {<br />// TODO Auto-generated method stub<br />while (flag) {<br />draw();<br />try {<br />Thread.sleep(50);<br />} catch (Exception ex) {<br />}<br />}<br />}<br />public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {<br />Log.v("Himi", "surfaceChanged");<br />}<br />public void surfaceDestroyed(SurfaceHolder holder) {<br />flag = false;<br />Log.v("Himi", "surfaceDestroyed");<br />}<br />}
如果大家想美化搖杆,那就讓你們的美工給出兩張圓形圖吧;還在等什麼,立刻為你的遊戲加上搖杆吧~娃哈哈
好啦,其實這裡只是給大家介紹思路,具體的書中,我已經將此封裝成了一個搖杆類,這樣更加的OOP,至於如何封裝,大家可以根據需要自由來設計;今天的博文寫的可能不是很清晰,因為我現在腦子昏昏沉沉的 - - 。
從寫書開始到現在,每天都3-4點睡覺,唉、不過值得高興的是,如果沒有特殊情況,6月底書籍就要交稿了,大家期待下吧;我也會繼續努力這最後兩個月的;
項目: http://www.himigame.com/android-game/384.html
還對了,以前上傳的項目資源分都將資源調整成0分了,全部設定為免費下載,方便大家;