求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
Android游戏开发之小球重力感应实现(十五)
 

发布于 2011-11-01

 

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

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

传感器名称如下:

  • 加速度传感器(accelerometer)
  • 陀螺仪传感器(gyroscope)
  • 环境光照传感器(light)
  • 磁力传感器(magnetic field)
  • 方向传感器(orientation)
  • 压力传感器(pressure)
  • 距离传感器(proximity)
  • 温度传感器(temperature)

1.SensorMannager传感器管理对象

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

view plain

SensorManager mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

2.实现SensorEventListener接口

说道SensorEventListener接口就不得不说SensorListener接口。在Android1.5一下是通过实现SensorListener接口来捕获 手机传感器状态,但是在1.5以上如果实现这个接口系统会提示你这行代码已经过期。今天我们不讨论SensorListener因为它已经是过时的东西了。主要讨论一下SensorEventListener接口。我们须要实现SensorEventListener这个接口 onSensorChanged(SensorEvent event)方法来捕获手机传感器的状态,拿到手机 X轴Y轴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。

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

重力感应简单速度计算的方式。 每次摇晃手机计算出 X轴 Y轴 Z轴的重力分量可以将它们记录下来 然后每次摇晃的重力分量和之前的重力分量可以做一个对比 利用差值和时间就可以计算出他们的移动速度。(下面这段代码是我之前的博文中摘录过来的,因为那篇写的不是很好所以在这一篇中我详细总结一下)

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小球重力感应的完整代码

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, 020, mPaint);  

        mCanvas.drawText("Y轴重力值 :" + mGY, 040, mPaint);  

        mCanvas.drawText("Z轴重力值 :" + mGZ, 060, 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;  

        }  

    }  

    }  


相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程

 
分享到
 
 
     


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...