此章節為正在創作的遊戲開發書籍中的一部分,由於寫書的緣故很久沒有更新瞭,挺對不起大傢的;那麼今天放出書中的一部分,讓大傢先睹為快吧;
在Android系統的手機,有的根本沒有實體的上下左右導航按鍵,所以很多遊戲都會有利用Android手機都具有觸屏的特性,制作360度搖桿來取代遊戲方向鍵,這樣不僅能使界面UI變得很美觀,而且更加的方便操作;
下面先來看效果吧:
下面開始實現:
首先,肯定是繪制兩個圓形,無可置疑;圓心點重合,為瞭區分 ,所以設置瞭不同顏色;
灰色:固定不動的搖桿背景(也意味著搖桿的活動范圍);
紅色:搖桿;
然後考慮:紅色搖桿肯定跟隨手指觸屏的位置而移動,那麼這個很easy啦,隻要在觸屏事件中處理,將獲取的觸屏XY坐標賦值與搖桿XY坐標即可;這個沒問題;但是緊接著在思考一個問題:
一般情況下,我們不可能希望搖桿一直跟隨手指位置,所以需要一個搖桿的活動區域,也就如同上圖中的灰色區域,在灰色區域內搖桿可以隨著用戶的觸屏位置移動,但是一旦用戶觸屏位置在活動區域之外,搖桿就不應該跑出灰色區域;所以具體實現步驟如下:
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://up.aiwalls.com/2011/1115/20111115053515741.rar
Himi原創,轉載務必註明出處!
原文地址:http://blog.csdn.net/xiaominghimi/archive/2011/05/16/6423983.aspx