cocos2dでロングプレス(タッチホールド)を検出する
今回はcocos2dでタッチホールドを検出する方法を解説します。前回解説したスケジューラの機能を使うと、とても簡単に実現できます。
プログラムの流れとしては
- タッチ開始
- カウンタを初期化
- スケジューラを開始
- 毎フレームごとに時間経過を取得し、カウンタをインクリメント
- カウンタが一定時間以上になればホールド検出処理をした後、スケジューラの終了
- タッチムーブ、タッチエンドが発生してもスケジューラを終了
のような流れでプログラムを組みます。
カウンタの準備と初期化
時間経過を蓄積していくカウンタは、グローバル変数やインスタンス変数で定義します。今回は実装部内で以下のように定義しました。
@implementation HelloWorldLayer { float holdCount; }
カウンタの初期化はタッチ開始時に毎回行われるようにしましょう。以下のようにccTouchesBeganメソッドを準備し、その中で初期化します。
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { holdCount = 0.0f; }
カウントスケジューラの設置
経過時間を蓄積し、ロングプレスかどうかを判断するスケジューラを準備します。
-(void)scheduleTouchHoldCounter:(ccTime)dlt { holdCount += dlt; if(holdCount > 3) { NSLog(@"Touch hold detected"); /** * タッチホールドが起こった時に行う処理 */ [self unschedule:_cmd]; } }
3行目で取得した時間経過をカウンタに加算し、全体のホールド時間を算出しています。そして次の条件文で一定時間以上になったかどうかを判断し、一定時間以上ならば出力を行い、自身のスケジューリングを停止するように実装しました。今回はタッチホールドと検出ための時間を3秒に設定しています。コメントの部分に好きな処理を書いてください。
スケジューラの開始と停止
スケジューラを開始するのはタッチが始まった時です。ですので、先ほどのccTouchesBegan
に開始処理を追加します。
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { holdCount = 0.0f; [self schedule:@selector(scheduleTouchHoldCounter:)]; }
初めのリストでも説明したように、スケジューラに停止メッセージを出すタイミングは3つあります。ホールドが検出された時、タッチムーブ時、タッチエンド時です。ホールドが検出された際の停止は先程実装したので、残りの2つを書きます。ccTouchesMoved
とccTouchesEnded
メソッドを作り、以下のように停止処理を書いてください。
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [self unschedule:@selector(scheduleTouchHoldCounter:)]; } -(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self unschedule:@selector(scheduleTouchHoldCounter:)]; }
以上でホールド検出のコードの設置は終了です。実行してみると、タッチ開始から3秒経った時にログが出力されると思います。
但し、実機で実行する場合は、タッチムーブの判定が厳しいので、タッチムーブの判定に遊びをもたせるような工夫が必要です。僕はよく次のような感じで書いています。
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { holdCount = 0.0f; [self schedule:@selector(scheduleTouchHoldCounter:)]; UITouch *touch = [[touches allObjects] objectAtIndex:0]; touchStartPos = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]]; } -(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [[touches allObjects] objectAtIndex:0]; CGPoint currentPos = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]]; float dist = ccpLengthSQ(ccpSub(touchStartPos, currentPos)); if(dist > 75) { [self unschedule:@selector(scheduleTouchHoldCounter:)]; } }
ccTouchesBegan
内のtouchStartPos
はholdCount
のようにインスタンス変数として持っています。そこにタッチ開始位置を保持しておき、ある程度大きく動いた時だけ、ccTouchesMoved
内で動いたという判定を行うようにしています。
とにかく、こんな感じでUIKitを使わなくてもロングプレスの検出をすることができます。