能否让SKCameraNode的setScale()排除特定子节点不参与缩放?
实现SKScene部分子节点的缩放控制
你当前通过缩放SKCameraNode实现的是整个场景的视觉缩放,要只让部分子节点参与缩放,可以用以下两种方案:
方案一:用容器节点分组管理(推荐)
核心思路是把需要缩放的节点统一放到一个容器节点里,捏合手势时只缩放这个容器,而非相机。不需要缩放的节点直接添加到场景根节点,完全不受容器缩放影响。
修改后的代码示例:
class GameScene: SKScene { // 定义缩放容器 private let scaleContainer = SKNode() // 记录上一次的容器缩放值 private var previousContainerScale: CGFloat = 1.0 override func didMove(to view: SKView) { // 添加缩放容器到场景 self.addChild(scaleContainer) // ---- 把需要缩放的子节点添加到scaleContainer里 ---- let scalableSprite = SKSpriteNode(color: .red, size: CGSize(width: 100, height: 100)) scalableSprite.position = self.midPoint scaleContainer.addChild(scalableSprite) // ---- 不需要缩放的节点直接添加到场景 ---- let fixedSprite = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 50)) fixedSprite.position = CGPoint(x: self.midPoint.x + 200, y: self.midPoint.y) self.addChild(fixedSprite) // 添加捏合手势 let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(scaleContainer(_:))) view.addGestureRecognizer(pinchGesture) } @objc func scaleContainer(_ sender: UIPinchGestureRecognizer) { guard sender.state == .began || sender.state == .changed else { return } let scaleFactor = 1 / sender.scale let newScale = max(0.5, min(1.0, previousContainerScale * scaleFactor)) previousContainerScale = newScale // 缩放容器节点 scaleContainer.setScale(newScale) // 重置手势的scale,避免累积影响 sender.scale = 1.0 } }
方案二:抵消相机缩放对特定节点的影响(适合必须用相机的场景)
如果你仍需要使用SKCameraNode(比如还要做相机平移),可以对不需要缩放的节点做反向缩放,抵消相机的缩放效果。
修改后的代码示例:
class GameScene: SKScene { private var previousCameraScale: CGFloat = 1.0 // 存储不需要缩放的节点数组 private var fixedNodes: [SKNode] = [] override func didMove(to view: SKView) { let cameraNode = SKCameraNode() cameraNode.position = self.midPoint self.addChild(cameraNode) self.camera = cameraNode // ---- 添加需要缩放的节点(直接加场景,受相机影响) ---- let scalableSprite = SKSpriteNode(color: .red, size: CGSize(width: 100, height: 100)) scalableSprite.position = self.midPoint self.addChild(scalableSprite) // ---- 添加不需要缩放的节点,存入fixedNodes数组 ---- let fixedSprite = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 50)) fixedSprite.position = CGPoint(x: self.midPoint.x + 200, y: self.midPoint.y) self.addChild(fixedSprite) fixedNodes.append(fixedSprite) let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(scaleCamera(_:))) view.addGestureRecognizer(pinchGesture) } @objc func scaleCamera(_ sender: UIPinchGestureRecognizer) { guard let camera = self.camera, (sender.state == .began || sender.state == .changed) else { print("GameScene - Pinch Gesture Action - No camera for scene") return } let scaleFactor = 1 / sender.scale let newScale = max(0.5, min(1.0, previousCameraScale * scaleFactor)) previousCameraScale = newScale camera.setScale(newScale) // 对固定节点做反向缩放,抵消相机的缩放效果 fixedNodes.forEach { node in node.setScale(1 / newScale) } sender.scale = 1.0 } }
注意事项
- 方案一逻辑更清晰,性能也更好,优先推荐;
- 方案二中要确保固定节点的初始scale是1,否则反向缩放会出现偏差;
- 两种方案都记得重置手势的
scale为1,避免手势多次触发时的累积计算误差。
内容的提问来源于stack exchange,提问作者Moses Harding
相关产品推荐
相关产品推荐

