Spritekitのタッチイベントの検出と長押し及びタップ継続の検出

風船を膨らませているゲーム画面
風船を膨らませているゲーム画面

ファミコン以来ゲームはコントローラーを以て操作するのが常道でしたが iPhoneに代表されるスマートフォンの普及に連れタップやスワイプ、フリックなどで操作する必要が出来しゅったいしました。 ゲーム開発に限りませんがiPhoneに於ける開発では従って 其れ等操作の検出が必須となります。

タッチの検出

先ずタップではタッチを検出し、其の位置を検出し、 タッチの時間が閾値内であるならタップとするためタッチしている時間を検出する必要があるでしょう。 当該案件に関する有用な情報を昨日2017年3月27日配信した記事 -(void)update:(CFTimeInterval)currentTimeメソッドに於ける任意時間のコールバックメソッド でも参考にしたiPhone及びiOSゲームアプリ開発のチュートリアルサイト RAYWENDERLICH TUTORIALS FOR DEVELOPERS & GAMERS を参考にします。 現在Swiftをメインに SpriteKit Swift 3 Tutorial for Beginners が配信されていますが本稿では2014年4月12日の試行に於いて現在アーカイブされている Objective-Cで書かれた情報ページを参考にします。

Sprite Kit Tutorial for Beginners
スポンサーリンク
日付:2014年4月12日
開発機:MacBook Air(11-inch, Mid 2013)
MacOSバージョン:OS X 10.9.2
Xcodeバージョン:5.1
言語:Objective-C
主関連アプリ:Balloons Occupy(邦題:バルーンズ・オキュパイ、バルオキュ)

タッチ位置の検出について手前勝手にポイントとなる部分を抜き出すと以下の部分となるでしょう。

UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];

此れが -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { メソッドの冒頭に書かれており実は此のメソッドは SpriteKitに標準で用意されるものでタッチの検出は此のメソッドが良しなに計らってくれるのでした。 此処等辺りがフレームワークを利用する旨味と言えるでしょう。 当該メソッド内に上記記述すれば location にタッチの2次元座標が保持される塩梅になっています。 後は必要に応じて location.xlocation.y などとして取り出してやれば良い訳です。

円の描画

さて当時開発していたiPhoneゲームアプリ バルオキュ ではゲームの基本機能として画面をタップしている間風船を膨らませてやりたい要請があります。 従って風船を模した円を画面上に描画せねばなりません。 Spritekitに依る円の描画については以下に挙げる2サイトを参考にしました。

前者の情報ページから手前勝手にポイントとなるコードを抜き出せは以下となります。

const float r = 1adius * DISPLAY_SCALE;
SKShapeNode* node = SKShapeNode.alloc.init;
UIBezierPath* ovalPath
   = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(-r, -r, r*2, r*2)];
node.path = ovalPath.CGPath;
node.fillColor = UIColor.whiteColor;
node.lineWidth = 0;
node.position = pos;
[self addChild:node];

また同様に後者では以下をポイントと解釈しました。

float r = 25 + i * 35;
UIBezierPath *path
   = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-r, -r, 2.0*r, 2.0*r)];
SKShapeNode *circle
   = [SKShapeNode node];
circle.strokeColor
   = i ? [SKColor blueColor] : [SKColor redColor];
circle.lineWidth = 15;
circle.path = path.CGPath;
circle.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) - 100);
[self addChild:circle];

SKspriteNodeではなく SKShapeNode を使うのがポイントとなる訳です。 共に UIBezierPathbezierPathWithOvalInRect メソッドに事前に設定した半径の値を引数で渡して生成されたパスインスタンスを用いてSKShapeNodeを生成しています。 斯くて生成された円インスタンスをSKViewにaddChildで追加しているのでした。

長押しの検出

UIButton(ボタンクラス)での長押し(longTouch)実装方法

最後は長押しの検出です。 サイト モバ造の家に突然iMacがやって来た!! の2010年7月25日に配信された上記リンク先のページを参考にしました。 此れは殆どがポイント見たような情報ですが手前勝手に以下に抽出引用しました。

-(IBAction) onTouchDown:(id)sender{
// Interface Builderを使って、TouchDownイベントと結びつける。
// 1秒後にdidDetectLoungTouchメソッドが呼び出される。
// ただしその間、TouchUpInsideイベントが発生すると、下のメソッドが
// 呼ばれ、キャンセルされる。
}
-(IBAction) onTouchUpInside:(id)sender{
// Interface Builderを使って、TouchUpInside イベントと結びつける。
// 上で設定したセレクタでの実行がキャンセルされる。
}

実際の実装コード

上記より考え方としてのヒントを得た感じで 此れを参考にタッチ継続処理を独自で実装してみました。 其れが以下のコードになります。

-(id)initWithSize:(CGSize)size {  
  if (self = [super initWithSize:size]) {
    touchDetect = NO;
  }
  …
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  touchDetect = YES;
  …
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    if (touchDetect) {
      circle.xScale *= 1.005;
      circle.yScale *= 1.005;
    }
  }
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  touchDetect = NO;
}

理屈は簡単でupdateメソッドはアプリ起動中随時実行されますから 此処で風船を模した円インスタンスのスケールを少しづず上げてやりますが アプリ起動中のべつ大きくなっては困りますからフラグを立てて制御してやります。 長押し検知と言いながら何のことはないSpriteKitに標準で用意される 画面に触れた時の検出と画面から離れた時の検出メソッドを其の儘利用するだけです。 フラグ用のBOOL型変数を touchDetect として宣言しデフォルトでFailに設定、 画面にタッチされたらTrueに変更しタッチされている間、 即ちtouchDetectがTrueの間はタッチで生成した円をだんだん大きくします。 指が離されたらtouchDetectをFailに戻しフラグを倒して風船が膨張しないように制御します。 基本的には此のコードで目論見通り動いてくれました。 若し興味があってiPhoneを用意出来る向きはApp Storeの バルオキュより ダウンロードしてご覧下さい。 但しキャラクターの はなまる が風船を割りに来ますのでご注意を。

Balloons Occupy
無料:カテゴリ: ゲーム: 4+ 評価
バージョン: 3.0
リリース: 2015年1月29日
更新: 2020年9月1日
サイズ : 13 MB
互換性: iOS 8.0 以降のiPhone、iPod touch に対応。および、macOS 11.0以降とApple M1 チップを搭載したMac に対応。