【Swift】SpriteKitの使い方。ノードとノードの接触を検知する。(Swift 2.2、XCode 7.3)

2020年6月16日

ノードの接触を検知する

本記事ではノードの接触検知について説明する。

ビームで障害物を破壊したり、キャラクターを動かしてアイテムを取ったりなど、ノードが別のノードに接触したことを検知して処理したいことがある。そんなときはSKPhysicsContactDelegateを利用する。

岩を破壊する

 

実際にやってみよう。

以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる方はご利用下さい。
⇒「テスト用プロジェクト

3匹の鳥ノードを配置し、茶鳥をドラッグで移動できるようにしておいた。この段階では接触は検知されない。

まず、TestScene.swiftを以下のコードに変更する(変更箇所は青色網掛け)。SKPhysicsContactDelegateプロトコルを適用、デリゲート先を自分に設定している。

茶鳥ノードのcontactTestBitMaskプロパティ(以下、接触マスク)に赤鳥のカテゴリマスクを設定している。この設定は、茶鳥の物理ボディが赤鳥の物理ボディと触れたときにデリゲートメソッドが呼ばれることを意味している。

カテゴリマスクについては次の記事を参照されたし。⇒「カテゴリマスクとは

なんで接触マスクの名前がcontactBitMaskではなくcontactTestBitMaskなのだろうか。。

 

SKPhysicsContactDelegateのデリゲートメソッドは接触開始時と終了時に呼び出される2つのメソッドしかない。両方とも試してみよう。

TestScene.swiftに以下のメソッドを追加する。接触開始時と終了時にラベルの文字列を変更している。

 

以下は実際のプレイ動画。

茶鳥で赤鳥を運んでいる最中に「接触開始」と「接触終了」が連続で呼び出された。また、開始と終了が必ず交互に呼び出されるのではなく、「接続開始、接続開始」のように同じものが連続で呼び出されることもある。

 

衝突させずに検知する

衝突しないノード同士でも物理ボディが重なることで接触を検知できる。やってみよう。

didMoveToViewメソッドに以下のコードを追加する。茶鳥と赤鳥の衝突マスクに青鳥のカテゴリマスクを設定し、茶鳥と赤鳥は衝突しないようにした。

 

以下は実際のプレイ動画。茶鳥と赤鳥が重なったときも「接触開始」、「接触終了」が呼び出されている。しかし、重なっている状態でも開始、終了が連続で呼び出されるのが気になる。

 

重なり開始、終了時の1回ずつ通知されるようにする

「上記を読むと、このデリゲートメソッドではノードがどこかの領域の中に入った、抜けたの検知はできないということなのだろうか。。」

と思ってしまうが、物理ボディの形状を四角形や円形にすれば、重なり始めたときに「接続開始」、重なりが終わったときに「接続終了」が呼び出されるようになる。

やってみよう。didMoveToViewメソッドの物理ボディを設定する箇所を以下のコードに変更する。

 

以下は実際のプレイ動画。デリゲートメソッドが呼び出されるタイミングが、重なり開始時と終了時の2回だけになった。

ただし、物理ボディに四角形や円形を使った場合はノードの接触位置がリアルではなくなる。

 

注意点

物理ボディのdynamicプロパティをfalseにするとデリゲートメソッドが呼び出されなくなるので注意すること。

stackoverflow参照⇒「SKPhysicsContactDelegate Not Working

 

接触したノードを調べる

デリゲートメソッドの引数のPhysicsContactインスタンスには、接触した2つの物理ボディがbodyA、bodyBのプロパティで渡ってくる。

そこで本記事の最後は、デリゲートメソッドが呼び出されたら接触したノードを調べ、赤鳥なら10点、青鳥なら20点が入り、鳥が大きくなる単純なゲームを作った。

 

以下は実際のプレイ動画