Android游戏编程之加快计和罗盘测试

Android游戏编程之加速计和罗盘测试

游戏中一个有趣的输入方法是加速计,所有的Android设备都要求有一个3D加速计。同样的还有罗盘功能能感应磁场方向以及手机俯仰角。

为了获取加速计信息,我们注册一个侦听器,需要实现的接口名为SensorEventListener,它具有两个方法:

public void onSensorChanged(SensorEvent event);

public void onAccuracyChanged(Sensor sensor, int accuracy);

当一个新的加速计事件发生时会调用第一个方法,而当加速计的精度发生变化时会调用第二个方法。一般我们可以放心的忽略第二个方法。

为了实现这个功能,首先我们需要确认设备上是否具有一个加速计。尽管现在所有的Android设备都应该有一个加速计,但也许将来会有变化。我们必须百分之百确保该输入方法是可用的。

首先我们需要做的是获得一个SensorManager实例,它将会表明设备是否安装了加速计,以及在哪里注册侦听器。我们可以通过Context接口的一个方法来获取SensorManager实例:

SensorManager manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);

SensorManager是Android系统提供的一个系统服务,Android具有多个系统服务,每一个服务可向我们提供不同的系统信息。

一旦获取SensorManager实例,就能检查加速计是否可用:

boolean hasAccel = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0;

通过这段代码,我们查询管理器以了解所有安装的accelerometer类型加速计。这就意味着一个设备可具有多个加速计,不过,实际上只返回一个加速计传感器。

如果已经安装了一个加速计,我们可通过SensorManager来获取它并向其注册SensorEventListener,如下所示:

Sensor sensor = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);

boolean sucess = manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);

参数SensorManager.SENSOR_DELAY_GAME用于指定侦听器更新的频率,其更新内容来自加速计的最新状态。

SensorManager.registerListener()方法返回一个Boolean变量,以表明注册过程是否成功。为了确保任何传感器事件,我们需要价差该Boolean变量。

一旦成功注册侦听器,我们就可以通过SensorEventListener.onSensorChanged()方法来接收SensorEvent。该方法只有在传感器的状态发生变化时才会被调用。

现在该处理SensorEvent了。该SensorEvent有一个公有浮点型数组成员变量SensorEvent.values,它存储了加速计当前3个轴的加速度值。SensorEvent.values[0]保存X轴的值,SensorEvent.values[1]保存Y轴的值,SensorEvent.values[2]保存Z轴的值。

总体来说X、Y轴方向位于你的手机屏幕所在的平面,而Z轴垂直于手机屏幕所在平面,X轴一般位于手机短的那条边的方向,

其他的坐标轴大家可以充分发挥你的空间想象能力。

代码如下:

package org.example.ch04_android_basics;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;

public class AccelerometerTest extends Activity implements SensorEventListener{
	TextView textView;
	StringBuilder builder = new StringBuilder();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		textView = new TextView(this);
		setContentView(textView);
		
		SensorManager manager = (SensorManager)getSystemService(
				Context.SENSOR_SERVICE);
		if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0){
			textView.setText("No accelerometer installed");
		}else{
			Sensor accelerometer = manager.getSensorList(
					Sensor.TYPE_ACCELEROMETER).get(0);
			if(!manager.registerListener(this, accelerometer,
					SensorManager.SENSOR_DELAY_GAME)){
				textView.setText("Couldn't register sensor listener");
			}
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// TODO Auto-generated method stub
		builder.setLength(0);
		builder.append("x: ");
		builder.append(event.values[0]);
		builder.append(", y: ");
		builder.append(event.values[1]);
		builder.append(", z: ");
		builder.append(event.values[2]);
		textView.setText(builder.toString());
	}
	
}

运行效果如图(我的手机是平放在桌面上的所以X,Y方向加速度几乎为0,Z轴大概是重力加速度9.8左右):

Android游戏编程之加快计和罗盘测试

现在我们看看手机如何像一个飞机一样知道自己的俯仰角、翻转角和方向,方法和获取加速计信息差不多,就是获取的Sensor类型不同,在这里我们是:

Sensor compass = manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);

其余的都差不多,代码如下:

package org.example.ch04_android_basics;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;

public class OrientationTest extends Activity implements SensorEventListener{
	TextView textView;
	StringBuilder builder = new StringBuilder();
	float yaw;
	float pitch;
	float roll;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		textView = new TextView(this);
		setContentView(textView);
		
		SensorManager manager = (SensorManager)getSystemService(
				Context.SENSOR_SERVICE);
		
		if(manager.getSensorList(Sensor.TYPE_ORIENTATION).size() == 0){
			textView.setText("No orientation installed");
		}else{
			Sensor compass = manager.getDefaultSensor(
					Sensor.TYPE_ORIENTATION);
			if(!manager.registerListener(this, compass,
					SensorManager.SENSOR_DELAY_GAME)){
				textView.setText("Couldn't register sensor listener");
			}
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// TODO Auto-generated method stub
		yaw = event.values[0];
		pitch = event.values[1];
		roll = event.values[2];

		calculateOrientation(); 
	}
	
	public void calculateOrientation(){
		builder.setLength(0);
		builder.append("yaw: ");
		builder.append(yaw + "\n");
		builder.append("pitch: " );
		builder.append(pitch + "\n");
		builder.append("roll: ");
		builder.append(roll);
		textView.setText(builder.toString());
	}
	
}

yaw存储的是偏航角即与地球磁场方向的偏角;范围在[0, 360]度之间

pitch存储的是手机的俯仰角; 范围在[-180, 180]度之间

roll存储的是手机的翻转角;范围在[-90, 90]度之间

现在我让手机平放在桌面上,指向正东方,会显示多少呢?运行效果如下:

Android游戏编程之加快计和罗盘测试