今回は、加速度センサーの値を利用してキャラクターを動かしてみます。
画面を傾けた方向に自機が動きます。
これでいよいよゲームっぽくなります。
まずは、加速度センサーの値の取得を調整します。
・画面を横向きにしているのでX軸、Y軸がてれこになります。
・ちょうど手に持った時の角度をニュートラルにするために Math.Atan2 で角度にオフセットを入れます。
この処理はネイティブ部分で行なっているので、iOS, Android それぞれのセンサーの値取得部分を修正します。
iOS は下記の通りです。
CocosSharpGameSample.iOS プロジェクトの AppDelegate.cs
// センサー更新時のハンドラの設定 cMMotionManager.StartAccelerometerUpdates(NSOperationQueue.CurrentQueue, (data, error) => { // センサーの値を書き込む lock (this.accelerometerSyncLock) { // 横向きなのでX,Yを入れ替え // X,Yの感度を調整するためセンサーのX,Y軸の取得値を調整 AccelerometerInfo.Default.AccelerationX = data.Acceleration.Y * 8d; // 角度のオフセット double angleY = Math.Atan2(data.Acceleration.Z, data.Acceleration.X); AccelerometerInfo.Default.AccelerationY = Math.Sin(angleY - 30.6d) * 6d; AccelerometerInfo.Default.AccelerationZ = data.Acceleration.Z; } });
Android は下記の通りです。
CocosSharpGameSample.Android プロジェクトの MainActivity.cs
// センサーの値が変更されたときのハンドラ public void OnSensorChanged(SensorEvent e) { lock (this.accelerometerSyncLock) { // 横向きなのでX,Yを入れ替え // X,Yの感度を調整するためセンサーのX,Y軸の取得値を調整 AccelerometerInfo.Default.AccelerationX = e.Values[1] * 1.5f; // 角度のオフセット double angleY = Math.Atan2(e.Values[2], e.Values[0]); AccelerometerInfo.Default.AccelerationY = Math.Sin(angleY - 29.1d) * 12f; AccelerometerInfo.Default.AccelerationZ = e.Values[2]; } }
自機の画像を配置します。
※自機の画像はハムコロ様の素材を利用させていただきました。
・制作者:ハムコロ様
・配付元:http://homepage2.nifty.com/hamcorossam
iOS は下記の通りです。
/Resources/Images/Background/GameBackground.png
Android は下記の通りです。
/Assets/Images/Background/GameBackground.png
ゲーム内の設定値を管理するクラスを作ります。
CocosSharpGameSample.Core プロジェクトの CrossCuttingConcerns に GameSettings.cs を追加します。
自機のサイズ, 画面のサイズ, 加速度センサーの取得値の増幅倍率を設定します。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CocosSharpGameSample.Core { public class GameSettings { // 自機のサイズ public const float PlayerWidth = 32f; public const float PlayerHeight = 32f; // 画面のサイズ public const float ScreenWidth = 1280f; public const float ScreenHeight = 720f; // 加速度センサーの取得値の増幅倍率 public const float AccelerationRatio = 3f; } }
CocosSharpGameSample.Core プロジェクトの SampleGameApplicationDelegate.cs の ApplicationDidFinishLaunching メソッドの中、
デザイン解像度設定部分を定数(GameSettings.ScreenWidth, GameSettings.ScreenHeight)に置き換えます。
public override void ApplicationDidFinishLaunching(CCApplication application, CCWindow mainWindow) { // デザイン解像度設定 CCScene.SetDefaultDesignResolution(GameSettings.ScreenWidth, GameSettings.ScreenHeight, CCSceneResolutionPolicy.ShowAll); // シーンにタイトル画面の作成、セット var scene = new CCScene(mainWindow); var titleLayer = new TitleLayer(); scene.AddChild(titleLayer); // シーン開始 mainWindow.RunWithScene(scene); }
CocosSharpGameSample.Core プロジェクトの GameLayer.cs のフィールドに、自機用の CCNode player を定義します。
public class GameLayer : LayerBase { private CCLabel accelerationX; private CCLabel accelerationY; private CCLabel accelerationZ; // 自機用の CCNode private CCNode player; public GameLayer() : base() { }
GameLayer.cs に画面に自機を配置するメソッドを追加します。
自機や敵キャラなどの画面上のオブジェクトは、CCSprite を使用します。
private void AddPlayer() { this.player = new CCSprite("/Resources/Images/Character/Player/Player.png", null); this.player.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y); this.AddChild(this.player); }
GameLayer.cs に自機の位置を更新するメソッドを追加します。
加速度センサーの取得値に、増幅倍率の値 GameSettings.AccelerationRatio を乗算します。
また、画面の端に来たときにはそれ以上進まないように位置の値の上限、下限を設定します。
private void UpdatePlayer() { float accelerationX = (float)AccelerometerInfo.Default.AccelerationX * GameSettings.AccelerationRatio; float accelerationY = -(float)AccelerometerInfo.Default.AccelerationY * GameSettings.AccelerationRatio; this.player.PositionX = this.player.PositionX + accelerationX; this.player.PositionY = this.player.PositionY + accelerationY; // 画面からはみ出ないように、位置の値の上限、下限を設定します。 // 左 if (this.player.PositionX < 0 + GameSettings.PlayerWidth) { this.player.PositionX = 0 + GameSettings.PlayerWidth; } // 右 if (this.player.PositionX > GameSettings.ScreenWidth - GameSettings.PlayerWidth) { this.player.PositionX = GameSettings.ScreenWidth - GameSettings.PlayerWidth; } // 下 if (this.player.PositionY < 0 + GameSettings.PlayerHeight) { this.player.PositionY = 0 + GameSettings.PlayerHeight; } // 上 if (this.player.PositionY > GameSettings.ScreenHeight - GameSettings.PlayerHeight) { this.player.PositionY = GameSettings.ScreenHeight - GameSettings.PlayerHeight; } }
GameLayer.cs のAddedToSceneメソッド内に、自機を配置するメソッド、AddPlayerメソッド を追加します。
protected override void AddedToScene() { base.AddedToScene(); (省略) AddChild(this.accelerationZ); // 自機を配置 this.AddPlayer(); this.StartScheduling(); }
GameLayer.cs のStartSchedulingメソッド内に、自機の位置を更新するメソッド、UpdatePlayerメソッド を追加します。
0.02秒の更新頻度なのでだいたい 50fps です。
private void StartScheduling() { this.Schedule(t => this.UpdateAccelerationLabels(), 0.2f); this.Schedule(t => this.UpdatePlayer(), 0.02f); }
これで、ゲーム画面の中で自機が動くようになりました。
今回はここまでです。
以上までをまとめると、ソースコードは以下の通りとなります。
CocosSharpGameSample.iOS プロジェクトの 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) { // 横向きなのでX,Yを入れ替え // X,Yの感度を調整するためセンサーのX,Y軸の取得値を調整 AccelerometerInfo.Default.AccelerationX = data.Acceleration.Y * 8d; // 角度のオフセット double angleY = Math.Atan2(data.Acceleration.Z, data.Acceleration.X); AccelerometerInfo.Default.AccelerationY = Math.Sin(angleY - 30.6d) * 6d; 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; } } }
CocosSharpGameSample.Android プロジェクトの MainActivity.cs
using System; using Android.App; using Android.Content; using Android.Content.PM; using Android.Hardware; using Android.OS; using CocosSharp; using CocosSharpGameSample.Core; using Microsoft.Xna.Framework; namespace CocosSharpGameSample.Android { [Activity(Label = "CocosSharpGameSample.Android" , MainLauncher = true , Icon = "@drawable/icon" , AlwaysRetainTaskState = true , LaunchMode = LaunchMode.SingleInstance , ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden )] public class MainActivity : AndroidGameActivity, ISensorEventListener { // センサーマネージャ private SensorManager sensorManager; // ロックキー private object accelerometerSyncLock = new object(); protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // センサーのセットアップ this.sensorManager = (SensorManager)GetSystemService(Context.SensorService); // ゲームを起動する。 var application = new CCApplication(); application.ApplicationDelegate = new SampleGameApplicationDelegate(); SetContentView(application.AndroidContentView); application.StartGame(); } // 画面の向きを Landscape に固定 public override ScreenOrientation RequestedOrientation { get { return ScreenOrientation.Landscape; } set { base.RequestedOrientation = ScreenOrientation.Landscape; } } // センサーの精度が変更されたときのハンドラ public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy) { // 何もしない } // センサーの値が変更されたときのハンドラ public void OnSensorChanged(SensorEvent e) { lock (this.accelerometerSyncLock) { // 横向きなのでX,Yを入れ替え // X,Yの感度を調整するためセンサーのX,Y軸の取得値を調整 AccelerometerInfo.Default.AccelerationX = e.Values[1] * 1.5f; // 角度のオフセット double angleY = Math.Atan2(e.Values[2], e.Values[0]); AccelerometerInfo.Default.AccelerationY = Math.Sin(angleY - 29.1d) * 12f; AccelerometerInfo.Default.AccelerationZ = e.Values[2]; } } protected override void OnResume() { base.OnResume(); this.sensorManager.RegisterListener(this, sensorManager.GetDefaultSensor(SensorType.Accelerometer), SensorDelay.Game); } protected override void OnPause() { base.OnPause(); this.sensorManager.UnregisterListener(this); } } }
CocosSharpGameSample.Core プロジェクトの GameLayer.cs
using System; using System.Diagnostics; using CocosSharp; namespace CocosSharpGameSample.Core { public class GameLayer : LayerBase { private CCLabel accelerationX; private CCLabel accelerationY; private CCLabel accelerationZ; private CCNode player; 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.AddPlayer(); this.StartScheduling(); } private void StartScheduling() { this.Schedule(t => this.UpdateAccelerationLabels(), 0.2f); this.Schedule(t => this.UpdatePlayer(), 0.02f); } 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"); } private void AddPlayer() { this.player = new CCSprite("/Resources/Images/Character/Player/Player.png", null); this.player.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y); this.AddChild(this.player); } private void UpdatePlayer() { float accelerationX = (float)AccelerometerInfo.Default.AccelerationX * GameSettings.AccelerationRatio; float accelerationY = -(float)AccelerometerInfo.Default.AccelerationY * GameSettings.AccelerationRatio; this.player.PositionX = this.player.PositionX + accelerationX; this.player.PositionY = this.player.PositionY + accelerationY; // 画面からはみ出ないように、位置の値の上限、下限を設定します。 // 左 if (this.player.PositionX < 0 + GameSettings.PlayerWidth) { this.player.PositionX = 0 + GameSettings.PlayerWidth; } // 右 if (this.player.PositionX > GameSettings.ScreenWidth - GameSettings.PlayerWidth) { this.player.PositionX = GameSettings.ScreenWidth - GameSettings.PlayerWidth; } // 下 if (this.player.PositionY < 0 + GameSettings.PlayerHeight) { this.player.PositionY = 0 + GameSettings.PlayerHeight; } // 上 if (this.player.PositionY > GameSettings.ScreenHeight - GameSettings.PlayerHeight) { this.player.PositionY = GameSettings.ScreenHeight - GameSettings.PlayerHeight; } } } }
以上です。