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