天天看點

Android 小球重力感應實作

重力感應主要是依靠手機的加速度傳感器(accelerometer)來實作

       在Android的開發中一共有八種傳感器但是不一定每一款真機都支援這些傳感器。因為很多功能使用者根本不care的是以可能開發商會把某些功能屏蔽掉。還是得根據真機的實際情況來做開發,今天我們主要來讨論加速度傳感器的具體實作方式。

       傳感器名稱如下:

       加速度傳感器(accelerometer)

       陀螺儀傳感器(gyroscope)

       環境光照傳感器(light)

       磁力傳感器(magnetic field)

       方向傳感器(orientation)

       壓力傳感器(pressure)

       距離傳感器(proximity)

       溫度傳感器(temperature)

       1.SensorMannager傳感器管理對象

       手機中的所有傳感器都須要通過SensorMannager來通路,調用getSystemService(SENSOR_SERVICE)方法就可以拿到目前手機的傳感器管理對象。

java代碼:

1. SensorManager mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);      

複制代碼

2.實作SensorEventListener接口

       說道SensorEventListener接口就不得不說SensorListener接口。在Android1.5一下是通過實作SensorListener接口來捕獲 手機傳感器狀态,但是在1.5以上如果實作這個接口系統會提示你這行代碼已經過期。今天我們不讨論SensorListener因為它已經是過時的東西了。主要讨論一下SensorEventListener接口。我們須要實作SensorEventListener這個接口 onSensorChanged(SensorEvent event)方法來捕獲手機傳感器的狀态,拿到手機 X軸Y軸Z軸三個方向的重力分量,有了這三個方向的資料重力感應的原理我們就已經學會了。

java代碼:

1. public void onSensorChanged(SensorEvent e) {
2. float x = e.values[SensorManager.DATA_X];
3. float y = e.values[SensorManager.DATA_Y];
4. float z = e.values[SensorManager.DATA_Z];
5. }      

複制代碼

       手機螢幕向左側方當X軸就朝向天空,垂直放置 這時候 Y 軸 與 Z軸沒有重力分量,因為X軸朝向天空是以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分别為(10,0,0)

       手機螢幕向右側方當X軸就朝向地面,垂直放置 這時候 Y 軸 與 Z軸沒有重力分量,因為X軸朝向地面是以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分别為(-10,0,0)

       手機螢幕垂直豎立放置方當Y軸就朝向天空,垂直放置 這時候 X 軸 與 Z軸沒有重力分量,因為Y軸朝向天空是以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分别為(0,10,0)

       手機螢幕垂直豎立放置方當Y軸就朝向地面,垂直放置 這時候 X 軸 與 Z軸沒有重力分量,因為Y軸朝向地面是以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分别為(0,-10,0)

       手機螢幕向上當Z軸就朝向天空,水準放置 這時候 X 軸與Y軸沒有重力分量,因為Z軸朝向天空是以它的重力分量則最大 。這時候X軸 Y軸 Z軸的重力分量的值分别為(0,0,10)

       手機螢幕向上當Z軸就朝向地面,水準放置 這時候 X 軸與Y軸沒有重力分量,因為Z軸朝向地面是以它的重力分量則最小 。這時候X軸 Y軸 Z軸的重力分量的值分别為(0,0,-10)

       因為這張圖檔是在模拟器上截得,是以沒有重力感應它的三個方向的的重力分量都為0。

3.注冊SensorEventListener

       使用SensorMannager調用getDefaultSensor(Sensor.TYPE_ACCELEROMETER)方法拿到加速重力感應的Sensor對象。因為本章我們讨論重力加速度傳感器是以參數為Sensor.TYPE_ACCELEROMETER,如果須要拿到其它的傳感器須要傳入對應的名稱。使用SensorMannager調用registerListener()方法來注冊,第三個參數是檢測的靈敏精确度根據不同的需求來選擇精準度,遊戲開發建議使用  SensorManager.SENSOR_DELAY_GAME。

java代碼:

1. mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
2. mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
3. // 注冊listener,第三個參數是檢測的精确度
4. //SENSOR_DELAY_FASTEST 最靈敏 因為太快了沒必要使用
5. //SENSOR_DELAY_GAME 遊戲開發中使用
6. //SENSOR_DELAY_NORMAL 正常速度
7. //SENSOR_DELAY_UI 最慢的速度
8. mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);      

複制代碼

       重力感應簡單速度計算的方式。 每次搖晃手機計算出 X軸 Y軸 Z軸的重力分量可以将它們記錄下來 然後每次搖晃的重力分量和之前的重力分量可以做一個對比 利用內插補點和時間就可以計算出他們的移動速度。

java代碼:

1. private SensorManager sensorMgr;
2. Sensor sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
3. //儲存上一次 x y z 的坐标
4. float bx = 0;
5. float by = 0;
6. float bz = 0;
7. long btime = 0;//這一次的時間
8. sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
9. SensorEventListener lsn = new SensorEventListener() {
10. public void onSensorChanged(SensorEvent e) {
11. float x = e.values[SensorManager.DATA_X];
12. float y = e.values[SensorManager.DATA_Y];
13. float z = e.values[SensorManager.DATA_Z];
14. //在這裡我們可以計算出 X Y Z的數值 下面我們就可以根據這個數值來計算搖晃的速度了
15. //我想大家應該都知道計算速度的公事 速度 = 路程/時間
16. //X軸的速度
17. float speadX = (x - bx) / (System.currentTimeMillis() - btime);
18. //y軸的速度
19. float speadY = (y - by) / (System.currentTimeMillis() - btime);
20. //z軸的速度
21. float speadZ = (z - bz) / (System.currentTimeMillis() - btime);
22. //這樣簡單的速度就可以計算出來 如果你想計算加速度 也可以 在運動學裡,加速度a與速度,
23. //位移都有關系:Vt=V0+at,S=V0*t+1/2at^2, S=(Vt^2-V0^2)/(2a),根據這些資訊也可以求解a。
24. //這裡就不詳細介紹了 公事 應該國中實體課老師就教了呵呵~~
25. bx = x;
26. by = y;
27. bz = z;
28. btime = System.currentTimeMillis();
29. }
30. public void onAccuracyChanged(Sensor s, int accuracy) {
31. }
32. };
33. // 注冊listener,第三個參數是檢測的精确度
34. sensorMgr.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);      
java代碼:


1. import android.app.Activity;
2. import android.content.Context;
3. import android.content.pm.ActivityInfo;
4. import android.graphics.Bitmap;
5. import android.graphics.BitmapFactory;
6. import android.graphics.Canvas;
7. import android.graphics.Color;
8. import android.graphics.Paint;
9. import android.hardware.Sensor;
10. import android.hardware.SensorEvent;
11. import android.hardware.SensorEventListener;
12. import android.hardware.SensorManager;
13. import android.os.Bundle;
14. import android.view.SurfaceHolder;
15. import android.view.SurfaceView;
16. import android.view.Window;
17. import android.view.WindowManager;
18. import android.view.SurfaceHolder.Callback;
19. 
20. 
21. public class SurfaceViewAcitvity extends Activity {
22. MyView mAnimView = null;
23. 
24. 
25. @Override
26. public void onCreate(Bundle savedInstanceState) {
27. super.onCreate(savedInstanceState);
28. // 全屏顯示視窗
29. requestWindowFeature(Window.FEATURE_NO_TITLE);
30. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
31. WindowManager.LayoutParams.FLAG_FULLSCREEN);
32. //強制橫屏
33. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
34. // 顯示自定義的遊戲View
35. mAnimView = new MyView(this);
36. setContentView(mAnimView);
37. }
38. public class MyView extends SurfaceView implements Callback,Runnable ,SensorEventListener{
39. /**每50幀重新整理一次螢幕**/
40. public static final int TIME_IN_FRAME = 50;
41. /** 遊戲畫筆 **/
42. Paint mPaint = null;
43. Paint mTextPaint = null;
44. SurfaceHolder mSurfaceHolder = null;
45. /** 控制遊戲更新循環 **/
46. boolean mRunning = false;
47. /** 遊戲畫布 **/
48. Canvas mCanvas = null;
49. /**控制遊戲循環**/
50. boolean mIsRunning = false;
51. /**SensorManager管理器**/
52. private SensorManager mSensorMgr = null;
53. Sensor mSensor = null;
54. /**手機螢幕寬高**/
55. int mScreenWidth = 0;
56. int mScreenHeight = 0;
57. /**小球資源檔案越界區域**/
58. private int mScreenBallWidth = 0;
59. private int mScreenBallHeight = 0;
60. /**遊戲背景檔案**/
61. private Bitmap mbitmapBg;
62. /**小球資源檔案**/
63. private Bitmap mbitmapBall;
64. /**小球的坐标位置**/
65. private float mPosX = 200;
66. private float mPosY = 0;
67. /**重力感應X軸 Y軸 Z軸的重力值**/
68. private float mGX = 0;
69. private float mGY = 0;
70. private float mGZ = 0;
71. public MyView(Context context) {
72. super(context);
73. /** 設定目前View擁有控制焦點 **/
74. this.setFocusable(true);
75. /** 設定目前View擁有觸摸事件 **/
76. this.setFocusableInTouchMode(true);
77. /** 拿到SurfaceHolder對象 **/
78. mSurfaceHolder = this.getHolder();
79. /** 将mSurfaceHolder添加到Callback回調函數中 **/
80. mSurfaceHolder.addCallback(this);
81. /** 建立畫布 **/
82. mCanvas = new Canvas();
83. /** 建立曲線畫筆 **/
84. mPaint = new Paint();
85. mPaint.setColor(Color.WHITE);
86. /**加載小球資源**/
87. mbitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
88. /**加載遊戲背景**/
89. mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
90. /**得到SensorManager對象**/
91. mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
92. mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
93. // 注冊listener,第三個參數是檢測的精确度
94. //SENSOR_DELAY_FASTEST 最靈敏 因為太快了沒必要使用
95. //SENSOR_DELAY_GAME 遊戲開發中使用
96. //SENSOR_DELAY_NORMAL 正常速度
97. //SENSOR_DELAY_UI 最慢的速度
98. mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
99. }
100. 
101. 
102. private void Draw() {
103. /**繪制遊戲背景**/
104. mCanvas.drawBitmap(mbitmapBg,0,0, mPaint);
105. /**繪制小球**/
106. mCanvas.drawBitmap(mbitmapBall, mPosX,mPosY, mPaint);
107. /**X軸 Y軸 Z軸的重力值**/
108. mCanvas.drawText("X軸重力值 :" + mGX, 0, 20, mPaint);
109. mCanvas.drawText("Y軸重力值 :" + mGY, 0, 40, mPaint);
110. mCanvas.drawText("Z軸重力值 :" + mGZ, 0, 60, mPaint);
111. }
112. 
113. 
114. @Override
115. public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
116. }
117. 
118. 
119. @Override
120. public void surfaceCreated(SurfaceHolder holder) {
121. /**開始遊戲主循環線程**/
122. mIsRunning = true;
123. new Thread(this).start();
124. /**得到目前螢幕寬高**/
125. mScreenWidth = this.getWidth();
126. mScreenHeight = this.getHeight();
127. /**得到小球越界區域**/
128. mScreenBallWidth = mScreenWidth - mbitmapBall.getWidth();
129. mScreenBallHeight = mScreenHeight - mbitmapBall.getHeight();
130. }
131. @Override
132. public void surfaceDestroyed(SurfaceHolder holder) {
133. mIsRunning = false;
134. }
135. @Override
136. public void run() {
137. while (mIsRunning) {
138. /** 取得更新遊戲之前的時間 **/
139. long startTime = System.currentTimeMillis();
140. /** 在這裡加上線程安全鎖 **/
141. synchronized (mSurfaceHolder) {
142. /** 拿到目前畫布 然後鎖定 **/
143. mCanvas = mSurfaceHolder.lockCanvas();
144. Draw();
145. /** 繪制結束後解鎖顯示在螢幕上 **/
146. mSurfaceHolder.unlockCanvasAndPost(mCanvas);
147. }
148. /** 取得更新遊戲結束的時間 **/
149. long endTime = System.currentTimeMillis();
150. /** 計算出遊戲一次更新的毫秒數 **/
151. int diffTime = (int) (endTime - startTime);
152. /** 確定每次更新時間為50幀 **/
153. while (diffTime <= TIME_IN_FRAME) {
154. diffTime = (int) (System.currentTimeMillis() - startTime);
155. /** 線程等待 **/
156. Thread.yield();
157. }
158. }
159. }
160. 
161. 
162. @Override
163. public void onAccuracyChanged(Sensor arg0, int arg1) {
164. // TODO Auto-generated method stub
165. }
166. @Override
167. public void onSensorChanged(SensorEvent event) {
168. mGX = event.values[SensorManager.DATA_X];
169. mGY= event.values[SensorManager.DATA_Y];
170. mGZ = event.values[SensorManager.DATA_Z];
171. //這裡乘以2是為了讓小球移動的更快
172. mPosX -= mGX * 2;
173. mPosY += mGY * 2;
174. //檢測小球是否超出邊界
175. if (mPosX < 0) {
176. mPosX = 0;
177. } else if (mPosX > mScreenBallWidth) {
178. mPosX = mScreenBallWidth;
179. }
180. if (mPosY < 0) {
181. mPosY = 0;
182. } else if (mPosY > mScreenBallHeight) {
183. mPosY = mScreenBallHeight;
184. }
185. }
186. }
187. }