前回、SceneKitの3次元世界がどの様に構築されているかを確認しました。
今回は、SceneKitの世界に重力や、あたり判定などの物理シュミレーションを設定していきたいと思います。
球体に重力を設定する
ノードに設定するプロパティのうち、Geometryがカタチや表面の色を、物理情報はPhysicsBodyが担当しています。
ここで球体ノードにPhysicsBodyを設定していきます。
PhysicsBodyで物理情報を設定
PhysicsBodyの種類は下記3つ。
- Static : 静止物
- Dynamic : 動く
- Kinematic : 単体だと動かないけど、他の物体の影響で動く
今回はDynamicを選択します。
その他の物理情報のパラメータも設定していきます。
- Restitution: 衝突時のエネルギーの増減 (反発)
- Friction: 摩擦の値を設定。摩擦0だと、滑り続ける。
- PhysicsShape: 物理シュミレーション時の物体のカタチ (衝突時の当たり判定など)
詳細は公式リファレンスを参照
https://developer.apple.com/documentation/scenekit/scnphysicsbody
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | func generateBall() { //ジオメトリを設定 let ball: SCNGeometry = SCNSphere(radius: 0.4) ball.firstMaterial?.diffuse.contents = [UIColor.red, UIColor.blue, UIColor.white].shuffled().first //Body情報を設定 let physicsShape = SCNPhysicsShape(geometry: ball, options: nil) let ballBody = SCNPhysicsBody(type: .dynamic, shape: nil) ballBody.restitution = 1.0 ballBody.physicsShape = physicsShape //ノードを設定 let ballNode = SCNNode(geometry: ball) ballNode.position.x = Float.random(in: -5 ... 5) ballNode.position.y = 10 //Body情報をノードにセット ballNode.physicsBody = ballBody scene.rootNode.addChildNode(ballNode) } |
床を固定する
球体を受け止める床も設定。
床は動かさないので、PhysicsBodyはStaticにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | func setFloor() { //ジオメトリを設定 let floor: SCNFloor = SCNFloor() floor.reflectivity = 0.1 //Body情報を設定 let floorShape = SCNPhysicsShape(geometry: floor, options: nil) let floorBody = SCNPhysicsBody(type: .static, shape: floorShape) //ノードを設定 let floorNode = SCNNode(geometry: floor) //Body情報をノードにセット floorNode.physicsBody = floorBody scene.rootNode.addChildNode(floorNode) } |
全体のコード
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 | import UIKit import SceneKit class ViewController: UIViewController { @IBOutlet weak var sceneView: SCNView! var scene: SCNScene! override func viewDidLoad() { super.viewDidLoad() scene = SCNScene() self.sceneView.scene = scene sceneView.autoenablesDefaultLighting = true setCamera() setFloor() setTimer() } @objc func update(tm: Timer) { generateBall() } func setTimer() { let timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.update), userInfo: nil, repeats: true) timer.fire() } func setFloor() { let floor: SCNFloor = SCNFloor() floor.reflectivity = 0.1 let floorShape = SCNPhysicsShape(geometry: floor, options: nil) let floorBody = SCNPhysicsBody(type: .static, shape: floorShape) let floorNode = SCNNode(geometry: floor) floorNode.physicsBody = floorBody scene.rootNode.addChildNode(floorNode) } func setCamera() { let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 1, z: 20) scene.rootNode.addChildNode(cameraNode) } func generateBall() { let ball: SCNGeometry = SCNSphere(radius: 0.4) ball.firstMaterial?.diffuse.contents = [UIColor.red, UIColor.blue, UIColor.white].shuffled().first let physicsShape = SCNPhysicsShape(geometry: ball, options: nil) let ballBody = SCNPhysicsBody(type: .dynamic, shape: nil) ballBody.restitution = 1.0 ballBody.physicsShape = physicsShape let ballNode = SCNNode(geometry: ball) ballNode.position.x = Float.random(in: -5 ... 5) ballNode.position.y = 10.0 ballNode.physicsBody = ballBody scene.rootNode.addChildNode(ballNode) } } |
実行
無事、球体ノードに重力が追加されました。
次回は、球体同士がぶつかった際のインタラクションや、音なども設定して、ゲームっぽくしていきたいと思います。