2017年9月1日金曜日

SKPhysicsJointFixed で結合するときのちょっとしたテクニック

概要

SKPhysicsJointFixed で 2 つの SKSpriteNode を結合するときにノードが小さかったりスピードが早かったりするとノード同士が重なったり、あまりに離れた距離で結合してしまうケースがあります (というか自分はありました)
で、そんなときに使えるテクニックを紹介します

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

2 ノード間の距離を測定して結合する

まずノードの衝突を判定するには SKPhysicsContactDelegate を継承したクラスに didBegin, didEnd というイベント用の関数を追加することで判定することができます
2 つの関数は衝突した点を管理する SKPhysicsContact を引数として扱うことができます

そしてこの SKPhysicsContact から衝突した 2 つのノード情報も取得することができます

let a = contact.bodyA
let b = contact.bodyB

bodyA が衝突されたノードで bodyB が衝突したノードになります
これら 2 つのノード間の距離を三平方の定理を利用して計測します
とその前に CGPoint を拡張して 2 ノード間の距離を測定する関数を定義します

extension CGPoint {
    func distanceFromCGPoint(point: CGPoint) -> CGFloat{
        return sqrt(pow(self.x - point.x,2) + pow(self.y - point.y,2))
    }
}

これを使って didBegin が呼び出されたときに以下のように距離を測定します

let dist = a.node?.position.distanceFromCGPoint(point: (b.node?.position)!)

そして、この計算した距離がある一定以上かつ一定以下であった場合に SKPhysicsJointFixed を使って結合すれば OK です

let distMin: CGFloat = 42.0
let distMan: CGFloat = 66.0

if dist! >= distMin && dist! <= distMax {
    let joint = SKPhysicsJointFixed.joint(withBodyA: a, bodyB: b, anchor: contact.contactPoint)
    self.physicsWorld.add(joint)
}

と自分はこんな感じで 2 ノードを結合するようにしています

最後に

SKPhysicsJointFixed で結合するときには 2 ノード間の距離を測定してから結合すると良いよというネタを紹介しました
この方法は衝突が頻繁に発生する場合には使える方法だと思います
そうではなく 1 回しか発生しない場合などはわざわざ計算しなくても良いかもしれません

また、この方法の弱点としてはノードが三角形や四角形の場合に頂点で衝突が発生した場合と辺で衝突が発生した場合に条件が一緒だということです
本当は辺と頂点で結合する条件を変更したほうがいいのですが、現状の Swift3 の物理エンジンを使った衝突判定だとそこまではできないようです

他にもテクニックはあると思うので参考程度に見てもらえると良いかなと思います

参考サイト

0 件のコメント:

コメントを投稿