You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpriteKit场景播放本地.mov视频问题求助

SpriteKit本地MOV视频播放与场景切换集成方案

问题背景

正在开发选择式冒险儿童绘本,需整合23个.mov短动画。已实现带切换按钮的静态场景,但SKVideoNode加载本地视频失败,不确定是否需额外文件关联;同时纠结代码实现 vs .sks/故事板的效率。目前场景切换框架可用,希望进入场景时自动播放对应视频,且发现.mov无法放入Assets.xcassets。

现有代码片段

Apple官方SKVideoNode示例

let sample = SKVideoNode(fileNamed: "sample.mov")
sample.position = CGPoint(x: frame.midX,
                      y: frame.midY)
addChild(sample)
sample.play()

自定义GameScene代码

import SpriteKit
import GameplayKit

class GameScene: SKScene {

    private var label : SKVideoNode?

    override func didMove(to view: SKView) {
        let sample = SKVideoNode(fileNamed: "V1.mov")
        sample.position = CGPoint(x: frame.midX,
                                  y: frame.midY)
        addChild(sample)
        sample.play()
    }
}

场景切换逻辑代码

import SpriteKit
import Foundation
import SceneKit

class v1Scene: SKScene {
    
    var trashButtonNode:SKSpriteNode!
    var bikeButtonNode:SKSpriteNode!
    var backButtonNode:SKSpriteNode!
    
    override func didMove(to view: SKView) {
        trashButtonNode = (self.childNode(withName: "trashButton") as? SKSpriteNode)
        bikeButtonNode = (self.childNode(withName: "bikeButton") as? SKSpriteNode)
        backButtonNode = (self.childNode(withName: "backButton") as? SKSpriteNode)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        let v2Scene = v2Scene(fileNamed: "v2Scene")
        let v3Scene = v3Scene(fileNamed: "v3Scene")
        let MainMenu = MainMenu(fileNamed: "MainMenu")
        
        if let location = touch?.location(in: self) {
            let nodesArray = self.nodes(at: location)
            
            if nodesArray.first?.name == "trashButton" {
                let transistion = SKTransition.fade(withDuration: 2)
                self.view?.presentScene(v2Scene!, transition: transistion)
            } else if nodesArray.first?.name == "bikeButton" {
                let transistion = SKTransition.fade(withDuration: 2)
                self.view?.presentScene(v3Scene!, transition: transistion)
            } else if nodesArray.first?.name == "backButton" {
                let transistion = SKTransition.fade(withDuration: 2)
                self.view?.presentScene(MainMenu!, transition: transistion)
            }
        }
    }
}

解决方案

1. 视频加载失败修复

  • 文件放置规则:.mov不能存Assets.xcassets,必须直接拖入Xcode项目根目录或子文件夹,拖入时勾选Copy items if needed和对应target。
  • 验证资源引用:进入Xcode的Build Phases -> Copy Bundle Resources,确认你的.mov文件已在列表中,缺失则手动添加。
  • 文件名校验:确保代码中文件名与实际文件完全匹配(iOS区分大小写),比如V1.mov和v1.mov是两个不同文件。

2. 实现方式选择

  • 代码实现:更灵活,适合动态控制视频播放(如场景切换自动播放),后期修改扩展更便捷,无需依赖可视化配置。
  • .sks/故事板:适合快速搭建静态UI(如按钮布局),但视频播放逻辑仍需代码补充。推荐组合使用:用.sks做场景UI布局,代码处理视频和切换逻辑,兼顾效率与灵活性。

3. 场景切换自动播放视频实现

修改目标场景的didMove(to:)方法,在场景加载完成后自动初始化并播放对应视频:

示例(v2Scene):

import SpriteKit

class v2Scene: SKScene {
    // 按钮节点定义
    var trashButtonNode:SKSpriteNode!
    var bikeButtonNode:SKSpriteNode!
    var backButtonNode:SKSpriteNode!
    
    override func didMove(to view: SKView) {
        // 初始化并播放视频
        guard let videoNode = SKVideoNode(fileNamed: "V2.mov") else {
            print("Failed to load V2.mov")
            return
        }
        // 让视频铺满场景
        videoNode.position = CGPoint(x: frame.midX, y: frame.midY)
        videoNode.size = frame.size
        addChild(videoNode)
        videoNode.play()
        
        // 初始化按钮(原有逻辑)
        trashButtonNode = (self.childNode(withName: "trashButton") as? SKSpriteNode)
        bikeButtonNode = (self.childNode(withName: "bikeButton") as? SKSpriteNode)
        backButtonNode = (self.childNode(withName: "backButton") as? SKSpriteNode)
        // 初始隐藏按钮,视频播放完成后显示
        trashButtonNode.isHidden = true
        bikeButtonNode.isHidden = true
        
        // 监听视频播放结束事件
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(videoPlaybackFinished),
            name: .AVPlayerItemDidPlayToEndTime,
            object: videoNode.player?.currentItem
        )
    }
    
    @objc private func videoPlaybackFinished() {
        // 视频播放完成后显示按钮
        trashButtonNode.isHidden = false
        bikeButtonNode.isHidden = false
        // 移除通知监听,避免内存泄漏
        NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    // 场景切换逻辑(原有代码保留)
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // ... 原有切换代码 ...
    }
}

额外提示

  • 视频格式:确保.mov使用H.264编码,这是iOS支持的标准编码格式,避免因编码不兼容导致加载失败。
  • 错误处理:添加guard let或if let判断视频节点是否初始化成功,方便调试定位问题。

内容的提问来源于stack exchange,提问作者Owen Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:45:45