方位センサーの使用
向きセンサーを使用してデバイスの向きを決定する方法について説明します。
重要な API
前提条件
Extensible Application Markup Language (XAML)、Microsoft Visual C#、イベントについて理解している必要があります。
使用しているデバイスまたはエミュレーターは、向きセンサーをサポートしている必要があります。
OrientationSensor アプリを作成する
向きセンサーは、デバイスの向きの変化にアプリが応答できるようにする、いくつかの種類の環境センサーの 1 つです。
Windows.Devices.Sensors 名前空間には、OrientationSensor と SimpleOrientation という 2 種類の方向センサー API が含まれています。 これらのセンサーはどちらも向きセンサーですが、その用語はオーバーロードされ、非常に異なる目的で使用されます。 ただし、どちらも向きセンサーであるため、どちらもこの記事で取り上げられます。
OrientationSensor API は、2 つの 4 元数と回転マトリックスを取得する 3-D アプリに使用されます。 四元数は、任意の 1 つの軸を中心とした点 [x,y,z] の回転と考えるとわかりやすいでしょう (一方、回転マトリックスは 3 軸を中心とした回転を表します)。 四元数の背後にある数学は、複素数の幾何学的特性と虚数の数学的特性を含むという点でかなりエキゾチックですが、それらを扱うのは簡単で、DirectX のようなフレームワークでそれらをサポートしています。 複雑な 3-D アプリでは、方向センサーを使用してユーザーの視点を調整できます。 このセンサーは、加速度計、ジャイロメーター、コンパスからの入力を結合します。
SimpleOrientation API は、縦向き、縦向き、左横、横向きなどの定義の観点から、現在のデバイスの向きを決定するために使用されます。 また、デバイスが上向きか下向きであるかも検出できます。 このセンサーは、"縦長" や "左横" などのプロパティを返すのではなく、回転値 "Not rotated"、"Rotated90DegreesCounterclockwise" などを返します。 次の表は、一般的な向きのプロパティを対応するセンサーの読み取りにマップします。
オリエンテーション | 対応するセンサーの読み取り |
---|---|
縦向き | NotRotated |
左横 | Rotated90DegreesCounterclockwise |
縦の下 | Rotated180DegreesCounterclockwise |
横右 | Rotated270DegreesCounterclockwise |
手順
[Visual C#] プロジェクト テンプレートから [空白のアプリ (ユニバーサル Windows] を選んで、新しいプロジェクトを作成します。
プロジェクトの MainPage.xaml.cs ファイルを開き、記載されているコードを次のコードで置き換えます。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Core;
using Windows.Devices.Sensors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?linkid=234238
namespace App1
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private OrientationSensor _sensor;
private async void ReadingChanged(object sender, OrientationSensorReadingChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
OrientationSensorReading reading = e.Reading;
// Quaternion values
txtQuaternionX.Text = String.Format("{0,8:0.00000}", reading.Quaternion.X);
txtQuaternionY.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Y);
txtQuaternionZ.Text = String.Format("{0,8:0.00000}", reading.Quaternion.Z);
txtQuaternionW.Text = String.Format("{0,8:0.00000}", reading.Quaternion.W);
// Rotation Matrix values
txtM11.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M11);
txtM12.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M12);
txtM13.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M13);
txtM21.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M21);
txtM22.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M22);
txtM23.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M23);
txtM31.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M31);
txtM32.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M32);
txtM33.Text = String.Format("{0,8:0.00000}", reading.RotationMatrix.M33);
});
}
public MainPage()
{
this.InitializeComponent();
_sensor = OrientationSensor.GetDefault();
// Establish the report interval for all scenarios
uint minReportInterval = _sensor.MinimumReportInterval;
uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
_sensor.ReportInterval = reportInterval;
// Establish event handler
_sensor.ReadingChanged += new TypedEventHandler<OrientationSensor, OrientationSensorReadingChangedEventArgs>(ReadingChanged);
}
}
}
元のスニペットの名前空間の名前を、自分のプロジェクトに指定した名前に変更する必要があります。 たとえば、 OrientationSensorCS という名前のプロジェクトを作成した場合、 namespace App1
を namespace OrientationSensorCS
に置き換えます。
- MainPage.xaml ファイルを開き、元の内容を次の XML に置き換えます。
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="Black">
<TextBlock HorizontalAlignment="Left" Height="28" Margin="4,4,0,0" TextWrapping="Wrap" Text="M11:" VerticalAlignment="Top" Width="46"/>
<TextBlock HorizontalAlignment="Left" Height="23" Margin="4,36,0,0" TextWrapping="Wrap" Text="M12:" VerticalAlignment="Top" Width="39"/>
<TextBlock HorizontalAlignment="Left" Height="24" Margin="4,72,0,0" TextWrapping="Wrap" Text="M13:" VerticalAlignment="Top" Width="39"/>
<TextBlock HorizontalAlignment="Left" Height="31" Margin="4,118,0,0" TextWrapping="Wrap" Text="M21:" VerticalAlignment="Top" Width="39"/>
<TextBlock HorizontalAlignment="Left" Height="24" Margin="4,160,0,0" TextWrapping="Wrap" Text="M22:" VerticalAlignment="Top" Width="39"/>
<TextBlock HorizontalAlignment="Left" Height="24" Margin="8,201,0,0" TextWrapping="Wrap" Text="M23:" VerticalAlignment="Top" Width="35"/>
<TextBlock HorizontalAlignment="Left" Height="23" Margin="4,234,0,0" TextWrapping="Wrap" Text="M31:" VerticalAlignment="Top" Width="39"/>
<TextBlock HorizontalAlignment="Left" Height="28" Margin="4,274,0,0" TextWrapping="Wrap" Text="M32:" VerticalAlignment="Top" Width="46"/>
<TextBlock HorizontalAlignment="Left" Height="21" Margin="4,322,0,0" TextWrapping="Wrap" Text="M33:" VerticalAlignment="Top" Width="39"/>
<TextBlock x:Name="txtM11" HorizontalAlignment="Left" Height="19" Margin="43,4,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM12" HorizontalAlignment="Left" Height="23" Margin="43,36,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM13" HorizontalAlignment="Left" Height="15" Margin="43,72,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM21" HorizontalAlignment="Left" Height="20" Margin="43,114,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM22" HorizontalAlignment="Left" Height="19" Margin="43,156,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM23" HorizontalAlignment="Left" Height="16" Margin="43,197,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM31" HorizontalAlignment="Left" Height="17" Margin="43,230,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM32" HorizontalAlignment="Left" Height="19" Margin="43,270,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock x:Name="txtM33" HorizontalAlignment="Left" Height="21" Margin="43,322,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="53"/>
<TextBlock HorizontalAlignment="Left" Height="15" Margin="194,8,0,0" TextWrapping="Wrap" Text="Quaternion X:" VerticalAlignment="Top" Width="81"/>
<TextBlock HorizontalAlignment="Left" Height="23" Margin="194,36,0,0" TextWrapping="Wrap" Text="Quaternion Y:" VerticalAlignment="Top" Width="81"/>
<TextBlock HorizontalAlignment="Left" Height="15" Margin="194,72,0,0" TextWrapping="Wrap" Text="Quaternion Z:" VerticalAlignment="Top" Width="81"/>
<TextBlock x:Name="txtQuaternionX" HorizontalAlignment="Left" Height="15" Margin="279,8,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="104"/>
<TextBlock x:Name="txtQuaternionY" HorizontalAlignment="Left" Height="12" Margin="275,36,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="108"/>
<TextBlock x:Name="txtQuaternionZ" HorizontalAlignment="Left" Height="19" Margin="275,68,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="89"/>
<TextBlock HorizontalAlignment="Left" Height="21" Margin="194,96,0,0" TextWrapping="Wrap" Text="Quaternion W:" VerticalAlignment="Top" Width="81"/>
<TextBlock x:Name="txtQuaternionW" HorizontalAlignment="Left" Height="12" Margin="279,96,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="72"/>
</Grid>
</Page>
元のスニペットのクラス名の最初の部分を、自分のアプリの名前空間に置き換える必要があります。 たとえば、 OrientationSensorCS という名前のプロジェクトを作成した場合、 x:Class="App1.MainPage"
を x:Class="OrientationSensorCS.MainPage"
に置き換えます。 また、xmlns:local="using:App1"
を xmlns:local="using:OrientationSensorCS"
に置き換える必要があります。
- アプリをビルド、展開、実行するには、F5 キーを押すか、[デバッグ]>[デバッグの開始] の順に選択します。
アプリが実行されたら、デバイスを移動するかエミュレーター ツールを使用して向きを変更できます。
- アプリを停止するには、Visual Studio に戻り、Shift キーを押しながら F5 キーを押すか、[デバッグ]>[デバッグの停止] の順にクリックします。
説明
前の例では、向きセンサーの入力をアプリに統合するために記述する必要があるコードの量を示しています。
アプリは、 MainPage メソッドの既定の向きセンサーとの接続を確立します。
_sensor = OrientationSensor.GetDefault();
このアプリでは、MainPage メソッドで、レポート間隔を設定しています。 次のコードは、デバイスでサポートされる最小の間隔を取得し、要求される 16 ミリ秒の間隔 (約 60 Hz のリフレッシュ レート) と比較します。 サポートされる最小の間隔が要求される間隔よりも大きい場合は、値を最小値に設定します。 それ以外の場合は、値を要求される間隔に設定します。
uint minReportInterval = _sensor.MinimumReportInterval;
uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
_sensor.ReportInterval = reportInterval;
新しいセンサー データは、 ReadingChanged メソッドでキャプチャされます。 センサーのドライバーは、センサーから新しいデータを受け取るたびに、このイベント ハンドラーを使ってアプリに値を渡します。 このアプリの場合、このイベント ハンドラーが次の行で登録されています。
_sensor.ReadingChanged += new TypedEventHandler<OrientationSensor,
OrientationSensorReadingChangedEventArgs>(ReadingChanged);
プロジェクトの XAML 内にある TextBlock に、これらの新しい値が書き込まれます。
SimpleOrientation アプリを作成する
このセクションは、2 つのサブセクションに分かれています。 最初のサブセクションでは、最初から簡単な向きアプリケーションを作成するために必要な手順について説明します。 次のサブセクションでは、先ほど作成したアプリについて説明します。
手順
[Visual C#] プロジェクト テンプレートから [空白のアプリ (ユニバーサル Windows] を選んで、新しいプロジェクトを作成します。
プロジェクトの MainPage.xaml.cs ファイルを開き、記載されているコードを次のコードで置き換えます。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Core;
using Windows.Devices.Sensors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?linkid=234238
namespace App1
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
// Sensor and dispatcher variables
private SimpleOrientationSensor _simpleorientation;
// This event handler writes the current sensor reading to
// a text block on the app' s main page.
private async void OrientationChanged(object sender, SimpleOrientationSensorOrientationChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SimpleOrientation orientation = e.Orientation;
switch (orientation)
{
case SimpleOrientation.NotRotated:
txtOrientation.Text = "Not Rotated";
break;
case SimpleOrientation.Rotated90DegreesCounterclockwise:
txtOrientation.Text = "Rotated 90 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
txtOrientation.Text = "Rotated 180 Degrees Counterclockwise";
break;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
txtOrientation.Text = "Rotated 270 Degrees Counterclockwise";
break;
case SimpleOrientation.Faceup:
txtOrientation.Text = "Faceup";
break;
case SimpleOrientation.Facedown:
txtOrientation.Text = "Facedown";
break;
default:
txtOrientation.Text = "Unknown orientation";
break;
}
});
}
public MainPage()
{
this.InitializeComponent();
_simpleorientation = SimpleOrientationSensor.GetDefault();
// Assign an event handler for the sensor orientation-changed event
if (_simpleorientation != null)
{
_simpleorientation.OrientationChanged += new TypedEventHandler<SimpleOrientationSensor, SimpleOrientationSensorOrientationChangedEventArgs>(OrientationChanged);
}
}
}
}
元のスニペットの名前空間の名前を、自分のプロジェクトに指定した名前に変更する必要があります。 たとえば、 SimpleOrientationCS という名前のプロジェクトを作成した場合、 namespace App1
を namespace SimpleOrientationCS
に置き換えます。
- MainPage.xaml ファイルを開き、元の内容を次の XML に置き換えます。
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
<TextBlock HorizontalAlignment="Left" Height="24" Margin="8,8,0,0" TextWrapping="Wrap" Text="Current Orientation:" VerticalAlignment="Top" Width="101" Foreground="#FFF8F7F7"/>
<TextBlock x:Name="txtOrientation" HorizontalAlignment="Left" Height="24" Margin="118,8,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="175" Foreground="#FFFEFAFA"/>
</Grid>
</Page>
元のスニペットのクラス名の最初の部分を、自分のアプリの名前空間に置き換える必要があります。 たとえば、 SimpleOrientationCS という名前のプロジェクトを作成した場合、 x:Class="App1.MainPage"
を x:Class="SimpleOrientationCS.MainPage"
に置き換えます。 また、xmlns:local="using:App1"
を xmlns:local="using:SimpleOrientationCS"
に置き換える必要があります。
- アプリをビルド、展開、実行するには、F5 キーを押すか、[デバッグ]>[デバッグの開始] の順に選択します。
アプリが実行されたら、デバイスを移動するかエミュレーター ツールを使用して向きを変更できます。
- アプリを停止するには、Visual Studio に戻り、Shift キーを押しながら F5 キーを押すか、[デバッグ]>[デバッグの停止] の順にクリックします。
説明
前の例では、シンプルな向きのセンサー入力をアプリに統合するために記述する必要があるコードの数を示しています。
アプリは、 MainPage メソッドで既定のセンサーとの接続を確立します。
_simpleorientation = SimpleOrientationSensor.GetDefault();
新しいセンサー データは、 OrientationChanged メソッドでキャプチャされます。 センサーのドライバーは、センサーから新しいデータを受け取るたびに、このイベント ハンドラーを使ってアプリに値を渡します。 このアプリの場合、このイベント ハンドラーが次の行で登録されています。
_simpleorientation.OrientationChanged += new TypedEventHandler<SimpleOrientationSensor,
SimpleOrientationSensorOrientationChangedEventArgs>(OrientationChanged);
プロジェクトの XAML 内にある TextBlock に、以下の新しい値が書き込まれます。
<TextBlock HorizontalAlignment="Left" Height="24" Margin="8,8,0,0" TextWrapping="Wrap" Text="Current Orientation:" VerticalAlignment="Top" Width="101" Foreground="#FFF8F7F7"/>
<TextBlock x:Name="txtOrientation" HorizontalAlignment="Left" Height="24" Margin="118,8,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="175" Foreground="#FFFEFAFA"/>