360°平滑游戏摇杆(触屏方向导航)
Android游戲開發二十四】360°平滑游戲搖桿(觸屏方向導航)
本站文章均為 李華明Himi 原創,轉載務必在明顯處注明:(作者新浪微博: @李華明Himi )
轉載自【黑米GameDev街區】原文鏈接: http://www.himigame.com/android-game/384.html
? 點擊訂閱 ? 本博客最新動態!及時將最新博文通知您!
分享 ?
此章節為正在創作的游戲開發書籍書稿中的一部分,由于寫書的緣故很久沒有更新了,挺對不起大家的;那么今天放出書稿中的一部分,讓大家先睹為快吧;
在Android系統的手機,有的根本沒有實體的上下左右導航按鍵,所以很多游戲都會有利用Android手機都具有觸屏的特性,制作360度搖桿來取代游戲方向鍵,這樣不僅能使界面UI變得很美觀,而且更加的方便操作;
下面先來看效果吧:
下面開始實現:
首先,肯定是繪制兩個圓形,無可置疑;圓心點重合,為了區分 ,所以設置了不同顏色;
灰色:固定不動的搖桿背景(也意味著搖桿的活動范圍);
紅色:搖桿;
然后考慮:紅色搖桿肯定跟隨手指觸屏的位置而移動,那么這個很easy啦,只要在觸屏事件中處理,將獲取的觸屏XY坐標賦值與搖桿XY坐標即可;這個沒問題;但是緊接著在思考一個問題:
一般情況下,我們不可能希望搖桿一直跟隨手指位置,所以需要一個搖桿的活動區域,也就如同上圖中的灰色區域,在灰色區域內搖桿可以隨著用戶的觸屏位置移動,但是一旦用戶觸屏位置在活動區域之外,搖桿就不應該跑出灰色區域;所以具體實現步驟如下:
1) 得到通過搖桿的坐標與觸屏點的坐標得到所形成的角度Angle
2) 根據Angle,以及已知所在圓的半徑,算出搖桿所在灰色圓形上做圓周運動的當前X,Y坐標;
首先第一步: 算出搖桿坐標與觸屏坐標形成的角度
我們肯定已知搖桿當前坐標,并且當用戶觸屏時的坐標也可以在觸屏按鍵中得到,那么獲取的方法就可以寫成一個方法,方法如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /*** * 得到兩點之間的弧度 */ 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坐標了;方法如下
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * * @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中全部代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 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"); ??? } } |
總結
以上是生活随笔為你收集整理的360°平滑游戏摇杆(触屏方向导航)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 传感器开发详解
- 下一篇: sdcms php版手册,sdcms常用