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 0…5 {
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.0 – 10.0
let z = Float(i / 3) * 10.0 – 5.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 0…20 {
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 0…1 {
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 “”
}
}
}