こんにちは、精神年齢は若手以下かもしれないponpoko1968です。 このたび、弊社ではiPhoneとiPadを連携させて、パーティなどで多人数で楽しめるゲームを開発し、見事IVS LaunchPadの第4位に輝いたのですが、似たようなスキームで、iPhoneをiPad上のリアルタイムゲームのリモートコントローラとして用いることが出来ないか試してみました。 今回は、iPhoneの加速度センサー機能を使って、デバイスの傾きを検出し、iPad上で動作するサーバに送信するアプリを作ってみました。 仕様としては、
  • 傾きで左右の方向決定
  • 4つのボタン(タッチ)で前進、後進、Aボタン、Bボタン
  • Game Kitをつかってサーバの検索と表示
  • サーバにコントローラの状態を送信してアプリをコントロール

テンプレートの生成

今回は単一画面のシンプルなアプリなので、「View based application」で作ります。 [caption id="" align="alignnone" width="677" caption="view-based application"]
view-based application


[/caption] 今回は「AnalogRemote」というアプリ名で作成します。 ドライビングゲームなど、傾き検出を用いるアプリでは、iPhoneをホームボタンが右に来るように横にして使うのが一般的らしいので、Info.plistの「initial interface orientations」という項目を「Landscape (right home button)」に設定することで、横画面で起動させます。


また、画面の向きを認識させるため下記のメソッドをオーバライドします。

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

加速度センサーによる傾きの検出

UIAccelerometerの使い方

傾きの検出には、 UIAccelerometerというクラスを用います。このクラスは、 sharedAccelerometerというクラスメソッドを用いることでシングルトンインスタンスを得ます。 UIAccelerometerはUIAccelerometerDelegateというプロトコルを通じて対象オブジェクトにデバイスの傾きを伝えるため、対象オブジェクトはUIAccelerometerDelegateプロトコルをサポートする必要があります。 今回は、唯一のビューコントローラクラスである、 AnalogRemoteViewControllerに UIAccelerometerDelegateをサポートさせます。 AnalogRemoteViewController.hを編集して、

@interface TanksControllerViewController : UIViewController<UIAccelerometerDelegate> {

...

@end
とします。 次に、AnalogRemoteViewControllerインスタンスのビューのローディングが終了したときに呼ばれるメソッドloadで、 UIAccelerometerの設定を行います。

- (void)viewDidLoad {

[super viewDidLoad];

// 0.1秒間隔で状態を通知

[[UIAccelerometer sharedAccelerometer] setUpdateInterval:0.1];

// デリゲート通知を自分(このインスタンス)に向けさせる

[[UIAccelerometer sharedAccelerometer] setDelegate:self];

...

}
デリゲート通知を設定されたクラスは、下記のメソッド

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
を実装することで、setUpdateIntervalで設定された時間間隔で、加速度センサーの状態が通知されます。

デバイスの傾きを検出する

加速度センサーはX,Y,Zの3軸をもちいてデバイスの状態を検知します。


下図のように、iPhoneを横に向けた状態では、地球から作用する重力、つまり下向きの力が作用するため、X軸が約−1の値を示します。


横に向けて、さらに傾けると、下図のように、X軸とY軸の両方に力が分散されます。傾きによって、X軸とY軸にかかる力の配分が変わるため、iPhoneの傾きを検出することができます。例えると、iPhoneの重心から重しを付けた糸を垂らしているイメージを思い浮かべて頂けるとわかりやすいかと思います。糸とiPhoneの角度を調べれば、iPhoneの傾きが求まると言うわけです。


加速度センサーの状態を表示する

デバッグ用に加速度センサーの状態をリアルタイムで見るために、画面に表示するようにします。 AnalogRemoteViewControllerのメンバにx,y,z軸の数値を表示するためのUILabelを追加します。

@interface AnalogRemoteViewController : UIViewController<UIAccelerometerDelegate> {

UILabel* x;

UILabel* y;

UILabel* z;

...

}

// Interface Builder連携のためプロパティとして定義

@property(nonatomic, retain) IBOutlet  UILabel* x;

@property(nonatomic, retain) IBOutlet  UILabel* y;

@property(nonatomic, retain) IBOutlet  UILabel* z;

...

@end
プロパティx,y,zのセッター・ゲッターを定義するため、 AnalogRemoteViewController.mに、下記のように追加します。

@implementation AnalogRemoteViewController

@synthesize x,y,z;
interface builderでビューにラベルを貼り付けて、UILabelと結びつけます。 デリゲートメソッド - (void) accelerometer:didAccelerate:を実装します。

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {

x.text  = [NSString stringWithFormat:@"%10.2f",acceleration.x];

y.text  = [NSString stringWithFormat:@"%10.2f",acceleration.y];

z.text  = [NSString stringWithFormat:@"%10.2f",acceleration.z];

}

X軸とY軸の逆正接を求め、角度を割り出します。


  float _angle;

  if( fabs(acceleration.x) > fabs(acceleration.z)){
    _angle = atan2(acceleration.y,-acceleration.x);

  }else {
    _angle = atan2(acceleration.y,-acceleration.z);
  }

※今回のコードでは、iPhoneがY軸を中心として一定以上地面に水平に傾いている場合には、X軸とZ軸の角度を求めるようにしています。 これで、傾きを検出することが出来ました。次回は他のボタン操作の検知と、サーバ(iPad)との通信について説明します。 サンプルをダウンロード [続く]