Android加速感应器开发平衡球代码解析

如何使用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文件夹下找到,这个小铁球的例子可以帮助我们改造为平衡球游戏。