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

SceneKit游戏中实现基于捏合手势的目标点精准缩放方案咨询

SceneKit 捏合缩放聚焦指定点实现方案

完全可以通过相机平滑注视捏合点+联动缩放实现需求,核心是避免相机突然跳转,而是在捏合过程中逐步调整注视方向,同时配合缩放参数。以下是具体实现步骤和关键逻辑:

1. 屏幕坐标转3D场景目标点

首先要把用户捏合的屏幕点转换成场景中的3D空间点,SceneKit的unprojectPoint(_:)可以完成这个转换,需要指定参考深度(比如相机前方固定距离的平面):

func getWorldTarget(from touchPoint: CGPoint, in scnView: SCNView) -> SCNVector3? {
    guard let cameraNode = scnView.pointOfView else { return nil }
    // 取相机前方10个单位的平面作为参考(可根据场景尺度调整)
    let localPoint = SCNVector3(touchPoint.x, touchPoint.y, 10)
    // 将相机局部空间的点转换为世界空间坐标
    return cameraNode.convertPosition(localPoint, to: nil)
}

如果场景中有实体平面(比如地面),也可以用hitTest获取实际碰撞点,这样目标点会更贴合场景内容。

2. 平滑调整相机注视方向

为了避免相机突然转向,使用**线性插值(Lerp)**逐步更新相机的注视目标,每帧只移动一小段距离:

var currentLookAt: SCNVector3 = SCNVector3Zero // 初始注视场景中心
let smoothFactor: Float = 0.1 // 平滑系数,值越小过渡越平缓
var targetLookAt: SCNVector3?

func updateCameraLookAt() {
    guard let target = targetLookAt else { return }
    // 对每个坐标分量做线性插值
    currentLookAt.x = currentLookAt.x * (1 - smoothFactor) + target.x * smoothFactor
    currentLookAt.y = currentLookAt.y * (1 - smoothFactor) + target.y * smoothFactor
    currentLookAt.z = currentLookAt.z * (1 - smoothFactor) + target.z * smoothFactor
    // 让相机朝向插值后的目标点
    cameraNode.look(at: currentLookAt)
}

在SCNSceneRendererDelegate的renderer(_:updateAtTime:)方法中调用updateCameraLookAt(),实现每帧平滑更新。

3. 捏合手势联动缩放与注视

在手势处理中,捏合开始时记录目标点,捏合过程中同步更新缩放和注视方向:

@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
    guard let scnView = gesture.view as? SCNView else { return }
    
    switch gesture.state {
    case .began:
        // 记录捏合点对应的3D目标
        let touchPoint = gesture.location(in: scnView)
        targetLookAt = getWorldTarget(from: touchPoint, in: scnView)
    case .changed:
        // 调整相机焦距实现缩放(可限制范围避免过度缩放)
        guard let camera = cameraNode.camera else { return }
        camera.focalLength = max(10, min(camera.focalLength / gesture.scale, 100))
        gesture.scale = 1.0 // 重置缩放因子,避免累积计算
    case .ended, .cancelled:
        targetLookAt = nil // 捏合结束后可恢复默认注视,或保持当前目标
    default:
        break
    }
}

可选优化:相机位置移动替代焦距调整

如果想要更真实的透视缩放效果,可以选择沿着视线方向移动相机,而非调整焦距。逻辑是:计算相机到目标点的方向向量,按缩放比例调整相机与目标点的距离:

// 在捏合changed状态中替换焦距调整的代码
guard let target = targetLookAt else { return }
let direction = cameraNode.position - target
let distance = direction.length()
let newDistance = distance / gesture.scale
// 保持方向不变,调整相机位置到新距离
cameraNode.position = target + direction.normalized() * newDistance
gesture.scale = 1.0

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:20:13