SceneKit 动画

2017-12-18  本文已影响53人  牛奈奈
前言

前面几章,SCNViewscene都是使用默认情况下的。在这里,介绍另外一个创建方法,需要指定加载的资源。

func scene(options: [SCNSceneSource.LoadingOption : Any]? = nil, statusHandler: SceneKit.SCNSceneSourceStatusHandler? = nil)

*demo1


import UIKit
import SceneKit

//展示3D模型并且施加动画

class ViewController: UIViewController {
    
    fileprivate lazy var sceneView: SCNView = {
        let sceneView = SCNView()
        sceneView.allowsCameraControl = true
        sceneView.backgroundColor = UIColor.black
        return sceneView
    }()
    
    fileprivate lazy var purpleButton: UIButton = {
        let button = UIButton()
        button.frame = CGRect(x: 0, y: 0, width: 120, height: 50)
        button.setTitle("紫色", for: .normal)
        button.setTitleColor(UIColor.purple, for: .normal)
        button.backgroundColor = UIColor.white
        return button
    }()

    fileprivate lazy var blackButton: UIButton = {
        let button = UIButton()
        button.frame = CGRect(x: 0, y: 0, width: 120, height: 50)
        button.setTitle("黑色", for: .normal)
        button.setTitleColor(UIColor.red, for: .normal)
        button.backgroundColor = UIColor.white
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView.frame = view.bounds
        let url = Bundle.main.url(forResource: "skinning", withExtension: "dae")
        // 强制解包,实际应用中不能这么处理
        let sceneSource = SCNSceneSource(url: url!, options: nil)
        // 指定资源
        sceneView.scene = sceneSource?.scene(options: nil)
        view.addSubview(sceneView)
        
        // 动画
        if let animalIDs = sceneSource?.identifiersOfEntries(withClass: CAAnimation.self){
            var array:[CAAnimation] = []
            var maxDuration: Float = 0
            for i in 0..<animalIDs.count {
                let animation = sceneSource?.entryWithIdentifier(animalIDs[i], withClass: CAAnimation.self)
                array.append(animation!)
                maxDuration = max(maxDuration, Float(animation!.duration))
            }
            let longAnimalGroup = CAAnimationGroup()
            longAnimalGroup.animations = array
            longAnimalGroup.duration = CFTimeInterval(maxDuration)
           
            let lastAnimalGroup = CAAnimationGroup()
            lastAnimalGroup.timeOffset = 20
            lastAnimalGroup.duration = CFTimeInterval(maxDuration)
            lastAnimalGroup.repeatCount = MAXFLOAT
            lastAnimalGroup.autoreverses = true
            
            //指定某一个节点的动作重复
            let personNode = sceneView.scene?.rootNode.childNode(withName: "avatar_attach", recursively: true)
            
            personNode?.addAnimation(lastAnimalGroup, forKey: "animation")
            
         
        }
        
        purpleButton.center = CGPoint(x: view.center.x, y: view.center.y + 140)
        blackButton.center = CGPoint(x: view.center.x, y: view.center.y + 200)
        view.addSubview(purpleButton)
        view.addSubview(blackButton)
        purpleButton.addTarget(self, action: #selector(purpleClick), for: .touchUpInside)
        blackButton.addTarget(self, action: #selector(blackClick), for: .touchUpInside)
    }
    
    @objc func purpleClick(){
        // 更换衣服节点处的内容
        let shirtNode = sceneView.scene?.rootNode.childNode(withName: "shirt", recursively: true)
        shirtNode?.geometry?.firstMaterial?.diffuse.contents = "export_0_texture19.png"
        
    }
    @objc func blackClick() {
        let shirtNode = sceneView.scene?.rootNode.childNode(withName: "shirt", recursively: true)
        shirtNode?.geometry?.firstMaterial?.diffuse.contents = "export_0_texture22.png"
    }
}


运行效果如下:


动画.gif

*demo2

import UIKit
import SceneKit

class ViewController: UIViewController {
    @IBOutlet weak var scnView: SCNView!
    var sunCameraNode: SCNNode?
    var earthCameraNode: SCNNode?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        scnView.backgroundColor = UIColor.black
        scnView.scene = SCNScene()
        
        // 太阳
        let sunNode = SCNNode()
        sunNode.geometry = SCNSphere(radius: 3)
        sunNode.geometry?.firstMaterial?.diffuse.contents = "sun.jpg"
        scnView.scene?.rootNode.addChildNode(sunNode)
        
        // 太阳自传
        let sunAction = SCNAction.repeatForever(SCNAction.rotate(by: 0.1, around: SCNVector3(0, 1, 0), duration: 0.5))
        sunNode.runAction(sunAction)
        
        //照相机视角1(太阳正前方)
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(0, 0, 40)
        cameraNode.camera?.automaticallyAdjustsZRange = true
        scnView.scene?.rootNode.addChildNode(cameraNode)
        //指定观察点
        scnView.pointOfView = cameraNode
        self.sunCameraNode = cameraNode
        
        //地球:直接把地球节点加在太阳上,在视角1的情况下正常。但是当切换视角2的时候,地球禁止不动,太阳也会出现,只有月球轨迹正常,所以考虑,不要直接把地球加到太阳的节点上去,创建一个过渡的节点来
        let transitionNode = SCNNode()
        transitionNode.position = SCNVector3(10, 0, 0)
        sunNode.addChildNode(transitionNode)
        
        let earthNode = SCNNode()
        earthNode.geometry = SCNSphere(radius: 1)
        earthNode.geometry?.firstMaterial?.diffuse.contents = "earth.jpg"
        earthNode.position = SCNVector3(0, 0, 0)
        transitionNode.addChildNode(earthNode)
        
//        earthNode.position = SCNVector3(10, 0, 0)
//        sunNode.addChildNode(earthNode)
        
        //地球自传+公转
        let earthAction = SCNAction.repeatForever(SCNAction.rotate(by: 0.1, around: SCNVector3(0, 1, 0), duration: 0.3))
        earthNode.runAction(earthAction)
        
        //照相机视角2(地球正前方)
        let cameraNode1 = SCNNode()
        cameraNode1.camera = SCNCamera()
        cameraNode1.position = SCNVector3(0, 0, 10)
        cameraNode1.camera?.automaticallyAdjustsZRange = true
//        earthNode.addChildNode(cameraNode1)
        transitionNode.addChildNode(cameraNode1)
        self.earthCameraNode = cameraNode1
        
        //月球
        let moonNode = SCNNode()
        moonNode.geometry = SCNSphere(radius: 0.5)
        moonNode.geometry?.firstMaterial?.diffuse.contents = "moon.jpg"
        moonNode.position = SCNVector3(2, 0, 0)
        earthNode.addChildNode(moonNode)
        
        //月球自传+公转
        let moonAction = SCNAction.repeatForever(SCNAction.rotate(by: 0.1, around: SCNVector3(0, 1, 0), duration: 0.1))
        moonNode.runAction(moonAction)
    
    }

    @IBAction func universeAction(_ sender: Any) {
        let action = SCNAction.repeatForever(SCNAction.move(to: SCNVector3(0, 0, 100), duration: 1))
        self.sunCameraNode?.runAction(action)
        //切换观察点
        self.scnView.pointOfView = self.sunCameraNode
    }
    
    @IBAction func sunAction(_ sender: Any) {
        let action = SCNAction.repeatForever(SCNAction.move(to: SCNVector3(0, 0, 40), duration: 1))
        self.sunCameraNode?.runAction(action)
        //切换观察点
        self.scnView.pointOfView = self.sunCameraNode
    }
    
    @IBAction func earthAction(_ sender: Any) {
        //切换观察点
        self.scnView.pointOfView = self.earthCameraNode
        
    }
}

运行效果如下:


太阳运动轨迹.gif

比较demo1demo2发现,demo1使用的是CAAnimation来促使节点运动,而demo2使用的是SCNAction,简单的从点到点的运动可以使用SCNAction,而稍微复杂自定义的动画使用CAAnimation

上一篇下一篇

猜你喜欢

热点阅读