Xamarin逆引きTips

Xamarin逆引きTips

Plugins for Xamarinを使いこなすには?(Device Motion ― 加速度センサー/ジャイロスコープセンサー編)

2016年3月8日 (2016/03/10 更新)

デバイス固有の機能に簡単にアクセスできるPlugins for Xamarinの一つ、「Device Motion Plugin」プラグインを紹介。今回は、Accelerometer(加速度)センサー、Gyroscope(ジャイロスコープ)センサーの機能を使う方法を説明する。

Japan Xamarin User Group 田淵 義人(@ytabuchi
  • このエントリーをはてなブックマークに追加

 前回はGPSを簡単に使用できるGeolocatorを紹介した。今回はデバイスの動きを検知する各種センサーを使用できるDevice Motion Pluginを2回に分けて紹介していく。

  • 本連載ではXamarin for Visual Studioの最新版を使用している。2016年2月末現在の最新版4.0.1.93では、[Blank App (Xamarin.Forms Portable)]テンプレートを使って新規プロジェクトを作成すると、PCL/Android/iOS/UWP/Windows 8.1(ストアアプリ)/Windows Phone 8.1の6つのプロジェクトが作成されるが、本連載ではAndroid/iOS/UWPでのスクリーンショットを使用している。ぜひ最新版にアップデートしてUWPアプリの作成も試してもらいたい。

Device Motion Pluginのインストール

 Xamarin.FormsのプロジェクトをVisual StudioもしくはXamarin Studioで開き、Visual Studioの場合は[ソリューション エクスプローラー]で一番上のソリューション項目を右クリックして(表示されるコンテキストメニューから)[ソリューションの NuGet パッケージの管理]をクリックする(Xamarin Studioの場合は[ソリューション]ビューの各プロジェクト項目からNuGetパッケージを追加できる)。

 これにより表示されたページの[参照]タブで「Xam.Plugin.DeviceMotion」を検索し、Device Motionを[インストール]する。

Device Motion Pluginの概要

 CrossDeviceMotionクラス(DeviceMotion.Plugin名前空間)のCurrentプロパティを使用して、Accelerometer(加速度)センサーGyroscope(ジャイロスコープ)センサーMagnetometer(磁気)センサーCompass(コンパス)にアクセスできる。

 センサーの種類を表すMotionSensorType列挙体にAccelerometerGyroscopeMagnetometerCompassが含まれており、センサーの応答間隔を表すMotionSensorDelay列挙体には以下が含まれている。単位はミリ秒だ。

  • Fastest = 0
  • Game = 20
  • Ui = 60
  • Default = 200

 戻り値の型であるMotionSensorValueType列挙体にはSingleVectorがあり、AccelerometerGyroscopeMagnetometerVector値を、CompassSingle値を返す。

 APIの詳細はGitHubのReadme(英語)を参照してほしい。

 今回は「Accelerometer」と「Gyroscope」を紹介する。

Accelerometer(加速度)センサー

 Accelerometerは、デバイスの傾きを取得するセンサーだ。使い道としては、例えばドライビングゲームでスマホの傾きを検出して、それに応じてハンドルを切る操作などが想像できるだろう。

 簡単なサンプルコードはGitHubにアップした。サンプルの中の「BI_Accelerometer」と「BI_Gyroscope」というフォルダーの中身がXamarin.Formsのプロジェクトで、「X_Accelerometer」と「X_Gyroscope」がXamarinネイティブのプロジェクトだ。

 次のコードで傾きを取得できる。

C#
using DeviceMotion.Plugin;
using DeviceMotion.Plugin.Abstractions;
using System.Diagnostics;
IDeviceMotion motion = CrossDeviceMotion.Current; // <- 1
motion.Start(MotionSensorType.Accelerometer, MotionSensorDelay.Default); // <- 2
motion.SensorValueChanged += (object sender, SensorValueChangedEventArgs e) => // <- 3
{
 //Device.BeginInvokeOnMainThread(() =>
 //{
   Debug.WriteLine(((MotionVector)e.Value).X); // <- 4
   Debug.WriteLine(((MotionVector)e.Value).Y);
   Debug.WriteLine(((MotionVector)e.Value).Z);
 //}); // <- 5
};
リスト1 デバイスの傾きを取得するコード例

1 CurrentメソッドでCrossDeviceMotionクラスのインスタンスを取得する。そのインスタンスをメンバー変数に代入して保持しておかないと、ガベージコレクションによりオブジェクトが自動削除されることがあるので注意してほしい。

2 Startメソッドに、引数としてMotionSensorType(センサーの種類)とMotionSensorDelay(センサー値の取得間隔)を与えて、センサー値の取得を開始する。

3 必要に応じてIsActive(MotionSensorType sensorType)メソッドでセンサーがアクティブなことを確認し、SensorValueChangedイベントのイベントハンドラーで、デバイスの傾きが変化した際の処理を記述すればよいだろう。

4 SensorValueChangedイベントハンドラーの引数として渡されたSensorValueChangedEventArgsValueプロパティ値は、2で指定したMotionSensorTypeに基づき、適切な型にキャストしてほしい。リスト1では、X/Y/Z軸の値を取得するためにMotionVector型にキャストしている。

5 センサー値の取得は、バックグラウンドのスレッドで動作している。そのため、Viewの値を変更する場合は、その処理をDevice.BeginInvokeOnMainThread(() =>{ }でくくる必要があるので注意してほしい(上記のコードでは、Viewが動作するメインスレッドにアクセスする必要がないのでコメントアウトしている)。BeginInvokeOnMainThreadメソッドは、名前のとおりだが、メインスレッド上のコードを呼び出すためのものである。

注意点

 Accelerometerセンサーで得られるX値・Y値・Z値だが、OSにより取得できる値が異なるので注意してほしい。図1に示すとおり、Androidだけ特殊のようだ2016/03/10更新: 以下の数値の説明に誤りがありましたので修正しました。詳しくは末尾の更新履歴をご参照ください)。

  • Androidの場合: [+]側が上になる場合、加速度の値(単位はm/s2=メートル毎秒毎秒)が戻り値として取得できる。例えば[X]を[+]にするにはAndroid端末を画面が左になるように垂直にすると[X]の傾きが1G(=Gは重力加速度を表し、1G= 9.8m/s2)で9.8になる。
  • iOS、UWPの場合: 共に、1G(=重力加速度)を1とした値が戻り値として取得できる。
図1 加速度の値(X/Y/Z軸のイメージ)

まずスマートフォンの表面の横軸がX軸、縦軸がY軸、スマートフォンの裏面から表面に突き刺さるような形でZ軸をイメージしてほしい。
Accelerometerは傾きなので、「X/Y/Z軸のどのベクトルが、重力に対して上になっているか?」が数値に反映される。
例えばスマートフォンを地面に対して水平に保持すると(=机の上にスマホを置いているのと同じ状態で)、重力に反するベクトルがZ軸のプラス方向に1Gとなるので、Z軸のみがAndroidでは9.8、iOS/UWPでは-1となる(X軸とY軸は0)。
スマートフォンを縦に持って地面に向かって垂直の状態を保持すると(=机の上にスマホの下側を付けて立てている状態で)、重力に反するベクトルがY軸のプラス方向に1Gとなるので、Y軸のみがAndroidでは9.8、iOS/UWPでは-1となる(X軸とZ軸は0)。
ここまでくればX軸についても理解できるだろうが、スマートフォンの左側面を下にして横に持って垂直の状態を保持すると(=机の上にスマホの左側を付けて立てている状態で)、重力に反するベクトルがX軸のプラス方向に1Gとなるので、X軸のみがAndroidでは9.8、iOS/UWPでは-1となる(Y軸とZ軸は0)。

 理解がより深まるように、もう少し複雑な事例を示しておこう。

図2 端末の右上が上になるようにいい感じに斜めに持った例(左:Android、中:iPhone、右:UWP)

これは図1で行ったX軸、Y軸、Z軸に対しての各プラス方向への傾けを全て半分ずつ(=45度ずつ)行った状態だ。コツとしては、机に置いた状態から下を付けた状態で45度傾け、さらに左側が下になる形で45度分横に回転させることだ。
これによりX/Yの値が、Androidではそれぞれ5、iOS/UWPでは-0.5となり、Zの値がAndroidでは7前後、iOS/UWPでは-0.7前後となる(Z値はセンサーの位置によって多少数値が異なるようだ)。

Xamarin.iOSでのAccelerometerの取得方法

 Xamarin.iOSでAccelerometerセンサーの値を取得するには、CMMotionManagerクラスを使用する。

 ざっくりと説明すると、CMMotionManagerオブジェクトのStartAccelerometerUpdatesメソッドでセンサー値の取得を開始する。同メソッドの第2引数にはCMAccelerometerHandlerデリゲートを指定するが、そのデリゲートメソッドの第1引数に渡されるCMAccelerometerDataオブジェクトのAcceleration.XプロパティでX値、Acceleration.YプロパティでY値、Acceleration.ZプロパティでZ値を取得すればよい。

 詳しくは「Xamarin.iOSでAccelerometerを使うには? - Xamarin 日本語情報」を参照してほしい。併せて、英語になるが「Use CoreMotion with Accelerometer - Xamarin(英語)」と、そのサンプルアプリも参照するとより理解が深まるだろう。

Xamarin.AndroidでのAccelerometerの取得方法

 一方、Xamarin.Androidでセンサー値を取得するには、SensorManagerクラスを使用する。

 ざっくりと説明すると、ActivityにISensorEventListenerインターフェースの実装を追加し、SensorManagerオブジェクトのRegisterListenerメソッドを使用してセンサー監視を登録する。その後は、先ほどのインターフェースにより実装したOnSensorChangedメソッド内で、その引数として渡されたSensorEventオブジェクトのValuesプロパティ値(=IList<float>型で、Values[0]がX値、Values[1]がY値、Values[2]がZ値を表す)を使用すればよい。

 詳しくは「Xamarin.AndroidでAccelerometerを使うには? - Xamarin 日本語情報」を参照してほしい。併せて、英語になるが「Get Accelerometer Readings - Xamarin(英語)」と、そのサンプルアプリも参照するとより理解が深まるだろう。

Gyroscope(ジャイロスコープ)センサー

 Gyroscopeは、デバイスの動きを検知するセンサーだ。使い道としては、例えばLINEユーザーであれば「ふるふる」の機能を思い浮かべてほしい。Googleマップにも「シェイクでフィードバックを送信」の機能がある。

 Gyroscopeのセンサー値を取得するコードは、先ほどのAccelerometerの場合と比較して、MotionSensorType列挙体でGyroscopeを指定する以外は変わらないため、ここでは詳しい説明を割愛する。ポイントは、MotionVectorオブジェクトのXプロパティ値で前後のひねりの速さ、Yで左右のひねりの速さを取得でき、Zでスマホ正面から見て左右の回転(=スライド)を取得できる。Apple Developer Guide(英語)のMotion EventsのページのFigure 4-2が分かりやすいだろう。

図3 ひねりの速度の値(X/Y/Z軸のイメージ)

まずスマートフォンの表面の横軸がX軸、縦軸がY軸、スマートフォンの裏面から表面に突き刺さるような形でZ軸をイメージしてほしい。
Gyroscopeは「X/Y/Z軸の各軸に対するひねり」が数値に反映される。試す際には、それぞれの軸を中心として円状の矢印の方向に回転させるとプラスの値、逆方向に回転させるとマイナスの値が得られる。なお、AndroidとiOS/UWPでは回転方向に対して数値の+と-が逆になるので注意してほしい。回転速度の単位はrad/s(ラジアン毎秒)となる。

 かなり乱暴だが、サンプルアプリでは端末のシェイクを判定する次のロジックを用意した。SensorValueChangedイベントハンドラー内で判定しているが、通常は別途Modelを用意して判定するとよいだろう。

C#
gyroData.Add(((MotionVector)e.Value).X); // <- 1
gyroData.Add(((MotionVector)e.Value).Y); // <- 1
gyroData.Add(((MotionVector)e.Value).Z); // <- 1
var shake = gyroData.Where(gyroData => Math.Abs(gyroData) > 4).Count(); // <- 2
if (shake > 3) // <- 3
{
   motion.Stop(MotionSensorType.Gyroscope);
   await DisplayAlert("Gyroscope", "Shaked!", "OK");
}
リスト2 端末のシェイクを判定する次のロジック(SensorValueChanged◎イベントハンドラーの中)

1 ObservableCollection<double>型のメンバー変数gyroDataに値を追加していき、

2 LINQでgyroDataの絶対値が4より大きいデータの個数をカウントし、

3 その個数が3より大きい際に「シェイクした」と判定して、ダイアログを表示している。

Xamarin.iOSでのGyroscopeの取得方法

 基本的なコードはAccelerometerと同じなので割愛する。違いは、CMMotionManagerクラスのStartGyroUpdatesメソッドでセンサー値の取得を開始することと、(先ほどはAcceleration.Xプロパティだった箇所が)RotationRate.xプロパティでX値、RotationRate.yプロパティでY値、RotationRate.zプロパティでZ値を取得することだ。

Xamarin.AndroidでのGyroscopeの取得方法

 こちらも基本的なコードはAccelerometerと同じだ。違いは、SensorManager型のメンバー変数sensorManagerRegisterListenerメソッドを呼び出す際に、その第2引数に(先ほどのAccelerometerではsensorManager.GetDefaultSensor(SensorType.Accelerometer)を指定したが)GyroscopeではsensorManager.GetDefaultSensor(SensorType.Gyroscope)を渡すことだ。

まとめ

 Device Motion Pluginを使用することで、iOS/Android/UWP/Windows Phoneなどで簡単に各種センサーの機能を使用することができる。皆さんのアプリに、タップ以外のモーションによる操作を追加したい場合にぜひ活用してほしい。

 次回はMagnetometer、Compassを紹介する。また、Xamarinネイティブでの各センサーの使い方もあらためて詳しく紹介したい。

【更新履歴】2016/03/10

 Accelerometerセンサーで得られる値について、「Androidの最大値が10、iOSが1」と記載していましたが、「Androidは加速度の値(=単位はm/s2=メートル毎秒毎秒で、重力加速度は9.8m/s2)、iOSはG(=重力加速度)を1とした値」の誤りでした。お詫びして訂正させていただきます。

※以下では、本稿の前後を合わせて5回分(第62回~第66回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

62. Plugins for Xamarinを使いこなすには?(GPS編)

デバイス固有の機能に簡単にアクセスできるPlugins for Xamarinを複数回にわたって紹介していく。今回は、GPSの機能を使える「Geolocator」プラグインを説明する。

63. Plugins for Xamarinを使いこなすには?(ファイルシステム編)

デバイス固有の機能に簡単にアクセスできるPlugins for Xamarinを複数回にわたって紹介していく。今回は、簡単にファイルの入出力が行える「PCL Storage」プラグインを説明する。

64. 【現在、表示中】≫ Plugins for Xamarinを使いこなすには?(Device Motion ― 加速度センサー/ジャイロスコープセンサー編)

デバイス固有の機能に簡単にアクセスできるPlugins for Xamarinの一つ、「Device Motion Plugin」プラグインを紹介。今回は、Accelerometer(加速度)センサー、Gyroscope(ジャイロスコープ)センサーの機能を使う方法を説明する。

65. Plugins for Xamarinを使いこなすには?(Device Motion ― 磁気センサー/コンパス編)

デバイス固有の機能に簡単にアクセスできるPlugins for Xamarinの一つ、「Device Motion Plugin」プラグインを紹介。今回は、Magnetometer(磁気)センサー、Compass(コンパス)の機能を使う方法を説明する。

66. Xamarin Workbooksを使用するには?(REPL&リッチテキスト編)

C#のREPLアプリとして対話型でコード実行ができるだけでなく、そのコード実行をコンテンツに含めたリッチ文書が作成できるXamarin Workbooksの基本的な使い方を解説。

GrapeCity Garage 記事内容の紹介

Azure Central の記事内容の紹介

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • 日本マイクロソフト株式会社
  • グレープシティ株式会社