読者です 読者をやめる 読者になる 読者になる

個人的なメモ 〜Cocos Sharp 情報を中心に‥

Tomohiro Suzuki @hiro128_777 のブログです。Cocos Sharp の事を中心に書いています。 Microsoft MVP for Visual Studio and Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

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

Xamarin Cocos Sharp

今回は 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

今回はここまでです。