1

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

4

2 回答 2

3

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

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

几乎所有东西都被评论了,所以我想你会没事的:

斯威夫特 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")
        }
        
    }
}

斯威夫特 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 ......实际上它是非常可配置的。

这是粒子发射器编辑器的设置:

粒子发射器编辑器

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

发射器

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

于 2016-01-21T15:08:42.643 回答
2

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

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

正如旋风所说,你可以通过使用发射器节点以各种方式让看起来“像”它实际上分解的东西。只需使生成区域更大并尝试尽可能多地模拟颜色。为了让船消失,你可以做一个淡入淡出吗?或者 Mabye 是一个爆炸精灵?通常使用实时特效和物理,或者使用 vfx,它更多的是让它看起来像现实,然后实际模拟现实。有时你必须使用诡计来让事情看起来不错并实时运行。

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

祝你好运!

于 2016-01-21T22:53:21.613 回答