サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

モーション制御

前回のチュートリアルではタッチイベントとジェスチャーイベントの制御を見てきました。 最近ではほとんどの携帯端末で非常に正確な動作検知機能を搭載しており、LibGDXではそれらを完全にサポートしています。 今回の例では、動作を制御する方法だけでなく、端末が特定の機能をサポートしているかどうかを確認する方法や端末がどのような向きをしているかを検知する方法を見ていきます。

今回は一つのコードを例に説明しますが、注意すべき設定手順がいくつかあります。

まず最初に、LibGDXに対してコンパスと加速度センサーを使うよう指示するためには、Android の MainActivity 内にて、その指示を設定の一部として渡す必要があります。 androidプロジェクト内にあるMainActivity.javaを、以下のように編集してください:

package com.gamefromscratch;

import android.os.Bundle;

import com.badlogic.gdx.backends.android.AndroidApplication;

import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;

public class MainActivity extends AndroidApplication {

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

       

        AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();

        cfg.useGL20 = true;

        cfg.useAccelerometer = true;

        cfg.useCompass = true;

       

        initialize(new MotionDemo(), cfg);

    }

}

 

重要な行は以下の二つです。

cfg.useAccelerometer = true;

cfg.useCompass = true;

 

この行で、加速度センサーとコンパスを有効にするようLibGDX に指示を出しています。



次に、 Android manifestをいくつか変更する必要があります。 これは、アプリケーションがどのようなことを実行するのか、実行の際にどういった権限を要求するのかをAndroid OSへ指示するための設定ファイルです。 Android manifestの扱いに関する内容だけで文字通り本を一冊書き上げることができるくらいなので、詳細が知りたい場合は このページを読んでください。 manifest ファイルは Android プロジェクトのルート位置に置かれており、 AndroidManifest.xmlという名前が付けられています。 これを編集する方法はいくつかあります。 AndroidManifest.xml を右クリックして、Open With->を選んでください。

ManifestEditAs

個人的には単純にテキストエディターを使うのが好みですが、もっと分かりやすく編集がしたい場合はAndroid Manifestを使うことができます。 Android Manifestでは以下のようなウィンドウが開きます:

Java motion android AndroidManifest xml Eclipse Users Mike Dropbox Workspace

これは基本的に、Android manifestをGUIで編集する画面です。 下部のタブを使用すると別のカテゴリに切り替えることができ、対応するフォームが表示されます。 AndroidManifest.xmlをクリックすると、manifestのテキストビューが表示されます。 どのタブを使って編集しても、最終的な編集結果に違いは生じません。

manifestには編集が必要な部分が二つあります。 一つ目は、端末の回転をサポートしたいので、ユーザーが自分の端末を回転させた場合にそれにしたがってアプリケーションも回転させます。 これは android:screenOrientation に fullsensor と設定することで可能です。 二つ目は、android.permission.VIBRATEの権限を付与です。この権限を追加しないと、バイブレーションの呼び出しをするとアプリケーションがクラッシュします!

変更をすると、manifestは以下のようになります:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.gamefromscratch"

    android:versionCode="1"

    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.VIBRATE"/>

    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name"

            android:screenOrientation="fullSensor"

            android:configChanges="keyboard|keyboardHidden|orientation|screenSize">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

</manifest>

上記では変更部分を太字強調しています。 追加で権限を要求する場合は、ユーザーがアプリケーションをインストールする際に必要権限一覧が表示されるので注意してください。 あまりにも多くの権限を要求すると、ユーザーはアプリケーションのインストールをためらってしまいます。 もちろん、必要な権限なのであれば遠慮する必要はありません screenOrientationの値について、これはアプリケーションを作成するための向きをAndroidの指示します。 設定可能なオプションはいくつかあり、 Landscape と Portrait の二つが最も一般的なものです。 fullSensor は、基本的に全ての向きをサポートしていることを意味します。 つまり、端末を360度回転させることができ、アプリもそれに応じて回転します。 一方 “user”を設定した場合は、端末を180度回転させることができません。つまり、端末をさかさまに持って使うことができません。 使用可能なプロパティの詳細については、前述のリンク先で読むことができます。

次へ進む前に、最後に注意すべき点が一つあります。 あなたの android プロジェクトには、実際には AndroidManifest.xml ファイルが二つあります。一つはルートディテクトリにあり、もう一つはサブフォルダー bin 内にあります。 bin フォルダのAndroidManifest.xmlファイルはルートディレクトリのAndroidManifest.xmlファイルによって上書きされてしまうので、ルートディレクトリのAndroidManifest.xmlファイルを使うようにしてください!

はい、これで設定が全て完了したので、サンプルコードを見てみましょう:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationListener;

import com.badlogic.gdx.Gdx;

import com.badlogic.gdx.Input.Orientation;

import com.badlogic.gdx.Input.Peripheral;

import com.badlogic.gdx.graphics.Color;

import com.badlogic.gdx.graphics.GL10;

import com.badlogic.gdx.graphics.g2d.BitmapFont;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class MotionDemo implements ApplicationListener {

private SpriteBatch batch;

private BitmapFont font;

private String message = "Do something already!";

private float highestY = 0.0f;

@Override

public void create() {

   batch = new SpriteBatch();

   font = new BitmapFont(Gdx.files.internal("data/arial-15.fnt"),false);

   font.setColor(Color.RED);

}

@Override

public void dispose() {

   batch.dispose();

   font.dispose();

}

@Override

public void render() {

   int w = Gdx.graphics.getWidth();

   int h = Gdx.graphics.getHeight();

   Gdx.gl.glClearColor(1, 1, 1, 1);

   Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

   batch.begin();

   int deviceAngle = Gdx.input.getRotation();

   Orientation orientation = Gdx.input.getNativeOrientation();

   float accelY = Gdx.input.getAccelerometerY();

   if(accelY > highestY)

      highestY = accelY;

   message = "Device rotated to:" + Integer.toString(deviceAngle) + " degrees ";

   message += "Device orientation is ";

   switch(orientation){

      case Landscape:

         message += " landscape. ";

         break;

      case Portrait:

         message += " portrait. ";

         break;

      default:

         message += " complete crap! ";

         break;

   }

 

   message += "Device Resolution: " + Integer.toString(w) + "," + Integer.toString(h) + " ";

   message += "Y axis accel: " + Float.toString(accelY) + " ";

   message += "Highest Y value: " + Float.toString(highestY) + " ";

   if(Gdx.input.isPeripheralAvailable(Peripheral.Vibrator)){

      if(accelY > 7){

         Gdx.input.vibrate(100);

      }

   }

   if(Gdx.input.isPeripheralAvailable(Peripheral.Compass)){

      message += "Azmuth:" + Float.toString(Gdx.input.getAzimuth()) + " ";

      message += "Pitch:" + Float.toString(Gdx.input.getPitch()) + " ";

      message += "Roll:" + Float.toString(Gdx.input.getRoll()) + " ";

   }

   else{

      message += "No compass available ";

   }

   font.drawMultiLine(batch, message, 0, h);

   batch.end();

}

@Override

public void resize(int width, int height) {

   batch.dispose();

   batch = new SpriteBatch();

   String resolution = Integer.toString(width) + "," + Integer.toString(height);

   Gdx.app.log("MJF", "Resolution changed " + resolution);

}

@Override

public void pause() {

}

@Override

public void resume() {

}

 

}

 

このプログラムを端末上で実行すると、以下が表示されます:

Appresults

端末を動かすと、様々な値が更新されます。 携帯を直立状態から30度以内の角度まで持ち上げると、バイブレーション機能が動作します。 もちろん、これはあなたの端末がこれらセンサー全てをサポートしていることが前提です!

実際のところコード自体はとても簡単で、LibGDXによってモーションセンサーの操作が非常に簡単になっています。 少し複雑なのは、戻り値について理解することくらいです。 ロジックの大部分はrender()メソッド内に記述しています。 最初に、端末の現在の回転角度を取得します。 この値は、端末をあなたの顔と平行な状態で直立させて持つと0度になります。 One important thing to realize is this value will always have 0 as up, regardless to if you are in portrait or landscape mode.  これはLibGDXがあなたに代わって処理を簡単にするためのものですが、Android標準とは違う挙動となっています。



次に、端末の向きを取得します。向きは landscape もしくは portrait (横長スクリーンと縦長スクリーン)のいずれかになります。 そしてgetAccelerometerY()を使って加速度センサーのYの値を確認します。 またgetAcceleromterX()とgetAcceleromterZ()を使って、X軸とZ軸の加速度センサーの動きを確認することもできます。 改めて言いますが、端末の向きに関わらずLibGDXは軸の方向を標準化しています。 Speaking of which, Y is up.  つまり、携帯をあなたの顔の前で真っ直ぐと平行に持っている場合、Y軸は上下方向として考えることができます。 Z軸はあなたに向かって前後の方向に伸びているので、端末を前に突き出したりこちらへ引き戻したりするのがZ軸方向の動作になります。 X軸は左右方向の動きになります。

では、加速度センサーによって取得される値とは一体何なのでしょうか? Well this part gets a bit confusing, as it measures both speed and position in a way.  例えば携帯をあなたの前に真っ直ぐにして、顔と画面を平行にして持った場合、9.8の値が返されます。 この数字はあなたにとって馴染みのあるものでしょう。これは重力によって物体が1秒間に落下する速度をメートル単位で表した数字です。 したがって、あなたの携帯が直立状態で静止していた場合、この値は9.8になります。 携帯を体と平行状態で上に動かすとこの値は9.8から上がり、上昇度合いは携帯を動かす速さによって変わります。 一方で携帯を下に動かすと、9.8以下の値が返されます。 携帯を平らな机に置いた場合は、0の値が返されます。 携帯の上下をひっくり返すと、携帯を静止状態にした場合は-9.8の値が返されます。 X軸とZ軸の場合も全く同じ挙動をしますが、上下の動きではなく左右や前後への動きでこれが起こります。

それではコードの内容に戻りましょう。 現在のaccelY値がこれまでの最高値かどうかを確認し、最高値だった場合はそれを記録して画面に表示します。 次にorientationの戻り値を確認して、適切なメッセージを画面に表示します。 画面に表示するために、集めたいくつかの情報をまとめます。 次に、非常に重要なGdx.input.isPeripheralAvailable()を呼び出します。 これはユーザーの端末が必要な機能をサポートしている場合にはtrueを戻り値として返します。 最初に携帯がバイブレーション機能をサポートしている確認し、サポートしている場合は携帯のaccelYが7以上がどうかを確認します。 値が9.8だと端末が上下方向に直立している状態を表すので、値が7以上の場合だとだいたい直立状態から35度以内の傾きを保っているので覚えておいてください。 値が7以上の場合、vibrate()を呼び出してバイブレーションを機能させます。vibrate()に引数として渡す値は、振動させる時間をミリ秒で表したものです。

次は携帯がコンパスを実装しているかどうかを確認します。実装している場合は、北極に対する端末の位置を確認できます。 以下は、Googleのドキュメントより抜粋した各値の説明です:

方位角(Azimuth)とは、 Z 軸周りの回転のことです (0<=azimuth<360)。 0 = 北向き, 90 = 東向き, 180 = 南向き, 270 = 西向き
傾斜(Pitch)とは X 軸周りの回転のことです (-180<=pitch<=180), with positive values when the z-axis moves toward the y-axis.
回転(Roll)とは、Y 軸周りの回転のことです (-90<=roll<=90), with positive values when the z-axis moves toward the x-axis.

詳細についてはここで読めます。

最後に、完成したメッセージを画面に描写します。

今回の例では、注意すべきとても重要な点がもう一つあります。:

public void resize(int width, int height) {

   batch.dispose();

   batch = new SpriteBatch();

   String resolution = Integer.toString(width) + "," + Integer.toString(height);

   Gdx.app.log("MJF", "Resolution changed " + resolution);

}

 

resize() メソッド内で SpriteBatch()を破棄して再作成しています。 端末の向きをlandscapeからportraitへ、もしくはportraitからlandscapeへ変更するとsprite batchのサイズが現在の端末のサイズと合わなくなるので、 sprite batchを無効にしているのです。 したがって、resize() 呼び出し内でSpriteBatch を再作成しています。