ここ数日、私はセンサーとキャンバスをいじっていました。
これまでのところ、デバイスの角度に基づいてビットマップの位置を制御することができました。
アプリが動作する方法は、方向データを取得し、デバイスの傾きに応じて、ビットマップを画面上で左または右に移動することです。
私は Android 4.2.2 (AOKP) を実行している Samsung Galaxy S II GT-i9100 でほとんどのテストを行っていますが、再開時にアプリがクラッシュすることを除けば、アプリはほとんど問題なく動作します (原因はわかっていると思います)。
私が抱えている問題は次のとおりです。
Sony Xperia Z (android 4.1.2 を実行、Sony の在庫) で同じコードを実行しようとすると、アプリ全体が途切れ途切れになり (ビットマップがほとんど動かない)、センサー データの取得が途切れ途切れ/遅いためだと思います。私の友人のSony Xperia Sでも同じことが起こります。
Nexus 4 を持っている別の友人にこのアプリを渡しましたが、彼はそのような問題はないと言っています。
ゲームビュー
public class GameView extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private int x = 0;
private int xMultiplier = 0;
private Paint textPaint;
//lowPass
private float smoothVal = 0; //Main variable, check algorithm
private int smoothing = 5; //How strong the smoothing is, larger the value, more time is needed before the value reaches actual sensor value
//Sensors
private SensorManager sensorManager;
private SensorEventListener sensorEventListener;
//Rotation matrices for converting coordinate systems
private float[] rotationMatrixR = new float[9];
private float[] rotationMatrixI = new float[9];
//Arrays storing data for gravity and geomagnetic data needed to get device's angles
private float[] gravity = new float[3];
private float[] geomagnetic = new float[3];
//Array holding angles
private float[] angles = new float[3];
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(20);
sensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
sensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
gravity = sensorEvent.values;
}
else if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
geomagnetic = sensorEvent.values;
}
SensorManager.getRotationMatrix(rotationMatrixR, rotationMatrixI, gravity, geomagnetic);
SensorManager.getOrientation(rotationMatrixR, angles);
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
};
sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_FASTEST);
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {
//Shit hit the fan
Log.e("GameLoopThread", e.toString());
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder){
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
@Override
protected void onDraw(Canvas canvas)
{
x = (int) ((canvas.getWidth() / 100) * ((lowPass(angles[2]) * 100) + 50));
canvas.drawColor(Color.DKGRAY); //This also clears the screen
canvas.drawBitmap(bmp, x, canvas.getHeight() - bmp.getHeight() - 20, null);
canvas.drawText("Azimuth (Z): " + Float.toString(angles[0]),25,25, textPaint);
canvas.drawText("Pitch (X): " + Float.toString(angles[1]),25,45, textPaint);
canvas.drawText("Roll (Y): " + Float.toString(angles[2]),25,65, textPaint);
canvas.drawText("X: " + Integer.toString(x),25,85,textPaint);
}
public static BigDecimal roundFloat(float d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Float.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd;
}
private float lowPass(float curValue) {
smoothVal += (curValue - smoothVal) / smoothing;
return smoothVal;
}
}
ゲームループスレッド
public class GameLoopThread extends Thread {
static final long FPS = 25;
private GameView view;
private Boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean run){
running = run;
}
@Override
public void run(){
long tickPS = 1000/FPS;
long startTime;
long sleepTime;
while(running){
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()){
view.onDraw(c);
}
}
catch (NullPointerException e) {
Log.e("GameLoopThread", e.toString());
}
finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = tickPS - (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0) {
sleep(sleepTime);
}
else {
sleep(10);
}
}
catch (Exception e) {
Log.e("GameLoopThread", e.toString());
}
}
}
}