個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Microsoft MVP for Developer Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Xamarin + Cocos Sharp で iOS, Android 対応のゲームを開発する手順 (7) iOS で加速度センサーの値を取得する

今回は iOS で加速度センサーの値を取得する方法です。

Cocos Sharp でも CCAccelerometer で加速度センサーの値を取得できますが、
この方法だと、センサーの値が遅延して取得されるという不具合があるので、
ネイティブで取得したセンサーの値をグローバルで参照できるクラスに書込み、
その値を Cocos Sharp 側から参照する方法を採用しています。

センサーの値の取得方法は、プッシュ型とプル型の2種類あります。

・プッシュ型…センサーが値を通知してくれる。
・プル型…センサーの値を自分で取得しに行く。

今回はプッシュ型で値を取得します。

まず、センサーの値を書き込むクラス AccelerometerInfo を作成します。

CocosSharpGameSample.Core プロジェクトの CrossCuttingConcerns に AccelerometerInfo.cs を追加します。

f:id:hiro128:20160316165206p:plain

namespace CocosSharpGameSample.Core
{
	public sealed class AccelerometerInfo
	{

		private static readonly AccelerometerInfo defaultInstance = new AccelerometerInfo();
		private double accelerationX = 0d;
		private double accelerationY = 0d;
		private double accelerationZ = 0d;

		private AccelerometerInfo()
		{
		}

		public static AccelerometerInfo Default
		{
			get { return defaultInstance; }
		}

		public double AccelerationX
		{
			get { return this.accelerationX; }
			set { this.accelerationX = value; }
		}

		public double AccelerationY
		{
			get { return this.accelerationY; }
			set { this.accelerationY = value; }
		}

		public double AccelerationZ
		{
			get { return this.accelerationZ; }
			set { this.accelerationZ = value; }
		}

	}
}

CocosSharpGameSample.iOS プロジェクトの AppDelegate.cs のフィールドに、CMMotionManager を定義します。
また、センサーの値を書き込む際のロックキーを定義します。

f:id:hiro128:20160316165411p:plain

// 加速度センサー
private CMMotionManager cMMotionManager;
// ロックキー
private object accelerometerSyncLock = new object();

センサーを有効にし、イベントハンドラ内に値を書き込む処理を追加します。

// 加速度センサーのセットアップ
this.cMMotionManager = new CMMotionManager();
// センサーの更新間隔
cMMotionManager.AccelerometerUpdateInterval = 0.1d;
// センサー更新時のハンドラの設定
cMMotionManager.StartAccelerometerUpdates(NSOperationQueue.CurrentQueue, (data, error) =>
{
	lock (this.accelerometerSyncLock)
	{
		// センサーの値を書き込む
		AccelerometerInfo.Default.AccelerationX = data.Acceleration.X;
		AccelerometerInfo.Default.AccelerationY = data.Acceleration.Y;
		AccelerometerInfo.Default.AccelerationZ = data.Acceleration.Z;
	}
});

以上をまとめると AppDelegate.cs は下記のようになります。

using System;
using Foundation;
using UIKit;
using CoreMotion;
using CocosSharp;
using CocosSharpGameSample.Core;

namespace CocosSharpGameSample.iOS
{
	[Register("AppDelegate")]
	public partial class AppDelegate : UIApplicationDelegate
	{
		// 加速度センサー
		private CMMotionManager cMMotionManager;
		// ロックキー
		private object accelerometerSyncLock = new object();

		public override bool FinishedLaunching(UIApplication app, NSDictionary options)
		{

			// 加速度センサーのセットアップ
			this.cMMotionManager = new CMMotionManager();
			// センサーの更新間隔
			cMMotionManager.AccelerometerUpdateInterval = 0.1d;
			// センサー更新時のハンドラの設定
			cMMotionManager.StartAccelerometerUpdates(NSOperationQueue.CurrentQueue, (data, error) =>
			{
				lock (this.accelerometerSyncLock)
				{
					// センサーの値を書き込む
					AccelerometerInfo.Default.AccelerationX = data.Acceleration.X;
					AccelerometerInfo.Default.AccelerationY = data.Acceleration.Y;
					AccelerometerInfo.Default.AccelerationZ = data.Acceleration.Z;
				}
			});

			// ゲームをを起動する。
			var application = new CCApplication();
			application.ApplicationDelegate = new SampleGameApplicationDelegate();
			application.StartGame();
			return true;
		}

		// 画面の向きを LandscapeLeft に固定
		[Export("application:supportedInterfaceOrientationsForWindow:")]
		public UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, IntPtr forWindow)
		{
			return UIInterfaceOrientationMask.LandscapeLeft;
		}

	}
}

次に、ゲーム画面内にセンサーの値を表示します。

文字を表示する時には CCLabel を使用します。

CocosSharpGameSample.Core プロジェクトの GameLayer.cs のフィールドに、X,Y,Z軸のセンサーの値を表示する CCLabel を定義します。

f:id:hiro128:20160316165511p:plain

private CCLabel accelerationX;
private CCLabel accelerationY;
private CCLabel accelerationZ;

AddedToScene メソッド内で、ラベルのインスタンスを作成、配置します。

this.accelerationX = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
this.accelerationX.HorizontalAlignment = CCTextAlignment.Left;
this.accelerationX.VerticalAlignment = CCVerticalTextAlignment.Top;
this.accelerationX.Color = CCColor3B.White;
this.accelerationX.PositionX = 250f;
this.accelerationX.PositionY = 120f;
AddChild(this.accelerationX);

this.accelerationY = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
this.accelerationY.HorizontalAlignment = CCTextAlignment.Left;
this.accelerationY.VerticalAlignment = CCVerticalTextAlignment.Top;
this.accelerationY.Color = CCColor3B.White;
this.accelerationY.PositionX = 250f;
this.accelerationY.PositionY = 80f;
AddChild(this.accelerationY);

this.accelerationZ = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
this.accelerationZ.HorizontalAlignment = CCTextAlignment.Left;
this.accelerationZ.VerticalAlignment = CCVerticalTextAlignment.Top;
this.accelerationZ.Color = CCColor3B.White;
this.accelerationZ.PositionX = 250f;
this.accelerationZ.PositionY = 40f;
AddChild(this.accelerationZ);

ラベルのテキストにセンサーから取得した値を設定するメソッドを定義します。

private void UpdateAccelerationLabels()
{
	this.accelerationX.Text = "X : " + (AccelerometerInfo.Default.AccelerationX >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationX.ToString("0.00000000");
	this.accelerationY.Text = "Y : " + (AccelerometerInfo.Default.AccelerationY >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationY.ToString("0.00000000");
	this.accelerationZ.Text = "Z : " + (AccelerometerInfo.Default.AccelerationZ >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationZ.ToString("0.00000000");
}

ゲームは1/60秒などの一定時間ごとに「スケジューラ」が呼ばれ続け、
そのタイミングで処理が実行されることで構成されますので、
スケジューラにラベルのテキストを更新するメソッドを登録します。

第2引数は更新頻度です。0.1fだと0.1秒ごとに更新されます。

private void StartScheduling()
{
	this.Schedule(t => this.UpdateAccelerationLabels(), 0.1f);
}

AddedToScene メソッド内で、StartScheduling メソッドをコールします。

potected override void AddedToScene()
{
	(省略)

	this.StartScheduling();
}

以上をまとめると GameLayer.cs は下記のようになります。

using System;
using CocosSharp;

namespace CocosSharpGameSample.Core
{
	public class GameLayer : LayerBase
	{

		private CCLabel accelerationX;
		private CCLabel accelerationY;
		private CCLabel accelerationZ;

		public GameLayer()
			: base()
		{
		}

		protected override void AddedToScene()
		{
			base.AddedToScene();
			// ゲーム画面の背景画像を配置
			var gameBackground = new CCSprite("/Resources/Images/Background/GameBackground.png", null);
			gameBackground.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y);
			AddChild(gameBackground);

			// モーションセンサーの値を表示するラベル
			this.accelerationX = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
			this.accelerationX.HorizontalAlignment = CCTextAlignment.Left;
			this.accelerationX.VerticalAlignment = CCVerticalTextAlignment.Top;
			this.accelerationX.Color = CCColor3B.White;
			this.accelerationX.PositionX = 250f;
			this.accelerationX.PositionY = 120f;
			AddChild(this.accelerationX);

			this.accelerationY = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
			this.accelerationY.HorizontalAlignment = CCTextAlignment.Left;
			this.accelerationY.VerticalAlignment = CCVerticalTextAlignment.Top;
			this.accelerationY.Color = CCColor3B.White;
			this.accelerationY.PositionX = 250f;
			this.accelerationY.PositionY = 80f;
			AddChild(this.accelerationY);

			this.accelerationZ = new CCLabel(String.Empty, String.Empty, 40f, CCLabelFormat.SystemFont);
			this.accelerationZ.HorizontalAlignment = CCTextAlignment.Left;
			this.accelerationZ.VerticalAlignment = CCVerticalTextAlignment.Top;
			this.accelerationZ.Color = CCColor3B.White;
			this.accelerationZ.PositionX = 250f;
			this.accelerationZ.PositionY = 40f;
			AddChild(this.accelerationZ);

			this.StartScheduling();
		}

		private void StartScheduling()
		{
			this.Schedule(t => this.UpdateAccelerationLabels(), 0.1f);
		}

		private void UpdateAccelerationLabels()
		{
			this.accelerationX.Text = "X : " + (AccelerometerInfo.Default.AccelerationX >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationX.ToString("0.00000000");
			this.accelerationY.Text = "Y : " + (AccelerometerInfo.Default.AccelerationY >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationY.ToString("0.00000000");
			this.accelerationZ.Text = "Z : " + (AccelerometerInfo.Default.AccelerationZ >= 0 ? "+" : String.Empty) + AccelerometerInfo.Default.AccelerationZ.ToString("0.00000000");
		}

	}
}

これで、ゲーム画面でセンサーの値が更新されるようになりました。

f:id:hiro128:20160316165535p:plain

今回はここまでです。