iOS自定义粒子系统

时间:2016-01-20 12:33:10

标签: ios sprite-kit particle-system skemitternode

我想使用sprite kit在iOS上创建一个粒子系统,我在其中定义每个粒子的颜色。据我所知,现有的SKEmitterNode是不可能的。 似乎我能做的最好的是指定一般行为。有什么方法可以指定每个粒子的起始颜色和位置吗?

2 个答案:

答案 0 :(得分:3)

这可以让您基本了解我在评论中的含义。但请记住,它是未经测试的,我不确定如果帧速率下降,它将如何表现。

此示例每秒创建5个粒子,沿给定圆的周长按顺序(逆时针方向)添加它们。每个粒子将具有不同的预定义颜色。您可以使用“设置”结构属性来更改粒子生成速度或增加或减少要发射的粒子数。

几乎所有内容都被评论,所以我想你会没事的:

Swift 2

import SpriteKit

struct Settings {

    static var numberOfParticles = 30
    static var particleBirthRate:CGFloat = 5   //Means 5 particles per second, 0.2 means one particle in 5 seconds etc.
}

class GameScene: SKScene {

    var positions       = [CGPoint]()
    var colors          = [SKColor]()

    var emitterNode:SKEmitterNode?

    var currentPosition = 0

    override func didMoveToView(view: SKView) {

        backgroundColor = .blackColor()


        emitterNode = SKEmitterNode(fileNamed: "rain.sks")

        if let emitter = emitterNode {

            emitter.position =  CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
            emitter.particleBirthRate = Settings.particleBirthRate
            addChild(emitter)


            let radius = 50.0
            let center = CGPointZero

            for var i = 0; i <= Settings.numberOfParticles; i++ {

                //Randomize color
                colors.append(SKColor(red: 0.78, green: CGFloat(i*8)/255.0, blue: 0.38, alpha: 1))

                //Create some points on a perimeter of a given circle (radius = 40)
                let angle = Double(i) * 2.0 * M_PI / Double(Settings.numberOfParticles)
                let x = radius * cos(angle)
                let y = radius * sin(angle)


                let currentParticlePosition = CGPointMake(CGFloat(x) + center.x, CGFloat(y) + center.y)

                positions.append(currentParticlePosition)

                if i == 1 {
                    /*
                    Set start position for the first particle.
                    particlePosition is starting position for each particle in the emitter's coordinate space. Defaults to (0.0, 0,0).
                    */
                    emitter.particlePosition = positions[0]
                    emitter.particleColor = colors[0]

                    self.currentPosition++
                }

            }

            // Added just for debugging purposes to show positions for every particle.
            for particlePosition in positions {

                let sprite = SKSpriteNode(color: SKColor.orangeColor(), size: CGSize(width: 1, height: 1))
                sprite.position = convertPoint(particlePosition, fromNode:emitter)
                sprite.zPosition = 2
                addChild(sprite)
            }


            let block = SKAction.runBlock({

                // Prevent strong reference cycles.
                [unowned self] in

                if self.currentPosition < self.positions.count {

                    // Set color for the next particle
                    emitter.particleColor = self.colors[self.currentPosition]

                    // Set position for the next particle. Keep in mind that particlePosition is a point in the emitter's coordinate space.
                    emitter.particlePosition = self.positions[self.currentPosition++]

                }else {

                    //Stop the action
                    self.removeActionForKey("emitting")
                    emitter.particleBirthRate = 0
                }

           })


            // particleBirthRate is a rate at which new particles are generated, in particles per second. Defaults to 0.0.

            let rate = NSTimeInterval(CGFloat(1.0) / Settings.particleBirthRate)

            let sequence = SKAction.sequence([SKAction.waitForDuration(rate), block])

            let repeatAction = SKAction.repeatActionForever(sequence)


            runAction(repeatAction, withKey: "emitting")
        }

    }
}

Swift 3.1

import SpriteKit

struct Settings {

    static var numberOfParticles = 30
    static var particleBirthRate:CGFloat = 5   //Means 5 particles per second, 0.2 means one particle in 5 seconds etc.
}

class GameScene: SKScene {

    var positions = [CGPoint]()
    var colors = [SKColor]()

    var emitterNode: SKEmitterNode?

    var currentPosition = 0

    override func didMove(to view: SKView) {

        backgroundColor = SKColor.black


        emitterNode = SKEmitterNode(fileNamed: "rain.sks")

        if let emitter = emitterNode {

            emitter.position = CGPoint(x: frame.midX, y: frame.midY)
            emitter.particleBirthRate = Settings.particleBirthRate
            addChild(emitter)


            let radius = 50.0
            let center = CGPoint.zero

            for var i in 0...Settings.numberOfParticles {

                //Randomize color
                colors.append(SKColor(red: 0.78, green: CGFloat(i * 8) / 255.0, blue: 0.38, alpha: 1))

                //Create some points on a perimeter of a given circle (radius = 40)
                let angle = Double(i) * 2.0 * Double.pi / Double(Settings.numberOfParticles)
                let x = radius * cos(angle)
                let y = radius * sin(angle)


                let currentParticlePosition = CGPoint.init(x: CGFloat(x) + center.x, y: CGFloat(y) + center.y)

                positions.append(currentParticlePosition)

                if i == 1 {
                    /*
                    Set start position for the first particle.
                    particlePosition is starting position for each particle in the emitter's coordinate space. Defaults to (0.0, 0,0).
                    */
                    emitter.particlePosition = positions[0]
                    emitter.particleColor = colors[0]

                    self.currentPosition += 1
                }

            }

            // Added just for debugging purposes to show positions for every particle.
            for particlePosition in positions {

                let sprite = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 1, height: 1))
                sprite.position = convert(particlePosition, from: emitter)
                sprite.zPosition = 2
                addChild(sprite)
            }


            let block = SKAction.run({

                // Prevent strong reference cycles.
                [unowned self] in

                if self.currentPosition < self.positions.count {

                    // Set color for the next particle
                    emitter.particleColor = self.colors[self.currentPosition]

                    // Set position for the next particle. Keep in mind that particlePosition is a point in the emitter's coordinate space.
                    emitter.particlePosition = self.positions[self.currentPosition]

                    self.currentPosition += 1

                } else {

                    //Stop the action
                    self.removeAction(forKey: "emitting")
                    emitter.particleBirthRate = 0
                }

            })


            // particleBirthRate is a rate at which new particles are generated, in particles per second. Defaults to 0.0.

            let rate = TimeInterval(CGFloat(1.0) / Settings.particleBirthRate)

            let sequence = SKAction.sequence([SKAction.wait(forDuration: rate), block])

            let repeatAction = SKAction.repeatForever(sequence)


            run(repeatAction, withKey: "emitting")
        }

    }
}

添加橙色点仅用于调试目的,如果您愿意,可以删除该部分。

就我个人而言,我会说你是在思考这个,但我可能错了,因为没有明确的描述你想要做什么以及如何使用它。请记住,SpriteKit可以以非常高效的方式在一次绘制调用中渲染一堆精灵。如果谨慎使用,则与SKEmitterNode相同。另外,不要低估SKEmitterNode ......实际上它是非常可配置的。

以下是Particle Emitter Editor的设置:

Particle Emitter Editor

无论如何,这是最终结果:

emitter

请注意,节点数来自用于调试的橙色SKSpriteNodes。如果删除它们,您将看到场景中只添加了一个节点(发射器节点)。

答案 1 :(得分:2)

你想要的是完全可能的,甚至可能是实时的。不幸的是,做这样的事情你用移动粒子作为每个像素的粒子描述的方式最好用像素着色器完成。我不知道一种干净的方法,可以让你用像素着色器绘制场景的顶部,否则你需要的是一个像素着色器,它取出像素并将它们从中心移出。除非我使用我自己的自定义游戏引擎代替spritekit构建游戏,否则我个人不会尝试这样做。

话虽如此,我不确定每个像素的像素扩散在大多数情况下是最好的。特别是如果你有卡通艺术。许多流行的游戏实际上会为他们期望着色的对象的片段制作精灵。因此,如果它是一架飞机,你可能会有一个用于机翼的精灵,甚至可能还有一条悬挂在它上面的电线。然后,当它是粉碎飞机的时候,将它从场景中移除,并用与飞机相同形状的碎片替换该区域...... Sorta就像一个拼图。这可能需要一些调整。然后你可以将skphysicsbodies添加到所有这些部分,并有力量将它们推向各个方向。这并不意味着每个像素都获得一个节点。我建议创造性地把它分成10件以下。

旋风说你可以通过各种方式让事情看起来像#34;它实际上是通过使用发射器节点解体的。只需将产卵区域做大,尽量模拟颜色。为了让这艘船消失,你可能会褪色吗?或者Mabye上面有一个爆炸精灵?通常使用实时特效和物理,或者使用vfx,更多的是让它看起来像现实,然后实际模拟现实。有时你必须使用技巧来使事情看起来很好并且实时运行。

如果你想看看这看起来怎么样,我建议你看一下像jetpac joyride这样的游戏。

祝你好运!