您的位置:首页 > 移动开发 > Android开发

Android游戏开发之小球重力感应实现

2012-03-20 15:06 676 查看
注明:转载自雨松MOMO的博客原文地址:http://blog.csdn.net/xys289187120/article/details/6702953



重力感应主要是依靠手机的加速度传感器(accelerometer)来实现

在Android的开发中一共有八种传感器但是不一定每一款真机都支持这些传感器。因为很多功能用户根本不care的所以可能开发商会把某些功能屏蔽掉。还是得根据真机的实际情况来做开发,今天我们主要来讨论加速度传感器的具体实现方式。

传感器名称如下:

加速度传感器(accelerometer)

陀螺仪传感器(gyroscope)

环境光照传感器(light)

磁力传感器(magnetic field)

方向传感器(orientation)

压力传感器(pressure)

距离传感器(proximity)

温度传感器(temperature)

1.SensorMannager传感器管理对象

手机中的所有传感器都须要通过SensorMannager来访问,调用getSystemService(SENSOR_SERVICE)方法就可以拿到当前手机的传感器管理对象。

[java]
view plaincopyprint?

SensorManager mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

[java]
view plaincopyprint?

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];

}

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];
}


如图所示:上例代码中 float x y z 3个方向的取值范围是在 -10 到 10 之间,我向同学们说明一下 X轴 Y轴 Z轴 重力分量的含义。 这里须要注意的是坐标原点 向天空为正数 向地面为负数 刚好与编程时坐标是相反的。

手机屏幕向左侧方当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]
view plaincopyprint?

mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

// 注册listener,第三个参数是检测的精确度

//SENSOR_DELAY_FASTEST 最灵敏 因为太快了没必要使用

//SENSOR_DELAY_GAME 游戏开发中使用

//SENSOR_DELAY_NORMAL 正常速度

//SENSOR_DELAY_UI 最慢的速度

mSensorMgr.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);

[java]
view plaincopyprint?

private SensorManager sensorMgr;

Sensor sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

//保存上一次 x y z 的坐标

float bx = 0;

float by = 0;

float bz = 0;

long btime = 0;//这一次的时间

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];

//在这里我们可以计算出 X Y Z的数值 下面我们就可以根据这个数值来计算摇晃的速度了

//我想大家应该都知道计算速度的公事 速度 = 路程/时间

//X轴的速度

float speadX = (x - bx) / (System.currentTimeMillis() - btime);

//y轴的速度

float speadY = (y - by) / (System.currentTimeMillis() - btime);

//z轴的速度

float speadZ = (z - bz) / (System.currentTimeMillis() - btime);

//这样简单的速度就可以计算出来 如果你想计算加速度 也可以 在运动学里,加速度a与速度,

//位移都有关系:Vt=V0+at,S=V0*t+1/2at^2, S=(Vt^2-V0^2)/(2a),根据这些信息也可以求解a。

//这里就不详细介绍了 公事 应该初中物理课老师就教了呵呵~~

bx = x;
by = y;
bz = z;

btime = System.currentTimeMillis();

}

public void onAccuracyChanged(Sensor s, int accuracy) {

}
};
// 注册listener,第三个参数是检测的精确度

sensorMgr.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);

private SensorManager sensorMgr;
Sensor sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

//保存上一次 x y z 的坐标
float bx = 0;
float by = 0;
float bz = 0;
long btime = 0;//这一次的时间
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];
//在这里我们可以计算出 X Y Z的数值 下面我们就可以根据这个数值来计算摇晃的速度了
//我想大家应该都知道计算速度的公事 速度 = 路程/时间
//X轴的速度
float speadX = (x - bx) / (System.currentTimeMillis() - btime);
//y轴的速度
float speadY = (y - by) / (System.currentTimeMillis() - btime);
//z轴的速度
float speadZ = (z - bz) / (System.currentTimeMillis() - btime);
//这样简单的速度就可以计算出来 如果你想计算加速度 也可以 在运动学里,加速度a与速度,
//位移都有关系:Vt=V0+at,S=V0*t+1/2at^2, S=(Vt^2-V0^2)/(2a),根据这些信息也可以求解a。
//这里就不详细介绍了 公事 应该初中物理课老师就教了呵呵~~

bx = x;
by = y;
bz = z;

btime = System.currentTimeMillis();

}

public void onAccuracyChanged(Sensor s, int accuracy) {
}
};
// 注册listener,第三个参数是检测的精确度
sensorMgr.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);


真机上的效果图



下面给出这个DEMO小球重力感应的完整代码

[java]
view plaincopyprint?

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);

//强制横屏
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;

/** 控制游戏更新循环 **/

boolean mRunning = false;

/** 游戏画布 **/

Canvas mCanvas = null;

/**控制游戏循环**/
boolean mIsRunning = false;

/**SensorManager管理器**/

private SensorManager mSensorMgr = null;

Sensor mSensor = null;

/**手机屏幕宽高**/

int mScreenWidth = 0;

int mScreenHeight = 0;

/**小球资源文件越界区域**/

private int mScreenBallWidth = 0;

private int mScreenBallHeight = 0;

/**游戏背景文件**/

private Bitmap mbitmapBg;

/**小球资源文件**/
private Bitmap mbitmapBall;

/**小球的坐标位置**/

private float mPosX = 200;

private float mPosY = 0;

/**重力感应X轴 Y轴 Z轴的重力值**/

private float mGX = 0;

private float mGY = 0;

private float mGZ = 0;

public MyView(Context context) {

super(context);

/** 设置当前View拥有控制焦点 **/

this.setFocusable(true);

/** 设置当前View拥有触摸事件 **/

this.setFocusableInTouchMode(true);

/** 拿到SurfaceHolder对象 **/

mSurfaceHolder = this.getHolder();

/** 将mSurfaceHolder添加到Callback回调函数中 **/

mSurfaceHolder.addCallback(this);

/** 创建画布 **/

mCanvas = new Canvas();

/** 创建曲线画笔 **/

mPaint = new Paint();

mPaint.setColor(Color.WHITE);
/**加载小球资源**/

mbitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);

/**加载游戏背景**/

mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);

/**得到SensorManager对象**/

mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

// 注册listener,第三个参数是检测的精确度

//SENSOR_DELAY_FASTEST 最灵敏 因为太快了没必要使用

//SENSOR_DELAY_GAME 游戏开发中使用

//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) {

/**开始游戏主循环线程**/

mIsRunning = true;

new Thread(this).start();

/**得到当前屏幕宽高**/

mScreenWidth = this.getWidth();

mScreenHeight = this.getHeight();

/**得到小球越界区域**/

mScreenBallWidth = mScreenWidth - mbitmapBall.getWidth();

mScreenBallHeight = mScreenHeight - mbitmapBall.getHeight();

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

mIsRunning = false;

}

@Override
public void run() {

while (mIsRunning) {

/** 取得更新游戏之前的时间 **/

long startTime = System.currentTimeMillis();

/** 在这里加上线程安全锁 **/

synchronized (mSurfaceHolder) {

/** 拿到当前画布 然后锁定 **/

mCanvas = mSurfaceHolder.lockCanvas();
Draw();
/** 绘制结束后解锁显示在屏幕上 **/

mSurfaceHolder.unlockCanvasAndPost(mCanvas);

}

/** 取得更新游戏结束的时间 **/

long endTime = System.currentTimeMillis();

/** 计算出游戏一次更新的毫秒数 **/

int diffTime = (int) (endTime - startTime);

/** 确保每次更新时间为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是为了让小球移动的更快

mPosX -= mGX * 2;

mPosY += mGY * 2;

//检测小球是否超出边界

if (mPosX < 0) {

mPosX = 0;

} else if (mPosX > mScreenBallWidth) {

mPosX = mScreenBallWidth;
}
if (mPosY < 0) {

mPosY = 0;
} else if (mPosY > mScreenBallHeight) {

mPosY = mScreenBallHeight;
}
}
}
}

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);
//强制横屏
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;

/** 控制游戏更新循环 **/
boolean mRunning = false;

/** 游戏画布 **/
Canvas mCanvas = null;

/**控制游戏循环**/
boolean mIsRunning = false;

/**SensorManager管理器**/
private SensorManager mSensorMgr = null;
Sensor mSensor = null;

/**手机屏幕宽高**/
int mScreenWidth = 0;
int mScreenHeight = 0;

/**小球资源文件越界区域**/
private int mScreenBallWidth = 0;
private int mScreenBallHeight = 0;

/**游戏背景文件**/
private Bitmap mbitmapBg;

/**小球资源文件**/
private Bitmap mbitmapBall;

/**小球的坐标位置**/
private float mPosX = 200;
private float mPosY = 0;

/**重力感应X轴 Y轴 Z轴的重力值**/
private float mGX = 0;
private float mGY = 0;
private float mGZ = 0;

public MyView(Context context) {
super(context);
/** 设置当前View拥有控制焦点 **/
this.setFocusable(true);
/** 设置当前View拥有触摸事件 **/
this.setFocusableInTouchMode(true);
/** 拿到SurfaceHolder对象 **/
mSurfaceHolder = this.getHolder();
/** 将mSurfaceHolder添加到Callback回调函数中 **/
mSurfaceHolder.addCallback(this);
/** 创建画布 **/
mCanvas = new Canvas();
/** 创建曲线画笔 **/
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
/**加载小球资源**/
mbitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
/**加载游戏背景**/
mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);

/**得到SensorManager对象**/
mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 注册listener,第三个参数是检测的精确度
//SENSOR_DELAY_FASTEST 最灵敏 因为太快了没必要使用
//SENSOR_DELAY_GAME 游戏开发中使用
//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) {
/**开始游戏主循环线程**/
mIsRunning = true;
new Thread(this).start();
/**得到当前屏幕宽高**/
mScreenWidth = this.getWidth();
mScreenHeight = this.getHeight();

/**得到小球越界区域**/
mScreenBallWidth = mScreenWidth - mbitmapBall.getWidth();
mScreenBallHeight = mScreenHeight - mbitmapBall.getHeight();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsRunning = false;
}

@Override
public void run() {
while (mIsRunning) {

/** 取得更新游戏之前的时间 **/
long startTime = System.currentTimeMillis();

/** 在这里加上线程安全锁 **/
synchronized (mSurfaceHolder) {
/** 拿到当前画布 然后锁定 **/
mCanvas = mSurfaceHolder.lockCanvas();
Draw();
/** 绘制结束后解锁显示在屏幕上 **/
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}

/** 取得更新游戏结束的时间 **/
long endTime = System.currentTimeMillis();

/** 计算出游戏一次更新的毫秒数 **/
int diffTime = (int) (endTime - startTime);

/** 确保每次更新时间为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是为了让小球移动的更快
mPosX -= mGX * 2;
mPosY += mGY * 2;

//检测小球是否超出边界
if (mPosX < 0) {
mPosX = 0;
} else if (mPosX > mScreenBallWidth) {
mPosX = mScreenBallWidth;
}
if (mPosY < 0) {
mPosY = 0;
} else if (mPosY > mScreenBallHeight) {
mPosY = mScreenBallHeight;
}
}
}
}

老规矩每篇文章都会附带源代码,最后如果你还是觉得我写的不够详细 看的不够爽 不要紧我把源代码的下载地址贴出来 欢迎大家一起讨论学习雨松MOMO希望可以和大家一起进步。



下载地址:http://download.csdn.net/source/3533207
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: