如何使用Android的加速感应器开发一个控制铁球滚动的游戏:
public class AccelerometerPlayActivity extends Activity {
private SimulationView mSimulationView; //游戏主显示View
private SensorManager mSensorManager; //感应器管理类
private PowerManager mPowerManager; //电源控制,比如防锁屏
private WindowManager mWindowManager;
private Display mDisplay;
private WakeLock mWakeLock;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); // 实例化感应器管理类
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay(); //为了获取屏幕的DPI级别
mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
.getName()); //处理屏幕防止锁屏
mSimulationView = new SimulationView(this);
setContentView(mSimulationView); //设置游戏View
}
@Override
protected void onResume() {
super.onResume();
mWakeLock.acquire(); //恢复时解除锁屏
mSimulationView.startSimulation();
}
@Override
protected void onPause() {
super.onPause();
mSimulationView.stopSimulation(); //Activity切出去时停止画面更新
mWakeLock.release();
}
class SimulationView extends View implements SensorEventListener {
private static final float sBallDiameter = 0.004f; //设置小球直径
private static final float sBallDiameter2 = sBallDiameter * sBallDiameter;
private static final float sFriction = 0.1f; //摩擦系数
private Sensor mAccelerometer;
private long mLastT;
private float mLastDeltaT;
private float mXDpi;
private float mYDpi;
private float mMetersToPixelsX;
private float mMetersToPixelsY;
private Bitmap mBitmap; //小球素材
private Bitmap mWood; //背景使用木头
private float mXOrigin;
private float mYOrigin;
private float mSensorX;
private float mSensorY;
private long mSensorTimeStamp;
private long mCpuTimeStamp;
private float mHorizontalBound;
private float mVerticalBound;
private final ParticleSystem mParticleSystem = new ParticleSystem();
class Particle {
private float mPosX;
private float mPosY;
private float mAccelX;
private float mAccelY;
private float mLastPosX;
private float mLastPosY;
private float mOneMinusFriction;
Particle() {
final float r = ((float) Math.random() – 0.5f) * 0.2f;
mOneMinusFriction = 1.0f – sFriction + r;
}
public void computePhysics(float sx, float sy, float dT, float dTC) {
final float m = 1000.0f; // mass of our virtual object
final float gx = -sx * m;
final float gy = -sy * m;
final float invm = 1.0f / m;
final float ax = gx * invm;
final float ay = gy * invm;
final float dTdT = dT * dT;
final float x = mPosX + mOneMinusFriction * dTC * (mPosX – mLastPosX) + mAccelX
* dTdT;
final float y = mPosY + mOneMinusFriction * dTC * (mPosY – mLastPosY) + mAccelY
* dTdT;
mLastPosX = mPosX;
mLastPosY = mPosY;
mPosX = x;
mPosY = y;
mAccelX = ax;
mAccelY = ay;
}
public void resolveCollisionWithBounds() {
final float xmax = mHorizontalBound;
final float ymax = mVerticalBound;
final float x = mPosX;
final float y = mPosY;
if (x > xmax) {
mPosX = xmax;
} else if (x < -xmax) {
mPosX = -xmax;
}
if (y > ymax) {
mPosY = ymax;
} else if (y < -ymax) {
mPosY = -ymax;
}
}
}
class ParticleSystem { //收集取样感应器数据来绘制小球位置
static final int NUM_PARTICLES = 15;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
ParticleSystem() {
for (int i = 0; i < mBalls.length; i++) {
mBalls[i] = new Particle();
}
}
private void updatePositions(float sx, float sy, long timestamp) {
final long t = timestamp;
if (mLastT != 0) {
final float dT = (float) (t – mLastT) * (1.0f / 1000000000.0f);
if (mLastDeltaT != 0) {
final float dTC = dT / mLastDeltaT;
final int count = mBalls.length;
for (int i = 0; i < count; i++) {
Particle ball = mBalls[i];
ball.computePhysics(sx, sy, dT, dTC);
}
}
mLastDeltaT = dT;
}
mLastT = t;
}
public void update(float sx, float sy, long now) {
updatePositions(sx, sy, now);
final int NUM_MAX_ITERATIONS = 10;
boolean more = true;
final int count = mBalls.length;
for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {
more = false;
for (int i = 0; i < count; i++) {
Particle curr = mBalls[i];
for (int j = i + 1; j < count; j++) {
Particle ball = mBalls[j];
float dx = ball.mPosX – curr.mPosX;
float dy = ball.mPosY – curr.mPosY;
float dd = dx * dx + dy * dy;
// Check for collisions
if (dd <= sBallDiameter2) {
dx += ((float) Math.random() – 0.5f) * 0.0001f;
dy += ((float) Math.random() – 0.5f) * 0.0001f;
dd = dx * dx + dy * dy;
// simulate the spring
final float d = (float) Math.sqrt(dd);
final float c = (0.5f * (sBallDiameter – d)) / d;
curr.mPosX -= dx * c;
curr.mPosY -= dy * c;
ball.mPosX += dx * c;
ball.mPosY += dy * c;
more = true;
}
}
curr.resolveCollisionWithBounds();
}
}
}
public int getParticleCount() {
return mBalls.length;
}
public float getPosX(int i) {
return mBalls[i].mPosX;
}
public float getPosY(int i) {
return mBalls[i].mPosY;
}
}
public void startSimulation() {
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
public void stopSimulation() { //暂停时主要是停掉感应器
mSensorManager.unregisterListener(this);
}
public SimulationView(Context context) {
super(context);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); //实例化加速感应器
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics); //分辨率获取
mXDpi = metrics.xdpi;
mYDpi = metrics.ydpi;
mMetersToPixelsX = mXDpi / 0.0254f;
mMetersToPixelsY = mYDpi / 0.0254f;
Bitmap ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
final int dstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
final int dstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
mBitmap = Bitmap.createScaledBitmap(ball, dstWidth, dstHeight, true); //根据屏幕分辨率来设置素材的显示缩放比例
Options opts = new Options();
opts.inDither = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
mWood = BitmapFactory.decodeResource(getResources(), R.drawable.wood, opts);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) { //当View有变化时,Android123提示比如说横竖屏切换
mXOrigin = (w – mBitmap.getWidth()) * 0.5f;
mYOrigin = (h – mBitmap.getHeight()) * 0.5f;
mHorizontalBound = ((w / mMetersToPixelsX – sBallDiameter) * 0.5f);
mVerticalBound = ((h / mMetersToPixelsY – sBallDiameter) * 0.5f);
}
@Override
public void onSensorChanged(SensorEvent event) { //感应器数据有变化时
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
mSensorX = event.values[0];
mSensorY = event.values[1];
break;
case Surface.ROTATION_90:
mSensorX = -event.values[1];
mSensorY = event.values[0];
break;
case Surface.ROTATION_180:
mSensorX = -event.values[0];
mSensorY = -event.values[1];
break;
case Surface.ROTATION_270:
mSensorX = event.values[1];
mSensorY = -event.values[0];
break;
}
mSensorTimeStamp = event.timestamp;
mCpuTimeStamp = System.nanoTime();
}
@Override
protected void onDraw(Canvas canvas) { //主要的小球绘制
canvas.drawBitmap(mWood, 0, 0, null); //先画出背景
final ParticleSystem particleSystem = mParticleSystem;
final long now = mSensorTimeStamp + (System.nanoTime() – mCpuTimeStamp);
final float sx = mSensorX;
final float sy = mSensorY;
particleSystem.update(sx, sy, now);
final float xc = mXOrigin;
final float yc = mYOrigin;
final float xs = mMetersToPixelsX;
final float ys = mMetersToPixelsY;
final Bitmap bitmap = mBitmap;
final int count = particleSystem.getParticleCount();
for (int i = 0; i < count; i++) {
final float x = xc + particleSystem.getPosX(i) * xs;
final float y = yc – particleSystem.getPosY(i) * ys;
canvas.drawBitmap(bitmap, x, y, null);
}
invalidate();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
}
整个例子可以在Android 2.3 SDK的Samples文件夹下找到,这个小铁球的例子可以帮助我们改造为平衡球游戏。
RSS