SpriteKit - SKCameraNode 视差效果?

Posted

技术标签:

【中文标题】SpriteKit - SKCameraNode 视差效果?【英文标题】:SpriteKit - SKCameraNode Parallax effect? 【发布时间】:2015-09-14 15:17:53 【问题描述】:

我一直在尝试使用 SKCameraNode,但找不到任何关于使用它创建视差效果的信息。

假设我在上面的代码中添加了一个behindNode,在我的baseNode 下方添加了一个behindNode。然后我为我的相机设置动画。

let behindNode = SKNode()
addChild(behindNode)

let blueSquare = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 80, height: 80))
blueSquare.position = CGPoint(x: size.width/2+40, y: size.height/2+40)
behindNode.addChild(blueSquare)

let baseNode = SKNode()
addChild(baseNode)

let redSquare = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 80, height: 80))
redSquare.position = CGPoint(x: size.width/2, y: size.height/2)
baseNode.addChild(redSquare)


let cameraNode = SKCameraNode()
camera = cameraNode
addChild(camera!)
camera!.position = redSquare.position

cameraNode.runAction(SKAction.sequence([
    SKAction.waitForDuration(1),
    SKAction.moveBy(CGVector(dx: 100, dy: 100), duration: 3),
    SKAction.moveBy(CGVector(dx: -100, dy: 100), duration: 1),
    SKAction.moveBy(CGVector(dx: 100, dy: -100), duration: 1)
]))

当相机移动时,我需要蓝色方块相对于红色方块以较慢的速度移动。我希望相机节点有一个相对位置字典或其他东西,但它没有..

【问题讨论】:

【参考方案1】:

我创建了一个不处理秒数或帧数的解决方案。

您可以创建 SKCameraNode 的子类并使用 Swift 的 didSet() 方法,该方法在类变量更改后被调用。在我们的例子中,我们想监听相机节点位置变量的变化。

class CameraNodeParallax:SKCameraNode

override var position : CGPoint 
    didSet   
       // Move our backgrounds
    

我们还想创建一个包含所有视差背景节点的数组和一个包含与相机移动速度相关的相应移动速度的数组。

var backgroundNodes:[SKNode] = []
var backgroundNodesSpeedFactor:[CGVector] = [] // in relation to camera nodes speed

func addParallaxBackgroundNode(background:SKNode, vector:CGVector) 
    backgroundNodes.append(background)
    backgroundNodesSpeedFactor.append(vector)

填充我们的新变量的 init() 可能也很有用

init(position:CGPoint) 
  super.init()
  self.position = position

现在我们可以填充我们的 didSet() 监听器了:

// Move our backgrounds
var i = 0
for node in backgroundNodes 

    let positionChangeX = position.x-oldValue.x
    let positionChangeY = position.y-oldValue.y
    let changeX = positionChangeX*backgroundNodesSpeedFactor[i].dx
    let changeY = positionChangeY*backgroundNodesSpeedFactor[i].dy
    node.position = CGPointMake(node.position.x+changeX,node.position.y+changeY)

    i += 1

最终代码如下所示:

class CameraNodeParallax:SKCameraNode

    var backgroundNodes:[SKNode] = []
    var backgroundNodesSpeedFactor:[CGVector] = [] // in relation to camera nodes speed

    init(position:CGPoint) 
        super.init()
        self.position = position
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    override var position : CGPoint 
        didSet 

            // Move your parallax backgrounds
                var i = 0
                for node in backgroundNodes 

                    let positionChangeX = position.x-oldValue.x
                    let positionChangeY = position.y-oldValue.y
                    let changeX = positionChangeX*backgroundNodesSpeedFactor[i].dx
                    let changeY = positionChangeY*backgroundNodesSpeedFactor[i].dy
                    node.position = CGPointMake(node.position.x+changeX,node.position.y+changeY)

                    i += 1
                
        
    

    func addParallaxBackgroundNode(background:SKNode, vector:CGVector) 
        backgroundNodes.append(background)
        backgroundNodesSpeedFactor.append(vector)
    

我在这里创建了一个不继承自 SKCameraNode 的替代实现 https://github.com/gitmalong/lovelySpriteKitHelpers/blob/master/ParallaxBackgroundMover.swift

【讨论】:

【参考方案2】:

我在我的 ios 9 SpriteKit 游戏中使用了视差效果。

基本上,您将在场景update() 函数中添加几行代码,以将正方形移动到相机的相反方向,但需要一些缩放因子。

update()
  let moveXFactor: CGFloat = 3
  let moveYFactor: CGFloat = 2
  camera.position = CGPoint( camera.position.x + moveXFactor, camera.position.y + moveYFactor )

  let redSquareParallaxSpeedFactor: CGFloat = -0.2
  let blueSquareParallaxSpeedFactor: CGFloat = -0.1
  redSquare.position = CGPoint( redSquare.position.x + redSquareParallaxSpeedFactor * moveXFactor, redSquare.position.y + redSquareParallaxSpeedFactor * moveYFactor )
  blueSquare.position = CGPoint( blueSquare.position.x + blueSquareParallaxSpeedFactor * moveXFactor, blueSquare.position.y + blueSquareParallaxSpeedFactor * moveYFactor )

这是一个简化的示例。使用一个使用时钟跟踪 fps 并根据恒定时间尺度移动对象的变量,而不是根据设备的当前 fps 不断变快或变慢的变量。

所以,在场景的类文件中定义一些变量。

var lastUpdate: NSTimeInterval
var deltaTime: CGFloat

然后在场景类的init() 函数中初始化它们。

lastUpdate = 0
deltaTime = 0.01666

最后,在场景的 update() 函数中每次刷新时更新它们。

deltaTime = CGFloat( currentTime - lastUpdate )
lastUpdate = currentTime

if deltaTime > 1.0 
  deltaTime = 0.0166

现在这个时钟可以工作了,让我们用它来缩放视差效果,让它更平滑。

update()
  deltaTime = CGFloat( currentTime - lastUpdate )
  lastUpdate = currentTime

  if deltaTime > 1.0 
    deltaTime = 0.0166
  

  let someScale: CGFloat = 0.001

  var moveXFactor: CGFloat = 3 * deltaTime * someScale
  var moveYFactor: CGFloat = 2 * deltaTime * someScale
  camera.position = CGPoint( camera.position.x + moveXFactor, camera.position.y + moveYFactor )

  let redSquareParallaxSpeedFactor: CGFloat = -0.2
  let blueSquareParallaxSpeedFactor: CGFloat = -0.1
  redSquare.position = CGPoint( redSquare.position.x + redSquareParallaxSpeedFactor * moveXFactor, redSquare.position.y + redSquareParallaxSpeedFactor * moveYFactor )
  blueSquare.position = CGPoint( blueSquare.position.x + blueSquareParallaxSpeedFactor * moveXFactor, blueSquare.position.y + blueSquareParallaxSpeedFactor * moveYFactor )

【讨论】:

感谢所有代码。我有点希望有一种更成熟的方法来做到这一点。 skcameranode 并没有真正为我们节省很多代码。还不如绕着老式的方式滑动一堆 sknodes 这个想法是让你的游戏对象和相机保持在同一水平线上,所有的背景视差对象都使用上面的代码。 如何在这个背景对象从不出现在前景对象前面的s中对z-order进行排序? 我明白了一般原则.. 但我说在这种情况下使用 SKCameraNode 确实没有任何优势。如果您要编写所有这些代码以相对于相机以不同的速率移动物体,那么您不妨以老式方式滑动一些父 SKNodes..

以上是关于SpriteKit - SKCameraNode 视差效果?的主要内容,如果未能解决你的问题,请参考以下文章

SpriteKit:场景中的静态背景与移动的 SKCameraNode

捏手势不适用于 SKCameraNode

SpriteKit 捏缩放相机

触摸功能在 SpriteKit 中不起作用吗

UIScrollView 不会一直向上/向下滚动

SpriteKit + iOS8。 SpriteKit 是不是支持“烘焙”渲染结果?