iPhone3D電車

3Dで電車っぽいのを動かしてみるiPhoneアプリのサンプルコードを描いてみます。

import UIKit

import SceneKit

class ViewController: UIViewController, SCNSceneRendererDelegate {

    weak var sceneView : SCNView?

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        setupScene()

        createRail()

        createCamera()

    }

    

    func setupScene() {

        let sv = SCNView(frame: view.bounds)

        sv.backgroundColor = UIColor(white: 0.3, alpha: 1)

        sv.scene = SCNScene()

        sv.delegate = self

        sv.autoenablesDefaultLighting = true

        view.addSubview(sv)

        

        sceneView = sv

    }

    func createRail() {

        let pathA = UIBezierPath(rect: CGRect(x: –5, y: –0.25, width: 10, height: 0.5))

        

        

        for i in 05 {

            let plate = SCNBox(width: 10, height: 2, length: 10, chamferRadius: 0)

            plate.firstMaterial?.diffuse.contents = UIColor(white: 0.1, alpha: 1)

            let plateNode = SCNNode(geometry: plate)

            let x = Float(i % 3) * 10.010.0

            let z = Float(i / 3) * 10.05.0

            plateNode.position = SCNVector3(x: x, y: 0, z: z)

            plateNode.name = “rail”

            sceneView?.scene?.rootNode.addChildNode(plateNode)

            

            let rail = SCNShape(path: pathA, extrusionDepth: 0.3)

            rail.firstMaterial?.diffuse.contents = UIColor(white: 0.8, alpha: 1)

            let railNode = SCNNode(geometry: rail)

            railNode.name = “A”

            railNode.position = SCNVector3(x: 0, y: 1.2, z: 0)

            plateNode.addChildNode(railNode)

            

            let railBNode = SCNNode()

            railBNode.name = “B”

            railBNode.pivot = SCNMatrix4Rotate(railBNode.transform, Float(M_PI) * 0.5, 1, 0, 0)

            railBNode.position = SCNVector3(x: 0, y: –1.2, z: 0)

            plateNode.addChildNode(railBNode)

            

            for i in 020 {

                let dot = SCNBox(width: 0.3, height: 0.3, length: 0.5, chamferRadius: 0.1)

                dot.firstMaterial?.diffuse.contents = UIColor(white: 0.8, alpha: 1)

                let dotNode = SCNNode(geometry: dot)

                railBNode.addChildNode(dotNode)

                

                let angle = Float(i) * Float(M_PI)/40.0

                let (x, y) = (5 * cos(angle) – 5, 5 * sin(angle) – 5)

                dotNode.position = SCNVector3(x: x, y: y, z: 0)

            }

            

            plateNode.physicsBody = SCNPhysicsBody.staticBody()

            plateNode.physicsBody?.physicsShape = SCNPhysicsShape(node: plateNode, options: nil)

        }

    }

    

    func createCamera() {

        let camera = SCNNode()

        camera.camera = SCNCamera()

        camera.position = SCNVector3(x: 0, y: 30, z: 40)

        camera.rotation = SCNVector4(x: 1, y: 0, z: 0, w: –0.5)

        sceneView?.scene?.rootNode.addChildNode(camera)

    }

    

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {

        let p = touches.anyObject()!.locationInView(sceneView)

        if let hit = sceneView?.hitTest(p, options: [SCNHitTestSortResultsKey:true])?.first as? SCNHitTestResult {

            

            if hit.node.hasActions() { return }

            

            if hit.node.name == “rail” {

                let n = hit.node.childNodeWithName(“A”, recursively: false)?.presentationNode()

                let p = n?.convertPosition(n!.position, toNode: n?.parentNode)

                if p!.y < 0 {

                    hit.node.runAction(SCNAction.sequence([SCNAction.rotateByAngle(CGFloat(M_PI), aroundAxis: SCNVector3(x: 1, y: 0, z: 0), duration: 0.5), SCNAction.rotateByAngle(CGFloat(M_PI) * 0.5, aroundAxis: SCNVector3(x: 0, y: 1, z: 0), duration: 0.5)]))

                } else {

                    hit.node.runAction(SCNAction.rotateByAngle(CGFloat(M_PI), aroundAxis: SCNVector3(x: 1, y: 0, z: 0), duration: 0.5))

                }

            }

        } else {

            let train = SCNNode()

            train.name = “train”

            train.position = SCNVector3(x: 0, y: 5, z: 5)

            sceneView?.scene?.rootNode.addChildNode(train)

            

            let body = SCNBox(width: 2, height: 0.5, length: 1, chamferRadius: 0)

            body.firstMaterial?.diffuse.contents = UIColor.yellowColor()

            let bodyNode = SCNNode(geometry: body)

            train.addChildNode(bodyNode)

            

            for i in 01 {

                let bar = SCNBox(width: 2.0, height: 0.3, length: 0.2, chamferRadius: 0)

                bar.firstMaterial?.diffuse.contents = UIColor.darkGrayColor()

                let barNode = SCNNode(geometry: bar)

                barNode.position = SCNVector3(x: 0, y: –0.8, z: (i==0) ? 0.5 : –0.5)

                train.addChildNode(barNode)

            }

            

            train.physicsBody = SCNPhysicsBody.dynamicBody()

            train.physicsBody?.physicsShape = SCNPhysicsShape(node: train, options: nil)

            train.physicsBody?.friction = 0

        }

    }

    

    func renderer(aRenderer: SCNSceneRenderer, didApplyAnimationsAtTime time: NSTimeInterval) {

        sceneView?.scene?.rootNode.childNodes

            .filter { ($0 as SCNNode).name == “train” }

            .map {

                (var n) -> String in

                    var p = n.presentationNode().convertPosition(SCNVector3(x: 3, y: 0, z: 0), toNode: n.parentNode)

                    var direction = SCNVector3(x: p.x – n.presentationNode().position.x, y: 0, z: p.z-n.presentationNode().position.z)

                    n.physicsBody!?.applyForce(direction, impulse: false)

                return “”

        }

    }

}