美文网首页
SceneKit 动画

SceneKit 动画

作者: 牛奈奈 | 来源:发表于2017-12-18 15:41 被阅读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

    相关文章

      网友评论

          本文标题:SceneKit 动画

          本文链接:https://www.haomeiwen.com/subject/nddmwxtx.html