Hour 11. Using the Linear Acceleration and Proximity Sensors


What You’ll Learn in This Hour

• How to use a linear acceleration sensor

• How to use a proximity sensor


The previous hour introduced the sensors supported by the Android OS. Depending on the manufacturer, some Android devices will come with some sensors, and some will not, so we have to check the hardware to see if a certain sensor is available. For most game designs, advanced sensors are not needed. But a game custom-designed for a certain unique sensor, or two or three, might be very fascinating, indeed! In this hour, you learn how to use two of the advanced sensors: the linear acceleration sensor and the proximity sensor. The remaining sensors that will be studied over the next few hours include gravity, rotation, gyroscope, pressure, magnetic field, ambient light, and temperature. It almost seems like a typical Android device has enough built in to make a project robot—just add a chassis and motors because all the sensors are built in. I wouldn’t be at all surprised to find roboticists using an Android for their projects.

Accessing the Linear Acceleration Sensor

The linear acceleration sensor reports the movement of the Android device in any direction with an X and Y value. This sensor is similar to the accelerometer in the values reported from acceleration of the device. But, whereas the accelerometer reports the tilt of the device as a continuous position (via a pair of coordinates), the linear acceleration sensor reports only short-term or “sudden” movements. Moving the device left, right, forward, or backward, and rapidly, produces a short-term value for X and Y acceleration, but those values return to 0 when the device is no longer moving. Figure 11.1 shows the X and Y values returned by the sensor based on direction of movement.

Image

Figure 11.1. X and Y values reported by the linear acceleration sensor.

Acceleration does not mean movement! Acceleration is not usually continuous; it represents the increase of velocity from zero up to a certain amount. When velocity reaches the desired value, acceleration drops to zero, and the velocity continues. In space, where there is no atmosphere, an object will continue to move at a certain velocity, essentially forever, unless some counterforce slows it down. In the atmosphere, though, the air itself slows objects—as well as gravity. Firing a bullet from a gun, for instance, involves extremely high acceleration for a brief instant, and then it is “cruising” for the remainder of the time the bullet is in flight.

Initializing the Linear Acceleration Sensor

To begin using the linear acceleration sensor, you first need to create an object to gain access to the SensorManager, like you did before with the accelerometer in the previous hour. This code is shorthand; in the example, the variables are declared as public and initialized in onCreate().

SensorManager sensors = (SensorManager)getSystemService(SENSOR_SERVICE);

Next, create an object for the linear acceleration sensor:

Sensor linear = sensors.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

To use the linear acceleration sensor, no new technique is needed—just implement the same interface class as before:

... implements SensorEventListener

On Pausing and Resuming

The appropriate code should also be added to onPause() and onResume(), just as it was in the previous hour. The source code for the example shows these two methods in their entirety, so there’s no need to cite them here. The important calls are as follows. When the app is paused, we call unregisterListener(), and when resumed, we call registerListener(), with the appropriate parameters. The same code will be used in all the sensor examples, so you will become familiar with it.

Reading the Sensor

Data from the sensor comes into the onSensorChanged() event as a SensorEvent parameter. There are some interesting properties in SensorEvent that you may want to explore, but all we really need to look at are the indexes in the values[] array, where values[0] is X and values[1] is Y—and these are floating point numbers.

The Linear Acceleration Demo

There’s nothing better than a complete, working example to study when you’re learning a new programming library. Following is the source code for an example called the Linear Acceleration Demo. The output of the program is pretty interesting, shown in Figure 11.2. The current acceleration values for both X and Y are shown as bright green (for positive) and bright red (for negative), with X going left and right, and Y going up and down (refer to Figure 11.1 for an illustration of the returned values). In addition, this program remembers the minimum and maximum values and draws them as dark red and dark green for reference.

Image

Figure 11.2. The Linear Acceleration Demo program.

package android.program;

import java.math.BigDecimal;
import android.app.Activity;
import android.os.Bundle;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.view.*;
import android.content.pm.ActivityInfo;
import android.renderscript.*;
import android.hardware.*;

public class Game extends Activity implements SensorEventListener {
    DrawView drawView;
    SensorManager sensors;
    Sensor linear=null;
    String title="Linear Acceleration Sensor Demo";
    Float3 data = new Float3(0,0,0);
    Float3 min = new Float3(0,0,0);
    Float3 max = new Float3(0,0,0);

    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        drawView = new DrawView(this);
        setContentView(drawView);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        //initialize sensors
        sensors = (SensorManager)getSystemService(SENSOR_SERVICE);

        //try to get linear acceleration sensor
        linear = sensors.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        if (linear==null)
            title = "ERROR: NO LINEAR ACCELERATION SENSOR WAS FOUND";
    }

    @Override public void onSensorChanged(SensorEvent event) {
        switch (event.sensor.getType()) {
        case Sensor.TYPE_LINEAR_ACCELERATION:

            //get X value and save limits
            data.x = event.values[0];
            if (data.x < min.x) min.x = data.x;
            if (data.x > max.x) max.x = data.x;

            //get Y value and save limits
            data.y = event.values[1];
            if (data.y < min.y) min.y = data.y;
            if (data.y > max.y) max.y = data.y;
            break;
        }
    }

    @Override public void onAccuracyChanged(Sensor arg0, int arg1) {
    }

    @Override public void onResume() {
        super.onResume();
        drawView.resume();

        //enable the sensor
        sensors.registerListener(this, linear, SensorManager.
            SENSOR_DELAY_NORMAL);
    }

    @Override public void onPause() {
        super.onPause();
        drawView.pause();

        //disable the sensor
        sensors.unregisterListener(this);
    }

    public class DrawView extends SurfaceView implements Runnable
    {
        Thread gameloop = null;
        SurfaceHolder surface = null;
        volatile boolean running = false;
        AssetManager assets = null;
        Paint paint = new Paint();
        Float2 center;

        //constructor method
        public DrawView(Context context) {
            super(context);
            surface = getHolder();
            assets = context.getAssets();
        }

        public void resume() {
            running = true;
            gameloop = new Thread(this);
            gameloop.start();
        }

        public void pause() {
            running = false;
            while (true) {
                try {
                    gameloop.join();
                }
                catch (InterruptedException e) { }
            }
        }

        //thread run method
        @Override public void run() {

            while (running) {
                if (!surface.getSurface().isValid()) continue;

                //open the canvas for drawing
                Canvas canvas = surface.lockCanvas();

                //clear the screen
                canvas.drawColor(Color.BLACK);

                int centerx = canvas.getWidth()/2;
                int centery = canvas.getHeight()/2;
                int top = centery-100;
                int bottom = centery+100;

                paint.setColor(Color.WHITE);
                canvas.drawLine(centerx, top, centerx, bottom, paint);

                int minColor = Color.argb(100, 200, 0, 0);
                int maxColor = Color.argb(100, 0, 200, 0);

                //draw bar for -X minimum (RIGHT)
                int range = (int)Math.abs(min.x)*10;
                Rect rect = new Rect(centerx, centery-30, centerx+range,
                        centery+30);
                paint.setColor(minColor);
                canvas.drawRect(rect, paint);

                //draw bar for +X maximum (LEFT)
                range = (int)Math.abs(max.x)*10;
                rect = new Rect(centerx-range, centery-30, centerx,
                        centery+30);
                paint.setColor(maxColor);
                canvas.drawRect(rect, paint);

                //draw bar for -Y minimum (UP)
                range = (int)Math.abs(min.y)*10;
                rect = new Rect(centerx-30, centery-range, centerx+30,
                        centery);
                paint.setColor(minColor);
                canvas.drawRect(rect, paint);

                //draw bar for +Y maximum (DOWN)
                range = (int)Math.abs(max.y)*10;
                rect = new Rect(centerx-30, centery, centerx+30,
                        centery+range);
                paint.setColor(maxColor);
                canvas.drawRect(rect, paint);

                //draw bar for ACTUAL Y
                range = (int)data.y*10;
                if (range < 0) {
                    //range is negative here
                    rect = new Rect(centerx-30, centery+range, centerx+30,
                            centery);
                    paint.setColor(Color.RED);
                }
                else if (range > 0) {
                    //range is positive here
                    rect = new Rect(centerx-30, centery, centerx+30,
                            centery+range);
                    paint.setColor(Color.GREEN);
                }
                canvas.drawRect(rect, paint);

                //draw bar for ACTUAL X
                range = (int)data.x*10;
                if (range < 0) {
                    //range is negative here
                    rect = new Rect(centerx, centery-30, centerx-range,
                            centery+30);
                    paint.setColor(Color.RED);
                }
                else if (range > 0) {
                    //range is positive here
                    rect = new Rect(centerx, centery-30, centerx-range,
                            centery+30);
                    paint.setColor(Color.GREEN);
                }
                canvas.drawRect(rect, paint);

                //display sensor info
                paint.setColor(Color.WHITE);
                paint.setTextSize(24);
                canvas.drawText(title, 10, 20, paint);
                canvas.drawText("Model: " + linear.getVendor(), 10, 50,
                        paint);
                canvas.drawText("Data: " + toString(data), 10, 80, paint);
                canvas.drawText("Min: " + toString(min), 10, 110, paint);
                canvas.drawText("Max: " + toString(max), 10, 140, paint);

                //close the canvas
                surface.unlockCanvasAndPost(canvas);

                try {
                    Thread. sleep(20);
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public String toString(int value) {
            return Integer.toString(value);
        }

        public String toString(Float3 value) {
            String s = "X:" + round(value.x) + "," +
                "Y:" + round(value.y) + "," +
                "Z:" + round(value.z);
            return s;
        }

        //round to a default 2 decimal places
        public double round(double value) {
            return round(value,2);
        }

        //round a number to any number of decimal places
        public double round(double value, int precision) {
            BigDecimal bd = new BigDecimal(value);
            BigDecimal rounded = bd.setScale(precision,
                    BigDecimal.ROUND_HALF_UP);
            return rounded.doubleValue();
        }
    }
}

Accessing the Proximity Sensor

The proximity sensor is a small light detector that is primarily used to shut off the screen when the user is talking on the phone (to save on battery consumption). Normally, the proximity sensor is located near the speaker so that it will detect when users put the phone up to their ears. On most devices, this sensor tends to be an infrared detector with a very short range of just about one inch.

Sensor prox=sensors.getDefaultSensor(Sensor.TYPE_PROXIMITY);

The key code to respond to proximity sensor events is shown here. Instead of reporting a range of values, the proximity sensor works more like a binary momentary switch—on when it’s tripped, otherwise off. Some devices will simulate a proximity sensor by returning a static value, such as 4.0. Others will return a value of 1.0 for on, 0.0 for off.

@Override public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
    case Sensor.TYPE_PROXIMITY:
        if (event.values[0] < prox.getMaximumRange())
            proxValue = event.values[0];
        else
            proxValue = 0.0f;
        break;
    }
}

The value returned by tripping this sensor is like a momentary push-on switch (like the buttons in an elevator). Wouldn’t it be interesting to use this sensor for user input? How about tripping this sensor to fire a weapon or to cause a character to jump, or to engage nitrous oxide in a racing game? The possibilities are endless!

Summary

The sensor hardware that Android supports is fascinating. You learned how to use two of those sensors this hour—the linear acceleration sensor and the proximity sensor. But where is this going in terms of video game programming? That’s up to the designer! As a game programmer, your job is to tap into the hardware of a system and make services available to a designer, not usually to decide which services to include or not include. If we can provide access to an acceleration sensor, maybe that will come in handy in some unique type of game.

Q&A

Q. Linear acceleration input seems very similar to accelerometer input, as far as dealing with the orientation or movement of the Android device. How might you use the linear acceleration sensor to good effect in a video game?

A. Answers should suggest some behavior like shaking the device.

Q. In what real-world scenarios do you think a linear acceleration sensor would be really helpful?

A. Answers should suggest fast-moving experiences such as sky diving or drag racing.

Workshop

Quiz

1. What Sensor constant do you use with the linear acceleration sensor?

2. What constant is defined for the proximity sensor?

3. What SensorEventListener interface method is not often used?

Answers

1. Sensor.TYPE_LINEAR_ACCELERATION

2. Sensor.TYPE_PROXIMITY

3. onAccuracyChanged()

Activities

Run the Linear Acceleration Sensor Demo, which reports the minimum and maximum acceleration values detected. See if you can find the maximum range for your own Android device. Just be careful not to fling it while doing this test! In personal tests, it was rare to see a value beyond 20 in either direction. What were your results?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.147.77