|
41016Android游戲開發(fā)之小球重力感應(yīng)實(shí)現(xiàn)
重力感應(yīng)主要是依靠手機(jī)的加速度傳感器(accelerometer)來實(shí)現(xiàn)
在Android的開發(fā)中一共有八種傳感器但是不一定每一款真機(jī)都支持這些傳感器。因?yàn)楹芏喙δ苡脩舾静籧are的所以可能開發(fā)商會(huì)把某些功能屏蔽掉。還是得根據(jù)真機(jī)的實(shí)際情況來做開發(fā),今天我們主要來討論加速度傳感器的具體實(shí)現(xiàn)方式。
傳感器名稱如下:
加速度傳感器(accelerometer)
陀螺儀傳感器(gyroscope)
環(huán)境光照傳感器(light)
磁力傳感器(magnetic field)
方向傳感器(orientation)
壓力傳感器(pressure)
距離傳感器(proximity)
溫度傳感器(temperature)
1.SensorMannager傳感器管理對(duì)象
手機(jī)中的所有傳感器都須要通過SensorMannager來訪問,調(diào)用getSystemService(SENSOR_SERVICE)方法就可以拿到當(dāng)前手機(jī)的傳感器管理對(duì)象。 - SensorManager mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
復(fù)制代碼 2.實(shí)現(xiàn)SensorEventListener接口
說道SensorEventListener接口就不得不說SensorListener接口。在Android1.5一下是通過實(shí)現(xiàn)SensorListener接口來捕獲 手機(jī)傳感器狀態(tài),但是在1.5以上如果實(shí)現(xiàn)這個(gè)接口系統(tǒng)會(huì)提示你這行代碼已經(jīng)過期。今天我們不討論SensorListener因?yàn)樗呀?jīng)是過時(shí)的東西了。主要討論一下SensorEventListener接口。我們須要實(shí)現(xiàn)SensorEventListener這個(gè)接口 onSensorChanged(SensorEvent event)方法來捕獲手機(jī)傳感器的狀態(tài),拿到手機(jī) X軸Y軸Z軸三個(gè)方向的重力分量,有了這三個(gè)方向的數(shù)據(jù)重力感應(yīng)的原理我們就已經(jīng)學(xué)會(huì)了,簡(jiǎn)單吧 - public void onSensorChanged(SensorEvent e) {
float x = e.values[SensorManager.DATA_X];
float y = e.values[SensorManager.DATA_Y];
float z = e.values[SensorManager.DATA_Z];
}
復(fù)制代碼 如圖所示:上例代碼中 float x y z 3個(gè)方向的取值范圍是在 -10 到 10 之間,我向同學(xué)們說明一下 X軸 Y軸 Z軸 重力分量的含義。 這里須要注意的是坐標(biāo)原點(diǎn) 向天空為正數(shù) 向地面為負(fù)數(shù) 剛好與編程時(shí)坐標(biāo)是相反的。
手機(jī)屏幕向左側(cè)方當(dāng)X軸就朝向天空,垂直放置 這時(shí)候 Y 軸 與 Z軸沒有重力分量,因?yàn)閄軸朝向天空所以它的重力分量則最大 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(10,0,0)
手機(jī)屏幕向右側(cè)方當(dāng)X軸就朝向地面,垂直放置 這時(shí)候 Y 軸 與 Z軸沒有重力分量,因?yàn)閄軸朝向地面所以它的重力分量則最小 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(-10,0,0)
手機(jī)屏幕垂直豎立放置方當(dāng)Y軸就朝向天空,垂直放置 這時(shí)候 X 軸 與 Z軸沒有重力分量,因?yàn)閅軸朝向天空所以它的重力分量則最大 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(0,10,0)
手機(jī)屏幕垂直豎立放置方當(dāng)Y軸就朝向地面,垂直放置 這時(shí)候 X 軸 與 Z軸沒有重力分量,因?yàn)閅軸朝向地面所以它的重力分量則最小 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(0,-10,0)
手機(jī)屏幕向上當(dāng)Z軸就朝向天空,水平放置 這時(shí)候 X 軸與Y軸沒有重力分量,因?yàn)閆軸朝向天空所以它的重力分量則最大 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(0,0,10)
手機(jī)屏幕向上當(dāng)Z軸就朝向地面,水平放置 這時(shí)候 X 軸與Y軸沒有重力分量,因?yàn)閆軸朝向地面所以它的重力分量則最小 。這時(shí)候X軸 Y軸 Z軸的重力分量的值分別為(0,0,-10)
因?yàn)檫@張圖片是在模擬器上截得,所以沒有重力感應(yīng)它的三個(gè)方向的的重力分量都為0。
3.注冊(cè)SensorEventListener
使用SensorMannager調(diào)用getDefaultSensor(Sensor.TYPE_ACCELEROMETER)方法拿到加速重力感應(yīng)的Sensor對(duì)象。因?yàn)楸菊挛覀冇懻撝亓铀俣葌鞲衅魉詤?shù)為Sensor.TYPE_ACCELEROMETER,如果須要拿到其它的傳感器須要傳入對(duì)應(yīng)的名稱。使用SensorMannager調(diào)用registerListener()方法來注冊(cè),第三個(gè)參數(shù)是檢測(cè)的靈敏精確度根據(jù)不同的需求來選擇精準(zhǔn)度,游戲開發(fā)建議使用 SensorManager.SENSOR_DELAY_GAME。 - mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 注冊(cè)listener,第三個(gè)參數(shù)是檢測(cè)的精確度
//SENSOR_DELAY_FASTEST 最靈敏 因?yàn)樘炝藳]必要使用
//SENSOR_DELAY_GAME 游戲開發(fā)中使用
//SENSOR_DELAY_NORMAL 正常速度
//SENSOR_DELAY_UI 最慢的速度
mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
復(fù)制代碼 重力感應(yīng)簡(jiǎn)單速度計(jì)算的方式。 每次搖晃手機(jī)計(jì)算出 X軸 Y軸 Z軸的重力分量可以將它們記錄下來 然后每次搖晃的重力分量和之前的重力分量可以做一個(gè)對(duì)比 利用差值和時(shí)間就可以計(jì)算出他們的移動(dòng)速度。(下面這段代碼是我之前的博文中摘錄過來的,因?yàn)槟瞧獙懙牟皇呛芎盟栽谶@一篇中我詳細(xì)總結(jié)一下) - private SensorManager sensorMgr;
Sensor sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//保存上一次 x y z 的坐標(biāo)
float bx = 0;
float by = 0;
float bz = 0;
long btime = 0;//這一次的時(shí)間
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
SensorEventListener lsn = new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
float x = e.values[SensorManager.DATA_X];
float y = e.values[SensorManager.DATA_Y];
float z = e.values[SensorManager.DATA_Z];
//在這里我們可以計(jì)算出 X Y Z的數(shù)值 下面我們就可以根據(jù)這個(gè)數(shù)值來計(jì)算搖晃的速度了
//我想大家應(yīng)該都知道計(jì)算速度的公事 速度 = 路程/時(shí)間
//X軸的速度
float speadX = (x - bx) / (System.currentTimeMillis() - btime);
//y軸的速度
float speadY = (y - by) / (System.currentTimeMillis() - btime);
//z軸的速度
float speadZ = (z - bz) / (System.currentTimeMillis() - btime);
//這樣簡(jiǎn)單的速度就可以計(jì)算出來 如果你想計(jì)算加速度 也可以 在運(yùn)動(dòng)學(xué)里,加速度a與速度,
//位移都有關(guān)系:Vt=V0+at,S=V0*t+1/2at^2, S=(Vt^2-V0^2)/(2a),根據(jù)這些信息也可以求解a。
//這里就不詳細(xì)介紹了 公事 應(yīng)該初中物理課老師就教了呵呵~~
bx = x;
by = y;
bz = z;
btime = System.currentTimeMillis();
}
public void onAccuracyChanged(Sensor s, int accuracy) {
}
};
// 注冊(cè)listener,第三個(gè)參數(shù)是檢測(cè)的精確度
sensorMgr.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);
復(fù)制代碼 真機(jī)上的效果圖
下面給出這個(gè)DEMO小球重力感應(yīng)的完整代碼 - import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
import android.view.SurfaceHolder.Callback;
public class SurfaceViewAcitvity extends Activity {
MyView mAnimView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全屏顯示窗口
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//強(qiáng)制橫屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 顯示自定義的游戲View
mAnimView = new MyView(this);
setContentView(mAnimView);
}
public class MyView extends SurfaceView implements Callback,Runnable ,SensorEventListener{
/**每50幀刷新一次屏幕**/
public static final int TIME_IN_FRAME = 50;
/** 游戲畫筆 **/
Paint mPaint = null;
Paint mTextPaint = null;
SurfaceHolder mSurfaceHolder = null;
/** 控制游戲更新循環(huán) **/
boolean mRunning = false;
/** 游戲畫布 **/
Canvas mCanvas = null;
/**控制游戲循環(huán)**/
boolean mIsRunning = false;
/**SensorManager管理器**/
private SensorManager mSensorMgr = null;
Sensor mSensor = null;
/**手機(jī)屏幕寬高**/
int mScreenWidth = 0;
int mScreenHeight = 0;
/**小球資源文件越界區(qū)域**/
private int mScreenBallWidth = 0;
private int mScreenBallHeight = 0;
/**游戲背景文件**/
private Bitmap mbitmapBg;
/**小球資源文件**/
private Bitmap mbitmapBall;
/**小球的坐標(biāo)位置**/
private float mPosX = 200;
private float mPosY = 0;
/**重力感應(yīng)X軸 Y軸 Z軸的重力值**/
private float mGX = 0;
private float mGY = 0;
private float mGZ = 0;
public MyView(Context context) {
super(context);
/** 設(shè)置當(dāng)前View擁有控制焦點(diǎn) **/
this.setFocusable(true);
/** 設(shè)置當(dāng)前View擁有觸摸事件 **/
this.setFocusableInTouchMode(true);
/** 拿到SurfaceHolder對(duì)象 **/
mSurfaceHolder = this.getHolder();
/** 將mSurfaceHolder添加到Callback回調(diào)函數(shù)中 **/
mSurfaceHolder.addCallback(this);
/** 創(chuàng)建畫布 **/
mCanvas = new Canvas();
/** 創(chuàng)建曲線畫筆 **/
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
/**加載小球資源**/
mbitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
/**加載游戲背景**/
mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
/**得到SensorManager對(duì)象**/
mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 注冊(cè)listener,第三個(gè)參數(shù)是檢測(cè)的精確度
//SENSOR_DELAY_FASTEST 最靈敏 因?yàn)樘炝藳]必要使用
//SENSOR_DELAY_GAME 游戲開發(fā)中使用
//SENSOR_DELAY_NORMAL 正常速度
//SENSOR_DELAY_UI 最慢的速度
mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
private void Draw() {
/**繪制游戲背景**/
mCanvas.drawBitmap(mbitmapBg,0,0, mPaint);
/**繪制小球**/
mCanvas.drawBitmap(mbitmapBall, mPosX,mPosY, mPaint);
/**X軸 Y軸 Z軸的重力值**/
mCanvas.drawText("X軸重力值 :" + mGX, 0, 20, mPaint);
mCanvas.drawText("Y軸重力值 :" + mGY, 0, 40, mPaint);
mCanvas.drawText("Z軸重力值 :" + mGZ, 0, 60, mPaint);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
/**開始游戲主循環(huán)線程**/
mIsRunning = true;
new Thread(this).start();
/**得到當(dāng)前屏幕寬高**/
mScreenWidth = this.getWidth();
mScreenHeight = this.getHeight();
/**得到小球越界區(qū)域**/
mScreenBallWidth = mScreenWidth - mbitmapBall.getWidth();
mScreenBallHeight = mScreenHeight - mbitmapBall.getHeight();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsRunning = false;
}
@Override
public void run() {
while (mIsRunning) {
/** 取得更新游戲之前的時(shí)間 **/
long startTime = System.currentTimeMillis();
/** 在這里加上線程安全鎖 **/
synchronized (mSurfaceHolder) {
/** 拿到當(dāng)前畫布 然后鎖定 **/
mCanvas = mSurfaceHolder.lockCanvas();
Draw();
/** 繪制結(jié)束后解鎖顯示在屏幕上 **/
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
/** 取得更新游戲結(jié)束的時(shí)間 **/
long endTime = System.currentTimeMillis();
/** 計(jì)算出游戲一次更新的毫秒數(shù) **/
int diffTime = (int) (endTime - startTime);
/** 確保每次更新時(shí)間為50幀 **/
while (diffTime <= TIME_IN_FRAME) {
diffTime = (int) (System.currentTimeMillis() - startTime);
/** 線程等待 **/
Thread.yield();
}
}
}
@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub
}
@Override
public void onSensorChanged(SensorEvent event) {
mGX = event.values[SensorManager.DATA_X];
mGY= event.values[SensorManager.DATA_Y];
mGZ = event.values[SensorManager.DATA_Z];
//這里乘以2是為了讓小球移動(dòng)的更快
mPosX -= mGX * 2;
mPosY += mGY * 2;
//檢測(cè)小球是否超出邊界
if (mPosX < 0) {
mPosX = 0;
} else if (mPosX > mScreenBallWidth) {
mPosX = mScreenBallWidth;
}
if (mPosY < 0) {
mPosY = 0;
} else if (mPosY > mScreenBallHeight) {
mPosY = mScreenBallHeight;
}
}
}
}
復(fù)制代碼 老規(guī)矩每篇文章都會(huì)附帶源代碼,最后如果你還是覺得我寫的不夠詳細(xì) 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學(xué)習(xí)
第十五講重力感應(yīng).rar(1.65 MB, 下載次數(shù): 2300)[/I]2011-9-3 01:00 上傳點(diǎn)擊文件名 下載積分: 下載豆 -2 |
上一篇: 第十六講:菜單 Android Menu下一篇: 第十四講:Service入門指南
|