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

SceneKit如何实现相机围绕物体自动匀速旋转并按需变向

嘿,这个需求其实很好实现!不用手势识别,我们可以用两种方式来做:一种是用SceneKit自带的SCNAction,另一种是用CADisplayLink手动更新旋转角度,两种都能实现匀速旋转+按需切换方向。我结合你的代码给你详细讲讲~

核心思路

首先得明确:我们需要一个轨道节点(cameraOrbit),把相机节点挂在这个轨道节点下,然后让轨道节点围绕目标物体旋转——这样相机就会跟着轨道一起绕目标转了,这是SceneKit里实现环绕相机的标准做法。


方法一:用SCNAction实现(简单快捷)

这种方式利用SceneKit的动作系统,创建一个永久重复的旋转动作,切换方向时只需要重启动作并反转旋转方向即可。

先给你的GameViewController补充必要的变量和方法:

class GameViewController: UIViewController {
    var gameView:SCNView!
    var gameScene:SCNScene!
    var cameraNode:SCNNode!
    var camera:SCNCamera!
    var cameraOrbit:SCNNode!
    
    // 新增:记录旋转方向(1=顺时针,-1=逆时针)
    private var rotationDirection: Float = 1.0
    // 可选:保存当前的旋转动作,方便后续管理
    private var currentRotationAction: SCNAction?

    override func viewDidLoad() {
        super.viewDidLoad()
        initView()
        setupCamera()
        setupTargetObject() // 替换成你自己的目标物体初始化
        startCameraRotation() // 启动自动旋转
    }

    func initView() {
        gameView = SCNView(frame: view.bounds)
        gameView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        gameView.allowsCameraControl = false // 必须关闭默认相机控制,避免冲突
        view.addSubview(gameView)
        
        gameScene = SCNScene()
        gameView.scene = gameScene
    }

    func setupCamera() {
        camera = SCNCamera()
        cameraNode = SCNNode(camera: camera)
        // 设置相机到轨道节点的距离(可以根据需求调整)
        cameraNode.position = SCNVector3(x: 0, y: 2, z: 5)
        
        cameraOrbit = SCNNode()
        cameraOrbit.addChildNode(cameraNode)
        gameScene.rootNode.addChildNode(cameraOrbit)
    }

    func setupTargetObject() {
        // 示例:创建一个立方体作为旋转目标,你可以替换成自己的3D模型
        let cubeGeometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
        let cubeNode = SCNNode(geometry: cubeGeometry)
        gameScene.rootNode.addChildNode(cubeNode)
        // 把轨道节点放在目标物体的位置,这样就会围绕它旋转
        cameraOrbit.position = cubeNode.position
    }

    // 启动/重启相机旋转
    private func startCameraRotation() {
        // 先移除轨道节点上的所有旧动作,避免重复执行
        cameraOrbit.removeAllActions()
        // 创建旋转动作:绕Y轴转一圈(2π弧度),耗时10秒(速度可调整)
        let rotateAction = SCNAction.rotateBy(
            x: 0, 
            y: rotationDirection * .pi * 2, 
            z: 0, 
            duration: 10
        )
        // 让动作永久重复
        let repeatAction = SCNAction.repeatForever(rotateAction)
        cameraOrbit.runAction(repeatAction)
        currentRotationAction = repeatAction
    }

    // 切换旋转方向的方法(你可以在按钮点击、特定事件里调用它)
    func toggleRotationDirection() {
        rotationDirection *= -1 // 反转方向
        startCameraRotation() // 重启旋转动作
    }
}

优点:

  • 代码简单,不用手动处理帧刷新
  • 完全利用SceneKit的内置系统,性能稳定

方法二:用CADisplayLink实现(更灵活)

如果需要更精细的控制(比如随时暂停/恢复、动态调整旋转速度),可以用CADisplayLink来手动更新轨道节点的旋转角度。

替换上面的旋转相关代码为:

class GameViewController: UIViewController {
    // ... 保留之前的变量 ...
    
    // 新增:用CADisplayLink的相关变量
    private var displayLink: CADisplayLink?
    private var rotationSpeed: Float = 0.005 // 每帧旋转的角度(可调整)
    private var rotationDirection: Float = 1.0 // 1=顺时针,-1=逆时针
    private var currentRotationY: Float = 0.0

    override func viewDidLoad() {
        super.viewDidLoad()
        initView()
        setupCamera()
        setupTargetObject()
        setupDisplayLink() // 启动帧刷新回调
    }

    // ... 保留initView、setupCamera、setupTargetObject方法 ...

    private func setupDisplayLink() {
        displayLink = CADisplayLink(target: self, selector: #selector(updateCameraRotation))
        // 添加到主运行循环,跟随屏幕刷新频率执行
        displayLink?.add(to: .main, forMode: .common)
    }

    // 每帧更新相机旋转
    @objc private func updateCameraRotation() {
        currentRotationY += rotationSpeed * rotationDirection
        // 直接设置轨道节点的Y轴欧拉角,实现旋转
        cameraOrbit.eulerAngles.y = currentRotationY
    }

    // 切换旋转方向
    func toggleRotationDirection() {
        rotationDirection *= -1
    }

    // 可选:暂停旋转
    func pauseRotation() {
        displayLink?.isPaused = true
    }

    // 可选:恢复旋转
    func resumeRotation() {
        displayLink?.isPaused = false
    }
}

优点:

  • 可以随时暂停/恢复旋转
  • 动态调整旋转速度(比如根据场景变化修改rotationSpeed)
  • 旋转过程中可以加入额外逻辑(比如检测碰撞、调整相机位置)

注意事项

  1. 一定要把gameView.allowsCameraControl设为false,否则默认的相机控制会和你的自定义旋转冲突。
  2. 确保cameraOrbit的位置和目标物体的位置一致,这样才会围绕目标旋转。
  3. 旋转轴可以根据需求修改:比如绕X轴旋转就改rotateBy的x参数,或者eulerAngles.x。

内容的提问来源于stack exchange,提问作者Pedro de Sá

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:35:13