【Swift】SpriteKitの使い方。ゴムで引っ張ってボールを飛ばすような動きを作る。(Swift 2.2、XCode 7.3)
物理ボディを飛ばす
本記事ではボールを引っ張って飛ばす動きの実装方法について説明する。なお、実装サンプルに出てくるゴムは反発力を持っているのではなく、雰囲気を出すための演出である。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる方はご利用下さい。⇒「テスト用プロジェクト」
事前準備では、ボールを配置して指で運べるようにしておいた。
ゴムっぽいものを配置する。
まずはシーンに配置してある2つの青箱を線(以下、ゴム)で結び、ボールをクロスさせるとゴムがボールに引っ張られてついてくるような演出を実装する。
TestScene.swiftを以下のコードに変更する。ボールが青箱よりも上にいるときは青箱同士をゴムでつなげる。一方、ボールが青箱より下にいるときは青箱とボールをゴムでつなげている。
ゴムとボールを常にくっつかせるために描画処理はupdateメソッドに実装している。updateメソッドとはシーン表示後に1フレームごとに呼び出されるメソッドである。
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 |
// // TestScene.swift // import Foundation import SpriteKit class TestScene:SKScene { var bowl:SKSpriteNode! var leftPoint:SKSpriteNode! var rightPoint:SKSpriteNode! var line:SKShapeNode! var startPoint:(CGFloat, CGFloat) = (0.0, 0.0) //現在シーン設定時の呼び出しメソッド override func didMoveToView(view: SKView) { //シーンを画面サイズに合わせる。 self.scaleMode = .AspectFit //画面端に物理ボディを設定する。 self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame) //ノードを取得する。 leftPoint = self.childNodeWithName("left_point") as? SKSpriteNode rightPoint = self.childNodeWithName("right_point") as? SKSpriteNode bowl = self.childNodeWithName("bowl_black") as? SKSpriteNode //図形ノードを作成する。 line = SKShapeNode() line.strokeColor = SKColor.grayColor() line.lineWidth = 5 //図形ノードを子ノードに追加する。 self.addChild(line) } //画面タッチ移動時の呼び出しメソッド override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { //タッチした座標を取得する。 let location = touches.first!.locationInNode(self) //タッチした位置まで移動するアクションを作成する。 let action = SKAction.moveTo(CGPoint(x:location.x, y:location.y + 20), duration:0.1) //アクションを実行する。 bowl.runAction(action) } //1フレームごとの呼び出しメソッド override func update(currentTime: NSTimeInterval) { //パスを作成する。 let path = CGPathCreateMutable() //パスの開始点を左青箱の座標にする。 CGPathMoveToPoint(path, nil, leftPoint!.position.x, leftPoint!.position.y) if(bowl!.position.y > leftPoint.position.y) { //ボールが青箱よりも上にあるときは青箱同士を直線で結ぶ。 CGPathAddLineToPoint(path, nil, rightPoint!.position.x, rightPoint!.position.y) } else { //ボール左までの線を追加する。 CGPathAddLineToPoint(path, nil, bowl.position.x - bowl.frame.size.width/2, bowl!.position.y) //ボール下半分を円で囲む。 CGPathAddArc(path, nil, bowl.position.x , bowl!.position.y, bowl.frame.size.width/2, CGFloat(M_PI), 0, false); //ボール右から右青箱への線を追加する。 CGPathMoveToPoint(path, nil, bowl.position.x + bowl.frame.size.width/2, bowl!.position.y) CGPathAddLineToPoint(path, nil, rightPoint.position.x, rightPoint.position.y) } //線の描画を更新する。 line.path = path } } |
以下は実際のプレイ動画。
ボールを飛ばす
次に、ボールを引っ張って離すと飛んでいく動きを実装する。実装イメージは以下のようになる。
ボールをドラッグして、ボールとゴムが接触した座標を保存する(以下、スタートポイント)。
ボールを移動してゴムを引っ張ったあとに、ボールを離した座標を保存する(以下、エンドポイント)。
ボールをスタートボタンまで移動し、到着したらスタートポイントとエンドポイントから求められるベクトル方向の速度をつける。その際、スタートポイントとエンドポイントの距離が長いほど速度を大きくする。
TestScene.swiftのtouchesMovedメソッドに以下のメソッドを追加する。タッチ開始時にボールの速度を0にし、タッチ終了時にボールに速度をつけている。
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 |
//画面タッチ開始時の呼び出しメソッド override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { //ボールの速度を0にする。 bowl.physicsBody?.velocity = CGVectorMake(0, 0) } //画面タッチ終了時の呼び出しメソッド override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { //タッチした座標を取得する。 let location = touches.first!.locationInNode(self) if(location.y < leftPoint.position.y) { //最初にゴムに触れた座標まで移動するアクションを作成する。 let action = SKAction.moveTo(CGPoint(x:startPoint.0, y:startPoint.1), duration:0.5) //アクションを実行する。 bowl.runAction(action,completion: { //ボールに速度をつける。 self.bowl.physicsBody?.velocity = CGVectorMake((self.startPoint.0 - location.x)*2, (self.startPoint.1 - location.y)*2 ) self.startPoint = (0, 0) }) } } |
TestScene.swiftのupdateメソッドに以下のコードを追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//1フレームごとの呼び出しメソッド override func update(currentTime: NSTimeInterval) { ‥省略‥ //ボールがゴムに触れた座標を保存する。 if(bowl.position.y <= leftPoint.position.y && startPoint == (0.0, 0.0)) { startPoint = (bowl.position.x, bowl.position.y) } else if (bowl.position.y > leftPoint.position.y) { startPoint = (0.0, 0.0) } } |
以下は実際のプレイ動画