【问题标题】:Background animation with depth in SpriteKitSpriteKit中具有深度的背景动画
【发布时间】:2017-01-10 22:09:21
【问题描述】:

我正在使用 Swift 和 SpriteKit 构建我的第一个游戏,并且想要添加背景。游戏发生在太空中,所以我想让背景中的星星以不同的速度移动。目前,我希望通过让较大的星星在屏幕上的移动速度比较小的星星更快来获得 3D 外观。有没有一种有效的方法来做到这一点,而不是像这样创建一个 SKNode 子类并将其作为子类添加到DidMoveToView 的开头?看起来这种方法非常密集,但我想我会在一遍又一遍地回收同一个图像之前尝试一下。

class BackGroundAnimation:SKNode{

let theView:SKView
init(aView:SKView){

    theView = aView

    super.init()

    animate()
}


func animate(){


    for _ in 1...200{

        let randomSize = random(1, max: 3)
        var randomPosx = random(1,max: 1000)
        randomPosx = randomPosx/1000.0
        var randomPosy = random(1,max: 1000)
        randomPosy = randomPosy/1000.0

        let star:SKSpriteNode = SKSpriteNode(texture:starTexture)
        star.setScale(randomSize/60.0)



        star.position = CGPoint(x:(theView.scene?.size.width)! * randomPosx,y:(theView.scene?.size.width)! * randomPosy)//    (self.scene.size.width)*randomPosx, y:(self.scene.size.height) * randomPosy)

        //star.position = CGPoint(x: 200,y: 200)

        star.physicsBody = SKPhysicsBody(circleOfRadius: star.size.width/2 )
        star.physicsBody?.collisionBitMask = 0
        star.physicsBody?.categoryBitMask = 0
        star.physicsBody?.contactTestBitMask = 0

        star.physicsBody?.linearDamping = 0
        star.physicsBody?.velocity = CGVector(dx:1 * randomSize, dy:0)
        star.name = "star"

        //addChild(star)
        self.addChild(star)
        self.moveToParent(self.scene!)


    }


}




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

}

任何帮助都会很棒。

【问题讨论】:

  • 我相信您当前使用的方法是实现此类背景的正确方法。但是,如果您在真实设备(不是模拟器)上发现极端滞后和/或运行缓慢(由于背景),我会尝试废弃其中的一部分并将其降低到您真正需要的部分背景
  • 您可以使用粒子,然后以不同的速度渲染 3 个粒子层,仅此而已。我的意思截图:twitter.com/JozemiteApps/status/678734299521155072.
  • 查看 SKEmitterNode

标签: swift xcode background sprite-kit


【解决方案1】:

Swift 4 / iOS 11

更新了 @JozemiteApps 解决方案

在你的班级的任何地方添加这个函数。

//Creates a new star field
func starfieldEmitterNode(speed speed: CGFloat, lifetime: CGFloat, scale: CGFloat, birthRate: CGFloat, color: SKColor) -> SKEmitterNode {
    let star = SKLabelNode(fontNamed: "Helvetica")
    star.fontSize = 80.0
    star.text = "✦"
    let textureView = SKView()
    let texture = textureView.texture(from: star)
    texture!.filteringMode = .nearest

    let emitterNode = SKEmitterNode()
    emitterNode.particleTexture = texture
    emitterNode.particleBirthRate = birthRate
    emitterNode.particleColor = color
    emitterNode.particleLifetime = lifetime
    emitterNode.particleSpeed = speed
    emitterNode.particleScale = scale
    emitterNode.particleColorBlendFactor = 1
    emitterNode.position = CGPoint(x: frame.midX, y: frame.maxY)
    emitterNode.particlePositionRange = CGVector(dx: frame.maxX, dy: 0)
    emitterNode.particleSpeedRange = 16.0

    //Rotates the stars
    emitterNode.particleAction = SKAction.repeatForever(SKAction.sequence([
        SKAction.rotate(byAngle: CGFloat(-Double.pi/4), duration: 1),
        SKAction.rotate(byAngle: CGFloat(Double.pi/4), duration: 1)]))

    //Causes the stars to twinkle
    let twinkles = 20
    let colorSequence = SKKeyframeSequence(capacity: twinkles*2)
    let twinkleTime = 1.0 / CGFloat(twinkles)
    for i in 0..<twinkles {
        colorSequence.addKeyframeValue(SKColor.white,time: CGFloat(i) * 2 * twinkleTime / 2)
        colorSequence.addKeyframeValue(SKColor.yellow, time: (CGFloat(i) * 2 + 1) * twinkleTime / 2)
    }
    emitterNode.particleColorSequence = colorSequence

    emitterNode.advanceSimulationTime(TimeInterval(lifetime))
    return emitterNode
}

然后也添加这个功能。这是创建星星层的函数。只需调用这个函数,例如在 didMoveToView 中。

func createStarLayers() {
        //A layer of a star field
        let starfieldNode = SKNode()
        starfieldNode.name = "starfieldNode"
        starfieldNode.addChild(starfieldEmitterNode(speed: -48, lifetime: size.height / 23, scale: 0.2, birthRate: 1, color: SKColor.lightGray))
        addChild(starfieldNode)

        //A second layer of stars
        var emitterNode = starfieldEmitterNode(speed: -32, lifetime: size.height / 10, scale: 0.14, birthRate: 2, color: SKColor.gray)
        emitterNode.zPosition = -10
        starfieldNode.addChild(emitterNode)

        //A third layer
        emitterNode = starfieldEmitterNode(speed: -20, lifetime: size.height / 5, scale: 0.1, birthRate: 5, color: SKColor.darkGray)
        starfieldNode.addChild(emitterNode)
    }

【讨论】:

  • 请注意,particleActions 似乎在 iOS 10 + 中不起作用
【解决方案2】:

不使用粒子的方法是创建图层,然后移动各个图层

所以创建一个背景 SKNode,用你的背景精灵填充它

创建一个前景 SKNode,用你的前景精灵填充它

添加背景作为前景的子级,给它的 zPosition 至少为 -1

然后你移动前景,无论你移动前景,你都会向相反的方向移动背景,通常是它的较小百分比(我喜欢使用一半)。如果您的前景向左移动 10 像素,则您将背景向右移动 5 像素

由于您的所有节点都在这些层内,因此当您移动层时所有节点都会移动

【讨论】:

    【解决方案3】:

    正如我在评论中提到的,您可以使用粒子创建漂亮的视差背景。

    在你的班级的任何地方添加这个函数。

    //Creates a new star field
    func starfieldEmitterNode(speed speed: CGFloat, lifetime: CGFloat, scale: CGFloat, birthRate: CGFloat, color: SKColor) -> SKEmitterNode {
        let star = SKLabelNode(fontNamed: "Helvetica")
        star.fontSize = 80.0
        star.text = "✦"
        let textureView = SKView()
        let texture = textureView.textureFromNode(star)
        texture!.filteringMode = .Nearest
    
        let emitterNode = SKEmitterNode()
        emitterNode.particleTexture = texture
        emitterNode.particleBirthRate = birthRate
        emitterNode.particleColor = color
        emitterNode.particleLifetime = lifetime
        emitterNode.particleSpeed = speed
        emitterNode.particleScale = scale
        emitterNode.particleColorBlendFactor = 1
        emitterNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMaxY(frame))
        emitterNode.particlePositionRange = CGVector(dx: CGRectGetMaxX(frame), dy: 0)
        emitterNode.particleSpeedRange = 16.0
    
        //Rotates the stars
        emitterNode.particleAction = SKAction.repeatActionForever(SKAction.sequence([
            SKAction.rotateByAngle(CGFloat(-M_PI_4), duration: 1),
            SKAction.rotateByAngle(CGFloat(M_PI_4), duration: 1)]))
    
        //Causes the stars to twinkle
        let twinkles = 20
        let colorSequence = SKKeyframeSequence(capacity: twinkles*2)
        let twinkleTime = 1.0 / CGFloat(twinkles)
        for i in 0..<twinkles {
            colorSequence.addKeyframeValue(SKColor.whiteColor(),time: CGFloat(i) * 2 * twinkleTime / 2)
            colorSequence.addKeyframeValue(SKColor.yellowColor(), time: (CGFloat(i) * 2 + 1) * twinkleTime / 2)
        }
        emitterNode.particleColorSequence = colorSequence
    
        emitterNode.advanceSimulationTime(NSTimeInterval(lifetime))
        return emitterNode
    }
    

    然后也添加这个功能。这是创建星星层的函数。只需调用这个函数,例如在 didMoveToView 中。

    func createStarLayers() {
        //A layer of a star field
        let starfieldNode = SKNode()
        starfieldNode.name = "starfieldNode"
        starfieldNode.addChild(starfieldEmitterNode(speed: -48, lifetime: size.height / 23, scale: 0.2, birthRate: 1, color: SKColor.lightGrayColor()))
        addChild(starfieldNode)
    
        //A second layer of stars
        var emitterNode = starfieldEmitterNode(speed: -32, lifetime: size.height / 10, scale: 0.14, birthRate: 2, color: SKColor.grayColor())
        emitterNode.zPosition = -10
        starfieldNode.addChild(emitterNode)
    
        //A third layer
        emitterNode = starfieldEmitterNode(speed: -20, lifetime: size.height / 5, scale: 0.1, birthRate: 5, color: SKColor.darkGrayColor())
        starfieldNode.addChild(emitterNode)
    }
    

    这就是它的样子。

    【讨论】:

    • @Nik 你不知道什么是粒子发射器吗?你最终会得到数千个独立的移动精灵。仅当您不打算使用 GPU 和 CPU 密集型时才使用此方法
    • @Knight0fDragon 所以这不是一个好方法吗?抱歉,我不是很熟悉
    • @Knight0fDragon 我目前有一些类似于你当前答案的内容
    • @Nik 如果你有空闲的周期,这是一个不错的方法
    • 粒子发射器专门针对拥有数千个移动精灵(实际上都是具有不同修改器的单个精灵)进行了优化。不要犹豫,使用此解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-09
    相关资源
    最近更新 更多