How do I create a looping video material in SceneKit on iOS in Swift 3?

前端 未结 3 2003
甜味超标
甜味超标 2021-02-06 08:16

How do I create a material in SceneKit that plays a looping video?

相关标签:
3条回答
  • 2021-02-06 08:35

    Year 2019 solution:

    let mat = SCNMaterial()
    let videoUrl = Bundle.main.url(forResource: "YourVideo", withExtension: "mp4")!
    let player = AVPlayer(url: videoUrl)
    mat.diffuse.contents = player
    player.actionAtItemEnd = .none
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(playerItemDidReachEnd(notification:)),
                                           name: .AVPlayerItemDidPlayToEndTime,
                                           object: player.currentItem)
    player.play()
    

    Code for method in selector:

    @objc private func playerItemDidReachEnd(notification: Notification) {
        if let playerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: .zero, completionHandler: nil)
        }
    }
    

    Don't forget to remove your notification observer when the object is deallocated! Something like this:

    NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
    
    0 讨论(0)
  • 2021-02-06 08:55

    It's possible to achieve this in SceneKit using a SpriteKit scene as the geometry's material.

    The following example will create a SpriteKit scene, add a video node to it with a video player, make the video player loop, create a SceneKit scene, add a SceneKit plane, and finally add the SpriteKit scene as the plane's diffuse material.

    import UIKit
    import SceneKit
    import SpriteKit
    import AVFoundation
    
    class ViewController: UIViewController, SCNSceneRendererDelegate {
    
        @IBOutlet weak var sceneView: SCNView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // A SpriteKit scene to contain the SpriteKit video node
            let spriteKitScene = SKScene(size: CGSize(width: sceneView.frame.width, height: sceneView.frame.height))
            spriteKitScene.scaleMode = .aspectFit
    
            // Create a video player, which will be responsible for the playback of the video material
            let videoUrl = Bundle.main.url(forResource: "videos/video", withExtension: "mp4")!
            let videoPlayer = AVPlayer(url: videoUrl)
    
            // To make the video loop
            videoPlayer.actionAtItemEnd = .none
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(ViewController.playerItemDidReachEnd),
                name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                object: videoPlayer.currentItem)
    
            // Create the SpriteKit video node, containing the video player
            let videoSpriteKitNode = SKVideoNode(avPlayer: videoPlayer)
            videoSpriteKitNode.position = CGPoint(x: spriteKitScene.size.width / 2.0, y: spriteKitScene.size.height / 2.0)
            videoSpriteKitNode.size = spriteKitScene.size
            videoSpriteKitNode.yScale = -1.0
            videoSpriteKitNode.play()
            spriteKitScene.addChild(videoSpriteKitNode)
    
            // Create the SceneKit scene
            let scene = SCNScene()
            sceneView.scene = scene
            sceneView.delegate = self
            sceneView.isPlaying = true
    
            // Create a SceneKit plane and add the SpriteKit scene as its material
            let background = SCNPlane(width: CGFloat(100), height: CGFloat(100))
            background.firstMaterial?.diffuse.contents = spriteKitScene
            let backgroundNode = SCNNode(geometry: background)
            scene.rootNode.addChildNode(backgroundNode)
    
            ...
        }
    
        // This callback will restart the video when it has reach its end
        func playerItemDidReachEnd(notification: NSNotification) {
            if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
                playerItem.seek(to: kCMTimeZero)
            }
        }
    
        ...
    }
    
    0 讨论(0)
  • 2021-02-06 08:57

    It is possible to use an AVPlayer as the content of the scene's background. However, it was not working for me until I sent .play(nil) to the sceneView.

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Set the view's delegate
        sceneView.delegate = self
    
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
    
        // Create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
    
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
    
        // Set the scene to the view
        sceneView.scene = scene
    
        let movieFileURL = Bundle.main.url(forResource: "example", withExtension: "mov")!
        let player = AVPlayer(url:movieFileURL)
        scene.background.contents = player
        sceneView.play(nil) //without this line the movie was not playing
    
        player.play()
    }
    
    0 讨论(0)
提交回复
热议问题