Sensors are a common way to control games on smartphones. They work fine when the only controls in the game are left and right (like a car racing game). If you plan to also move up and down, you need to ask the player to do a calibration at the beginning of the game to make it usable. Note that, when you are using only one axis, such calibration is not necessary.
In addition to this, the up and down movement tends to interfere with the sensorLandscape orientation. So, the use of sensors is not a very good idea for YASS.
Note
Sensors are good controls only in certain cases.
You also have to consider that, while sensors are a replacement for directions, you still need to place the action buttons on the screen—in our case, the fire button.
We are not going to use sensors for YASS but, if you want to make a game that uses them, we will cover the basics.
You need to register a listener for the accelerometer and another one for the magnetic field. You should only listen to the sensors while the game is running, so we will override the life cycle methods to register and unregister sensors accordingly:
private void registerListeners() {
SensorManager sm = (SensorManager)
mActivity.getSystemService(Activity.SENSOR_SERVICE);
sm.registerListener(mAccelerometerChangesListener,
sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_FASTEST);
sm.registerListener(mMagneticChangesListener,
sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_FASTEST);
}
private void unregisterListeners() {
SensorManager sm = (SensorManager)
mActivity.getSystemService(Activity.SENSOR_SERVICE);
sm.unregisterListener(mAccelerometerChangesListener);
sm.unregisterListener(mMagneticChangesListener);
}
@Override
public void onStart() {
registerListeners();
}
@Override
public void onStop() {
unregisterListeners();
}
@Override
public void onResume() {
registerListeners();
}
@Override
public void onPause() {
unregisterListeners();
}
Note that we are using SensorManager.SENSOR_DELAY_FASTEST, which means that the sensors will give feedback as fast and as often as they can. This is very important for real-time games.
We are setting objects as listeners. Each listener will just copy the values of the sensor into a local array that we will process later on. For example, in the case of an accelerometer, we will do:
To obtain the final value, we have to do some calculations. So, we will add a onPreUpdate method that will be called by the GameEngine just before calling onUpdate.
It is important to note that there are some special cases. They are as follows:
There are devices that lack a magnetic field sensor. In such cases, we can use a simplified version using the value of the accelerometer. Nvidia Shield and specific versions of Nook are some of these devices.
In all cases, the sensors are related to the default orientation of the device, which can be either landscape or portrait. We have to take this into consideration while processing the values.
All in all, the conversion for the horizontal axis can be done like this:
private double getHorizontalAxis() {
if (SensorManager.getRotationMatrix(mRotationMatrix, null, mLastAccels, mLastMagFields)) {
if (mRotation == Surface.ROTATION_0) {
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, mRotationMatrix);
SensorManager.getOrientation(mRotationMatrix, mOrientation);
return mOrientation[1] * DEGREES_PER_RADIAN;
}
else {
SensorManager.getOrientation(mRotationMatrix, mOrientation);
return -mOrientation[1] * DEGREES_PER_RADIAN;
}
}
else {
// Case for devices which do NOT have magnetic sensors
if (mRotation == Surface.ROTATION_0) {
return -mLastAccels[0]* 5;
}
else {
return -mLastAccels[1] * -5;
}
}
}
The getHorizontalAxis code does the following steps:
Calculates the rotation matrix using the last data from the accelerometer and the magnetic sensor.
If it returns true, all goes well. Based on the rotation of the device, we decide whether we need to remap the coordinate system or not and then return the orientation that is converted into degrees.
If it could not be calculated (lack of a magnetic field sensor), the method returns false. We have to rely on an approximation using the accelerometer values. Based on the rotation of the device, we should use one or another axis.
The rotation of the device can be read in the constructor of the InputController with a single line of code.
The is method just converts the reading (in degrees) into values in the range [-1,1] by using the maximum angle at which we consider it to be fully tilted. I recommend you to play with this constant and start with 30 degrees.