【Swift】SpriteKitの使い方。2つの物理ボディをバネやスライドで結合する。(Swift 3.0、XCode 8.0)
SKPhysicsJointの続き
前回記事で2つの物理ボディをピンやヒモで結合する方法を説明した。⇒「記事」
本記事はその続きで、今度はバネやスライドで物理ボディを結合する方法を説明する。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる方はご利用下さい(前回記事と同じプロジェクトです)。⇒「テスト用プロジェクト」
事前準備では2本の丸太をシーンに配置し、ドラッグで運べるようにしておいた。
ゴムで結合する (SKPhysicsJointSpring)
2つの物理ボディをバネで結んだようにする。
TestScene.swiftのdidMoveメソッドに以下のコードを追加する。バネが伸縮するように物理ボディを動かすにはfrequencyプロパティに値を設定する必要がある。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//現在シーン設定時の呼び出しメソッド override func didMove(to view: SKView) { ‥省略‥ //物理ジョイントを作成する。 let joint = SKPhysicsJointSpring.joint(withBodyA: maruta1.physicsBody!, bodyB: maruta2.physicsBody!, anchorA: CGPoint(x: maruta1.frame.maxX, y: maruta1.frame.midY), anchorB: CGPoint(x: maruta2.frame.minX, y: maruta2.frame.midY)) //伸縮の周期を設定する。 joint.frequency = 1.0; //伸縮の減衰を設定する。 joint.damping = 0.5; //物理ジョイントを追加する。 self.physicsWorld.add(joint) } |
以下は実際のプレイ動画。丸太をドラッグすると、ドラッグしている丸太の周りを別の丸太が振幅する。ドラッグをやめると振幅は徐々に弱まり停止する。
先ほどの結果ではドラッグ中の丸太も回転してしまうのが気になる。
そこで、ドラッグ中の丸太は物理エンジンの影響を受けないように変更しよう。ついでに、バネの様子をわかりやすくするために丸太と丸太の間に線を引く。
TestScene.swiftを以下のコードに変更する(変更箇所は青色)。ドラッグしている丸太が物理エンジンの影響を受けないようにするために、画面タッチ時に丸太のdynamicプロパティをfalseにし、画面タッチ終了時にtrueに戻している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
// // TestScene.swift // import Foundation import SpriteKit class TestScene:SKScene { var maruta1:SKSpriteNode! var maruta2:SKSpriteNode! var target:SKSpriteNode! var line:SKShapeNode! //現在シーン設定時の呼び出しメソッド override func didMove(to view: SKView) { //SKSファイルに配置した丸太ノードを取得する。 maruta1 = self.childNode(withName: "maruta1") as? SKSpriteNode maruta2 = self.childNode(withName: "maruta2") as? SKSpriteNode //丸太をターゲットノードに設定する target = maruta1 //物理ジョイントを作成する。 let joint = SKPhysicsJointSpring.joint(withBodyA: maruta1.physicsBody!, bodyB: maruta2.physicsBody!, anchorA: CGPoint(x: maruta1.frame.maxX, y: maruta1.frame.midY), anchorB: CGPoint(x: maruta2.frame.minX, y: maruta2.frame.midY)) //伸縮の周期を設定する。 joint.frequency = 1.0; //伸縮の減衰を設定する。 joint.damping = 0.5; //物理ジョイントを追加する。 self.physicsWorld.add(joint) //図形ノードを作成する。 line = SKShapeNode() line.strokeColor = SKColor.gray line.lineWidth = 5 //図形ノードを子ノードに追加する。 self.addChild(line) } //画面タッチ開始時の呼び出しメソッド override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { //タッチしたノードを取得する。 let location = touches.first!.location(in: self) if let node = atPoint(location) as? SKSpriteNode { //タッチしたノードが丸太の場合、ターゲットノードに設定する。 if(node == maruta1 || node == maruta2){ target = node //物理エンジンの力を受けないようにする。 node.physicsBody?.isDynamic = false } } } //画面タッチ移動時の呼び出しメソッド override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { //タッチした座標を取得する。 let location = touches.first!.location(in: self) //ターゲットノードをタッチした座標まで移動するアクションを実行する。 let action = SKAction.move(to: CGPoint(x:location.x, y:location.y), duration:0.1) target.run(action) } //画面タッチ終了時の呼び出しメソッド override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { //物理エンジンの力を受けるようにする。 target.physicsBody?.isDynamic = true } //1フレームごとの呼び出しメソッド override func update(_ currentTime: TimeInterval) { //パスを作成する。 let path = CGMutablePath() //丸太に配置した空ノードを取得する。 let linePoint1 = self.childNode(withName: "//linepoint1") let linePoint2 = self.childNode(withName: "//linepoint2") //空ノードの座標をシーンを基準にしたものに変換する。 let position1 = convert(self.position, from: linePoint1!) let position2 = convert(self.position, from: linePoint2!) //線を引くパスを設定する。 path.move(to: CGPoint(x: position1.x, y: position1.y)) path.addLine(to: CGPoint(x: position2.x, y: position2.y)) //線の描画を更新する。 line.path = path } } |
以下は実際のプレイ動画。ゴムで結んだヌンチャクみたいだ。
スライドさせる (SKPhysicsJointSliding)
ついてくる物理ボディを一定の角度で移動する結合方法。この結合が一番イメージしづらい。
TestScene.swiftのdidMoveメソッドに以下のコードを追加する。
ドラッグしたときについてくる丸太は45度の方向に移動するようにした。また、物理ボディ間の距離が50ポイント以上離れないようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//現在シーン設定時の呼び出しメソッド override func didMove(to view: SKView) { ‥省略‥ //物理ジョイントを作成する。 let joint = SKPhysicsJointSliding.joint(withBodyA: maruta1.physicsBody!, bodyB: maruta2.physicsBody!, anchor: CGPoint(x: maruta1.frame.maxX, y: maruta1.frame.midY), axis:CGVector(dx: 1.0, dy: 1.0)); //物理ボディ間の最小距離は制限しない。 joint.lowerDistanceLimit = 0 //物理ボディ間の最大距離を制限する。 joint.upperDistanceLimit = 50 //制限を有効にする。 joint.shouldEnableLimits = true //物理ジョイントを追加する。 self.physicsWorld.add(joint) ‥省略‥ } |
以下は実際のプレイ動画
結合を削除する
追加した物理ジョイントを削除するには以下のメソッドを用いる。
1 2 3 4 5 6 |
//物理ジョイントを指定して削除する場合 self.physicsWorld.remove(joint) //すべての物理ジョイントを削除する場合 self.physicsWorld.removeAllJoints() |