很多Android手機都沒有實體的上下左右導航鍵,那麼怎麼控制游戲方向呢?Android游戲開發者想到了利用Android手機的觸屏特性,制作360度搖桿來取代游戲方向鍵,最終使得界面UI變得美觀,用戶操作也方便。
先貼上效果圖:
下面開始實現:
首先,肯定是繪制兩個圓形,無可置疑;圓心點重合,為了區分 ,所以設置了不同顏色;
灰色:固定不動的搖桿背景(也意味著搖桿的活動范圍);
紅色:搖桿;
然後考慮:紅色搖桿肯定跟隨手指觸屏的位置而移動,那麼這個很easy啦,只要在觸屏事件中處理,將獲取的觸屏XY坐標賦值與搖桿XY坐標即可;這個沒問題;但是緊接著在思考一個問題:
一般情況下,我們不可能希望搖桿一直跟隨手指位置,所以需要一個搖桿的活動區域,也就如同上圖中的灰色區域,在灰色區域內搖桿可以隨著用戶的觸屏位置移動,但是一旦用戶觸屏位置在活動區域之外,搖桿就不應該跑出灰色區域;所以具體實現步驟如下:
1)得到通過搖桿的坐標與觸屏點的坐標得到所形成的角度Angle。
2)根據Angle,以及已知所在圓的半徑,算出搖桿所在灰色圓形上做圓周運動的當前X,Y坐標。
首先第一步: 算出搖桿坐標與觸屏坐標形成的角度。
我們肯定已知搖桿當前坐標,並且當用戶觸屏時的坐標也可以在觸屏按鍵中得到,那麼獲取的方法就可以寫成一個方法,方法如下:
Java代碼
- /***
- * 得到兩點之間的弧度
- */
- 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坐標了;方法如下:
Java代碼
- /**
- *
- * @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中全部代碼:
Java代碼
- 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");
- }
- }
如果大家想美化搖桿,那就讓你們的美工給出兩張圓形圖吧。
還在等什麼?立刻為你的游戲加上搖桿吧!