[iOS][cocos2d]UIRotationGestureRecognizerを使ってローテーションジェスチャを検出する

cocos2dでスプライトをタップしてD&Dで移動するなどはCCStandardTouchDelegateを実装して、

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

で記述すれは、難なくできる。スプライトの移動制限(座標演算)はここに記述すればいい。 ジェスチャー(スワイプ・ローテーション)を実装する場合も、ジェスチャ検出コードを記述する方法 があり、また有志の方々でクラス化されています。実際、ジェスチャのコードを書くのは、とってもめんどくさい。というわけで

http://www.supersuraccoon-cocos2d.com/ja/2012/11/14/introduction-to-some-great-ios-gesture-recognition-libraries-cocos2d/

上のサイトをみてみると、CCStandardTouchDelegateとUIGestureRecognizerでのジェスチャーの実装があり、 今回は、ローテーションを検出するためにUIGestureRecognizerのサブクラスのUIRotationGestureRecognizerを使った検出をしてみた。 ジェスチャーの検出自体は勝手にやってくれる。イニシャライザかなんかで下のコードで実装して、ローテーション時にselectorが呼ばれるので、そこで処理を行う。

        UIRotationGestureRecognizer* rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotationGesture:)];
        [[[CCDirector sharedDirector] view] addGestureRecognizer:rotationGesture];
- (void) handleRotationGesture:(UIRotationGestureRecognizer*) sender {
    
    UIRotationGestureRecognizer* rotation = (UIRotationGestureRecognizer*) sender;
    float dgree = rotation.rotation * 180.0f / M_PI;
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    
    if(rotation.state==UIGestureRecognizerStateBegan){
        //回転対象のスプライトの特定
        //moji_rotate = nil;
        if([rotation numberOfTouches]==2){ //二本指でタップされているとき
            CGPoint fin1_pos = [rotation locationOfTouch:0 inView:[[CCDirector sharedDirector] view]];
            CGPoint fin2_pos = [rotation locationOfTouch:1 inView:[[CCDirector sharedDirector] view]];
            //Y軸の反転
            fin1_pos.y = screenSize.height - fin1_pos.y;
            fin2_pos.y = screenSize.height - fin2_pos.y;
            
            int z = -1;
            for(int i=0;i<[chars count];i++){
                CharPanel *moji_buf = [chars objectAtIndex:i];
                float h_value = moji_buf.boundingBox.size.height;
                float w_value = moji_buf.boundingBox.size.width;
                float dist = sqrtf(pow(h_value,2)+pow(w_value,2))/2 + 0; //スプライト中心からの許容距離を求める
                
                float fin1_dist = [self getdistance:fin1_pos:moji_buf.position];
                float fin2_dist = [self getdistance:fin2_pos:moji_buf.position];
                
                if((fin1_dist<dist)&&(fin2_dist<dist)){
                    if(moji_buf.zOrder>z){  //一番上のスプライトを対象にする
                        z = moji_buf.zOrder;
                        moji_rotate = [chars objectAtIndex:i];
                    }
                }
            }
        }
    
    }else if(rotation.state==UIGestureRecognizerStateEnded){
        
        if(moji_rotate!=nil){
            [moji_rotate setDgree:[moji_rotate getDgree] + dgree];
            moji_rotate = nil;
        }
    
    }else{
        
        if(moji_rotate!=nil){
            moji_rotate.rotation = [moji_rotate getDgree] + dgree;
        }
    }
}

 冗長なコードで申し訳ないですが、(NSMutablearray*)charsにはスプライト(CCSpriteのサブクラス)が配列として入っています。

 ジェスチャーの状態はUIRotationGestureRecognizerのstateで検出します。まずはUIGestureRecognizerStateBeganにて開始部からです  

ここでローテーションならば2本の指が必須とは思うのですが、状態を追ってみるとnumberOfTouchesが1を返すときも多々あります。 このため2以外は無視します。2つの指のタッチ座標を記録します。このとき、CGとUIVIewの座標の違いから取得座標のY軸が反転することを 忘れないでください。つまりはCG座標(cocos2d)に変換します。  ここからの処理は直接ジェスチャーと関係ないですが、ちなみにcharsのスプライトは、ほぼ正方形前提とします。chars格納のスプライトの対角線の 1/2を半径とする円の中に2つの指の座標が含まれていれば対象スプライトと判断します。また、charsから複数検出されるばあいは、重なっていると 判断し、z値を参考に一番上のスプライトを対象スプライトと判断します。対象スプライトはmoji_rotaionに入ります。  

あとはスプライトのrotationにどのくらい回転させたかをUIRotationGestureRecognizerのrotationより代入すればよいです。ちなみに、 スプライトのrotationは度数法でUIRotationGestureRecognizerは弧度法です。ここでは度数法に変換しています。  

上のコードではスプライト自体がどれだけ回転しているかを保持させる必要があったため、UIGestureRecognizerStateEndedで、CCSpriteの サブクラスにてメンバ変数rotationにて回転角度を保持させています。

また、ジェスチャーが終わったと判断するためmoji_rotationにはnilを入れます。  あとはstate判断のelesで実際、ローテーションさせているときに角度を代入するだけです。

 rotationだらけのコードで、そーとーわかりにくいです。。別の名前にしろよ!と言われそうですが・・・が時間もなく…御察しくださいw

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください