How do I make confetti? - ios

I essentially want to emit confetti particles. Each particle is the same shape, however, I want each particle to be a random colour from a selection of colors I specify.
Is there a way for each emitted particle to have a random color or do I need a separate emitter for each particle color?

You can use single emitter to achieve what you want:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let emitter = SKEmitterNode(fileNamed: "particle")
let colors = [SKColor.whiteColor(),SKColor.grayColor(),SKColor.greenColor(),SKColor.redColor(),SKColor.blackColor()]
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
emitter.position = CGPoint(x: 200, y:300)
emitter.particleColorSequence = nil
emitter.particleColorBlendFactor = 1.0
self.addChild(emitter)
let action = SKAction.runBlock({
[unowned self] in
let random = Int(arc4random_uniform(UInt32(self.colors.count)))
self.emitter.particleColor = self.colors[random];
println(random)
})
let wait = SKAction.waitForDuration(0.1)
self.runAction(SKAction.repeatActionForever( SKAction.sequence([action,wait])))
}
}
EDIT:
Try changing duration of wait action to get different results.
You can play with color ramp too (in particle editor) to achieve the same effect:
Or you can use particleColorSequence and SKKeyframeSequence in order to change particle color over its lifetime. Hope this helps.

Just for anyone else who might want an answer to this. There is a framework called SAConfettiView https://github.com/sudeepag/SAConfettiView. Definitely check it out! It worked for me.

Related

SpriteKit not detecting physics bodies when using alpha mask but does when using bounding circle

I am pretty new to SpriteKit so I may be missing something quite obvious.
I am attempting to create an interactive map of the US. I have loaded PNG images for each state and placed a couple of them into the main SKScene using the scene editor.
My goal is wanting to detect when each state is tapped by the user. I initially wrote some code that would find the nodes within a touch location however, this meant that the user could tap the frame and it would be counted as a valid tap. I only want to register touches that happen within the state texture and not the frame. Reading online it was suggested to use SKPhysicsBody to register when a tap takes place. So I changed my code to the following.
class GameScene: SKScene {
override func didMove(to view: SKView) {}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location: CGPoint = self.convertPoint(fromView: touch.location(in: self.view))
let body = self.physicsWorld.body(at: location)
if let state = body?.node, let name = state.name {
state.run(SKAction.run({
var sprite = self.childNode(withName: name) as! SKSpriteNode
sprite.color = UIColor.random()
sprite.colorBlendFactor = 1
}))
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
Now, if I choose the Bounding circle body type everything works as expected (shown above in the screenshot). When I click within the boudning circle it runs the SKAction otherwise it does nothing. However, when I change the body type to Alpha mask (the body type I want) it suddenly stops detecting the state. In fact, it returns the SKPhysicsBody for the MainScene entity.
Any advice on how to fix this would be greatly appreciated.
Thanks.
i can reproduce this behavior (bug?) when using the scene editor. however it goes away if you skip the sks file and initialize your sprites in code. (i acknowledge that setting locations for 50 states is more tedious this way.)
class GameScene: SKScene {
let ok = SKSpriteNode(imageNamed: "ok")
let nm = SKSpriteNode(imageNamed: "nm")
let tx = SKSpriteNode(imageNamed: "tx")
override func didMove(to view: SKView) {
tx.name = "Texas"
nm.name = "New Mexico"
ok.name = "Oklahoma"
//set up physics bodies w/ alpha mask
tx.physicsBody = SKPhysicsBody(texture: tx.texture!, size: tx.texture!.size())
tx.physicsBody?.affectedByGravity = false
nm.physicsBody = SKPhysicsBody(texture: nm.texture!, size: nm.texture!.size())
nm.physicsBody?.affectedByGravity = false
ok.physicsBody = SKPhysicsBody(texture: ok.texture!, size: ok.texture!.size())
ok.physicsBody?.affectedByGravity = false
//then position your sprites and add them as children
}
}

SpriteKit particle emitter change color

I have created a particle effect using the editor and now I'd like to change particleColor in code. I have set particleColorSequence to nil (otherwise I the colors would come from the color ramp in the editor and not my code) and particleColorBlendFactor is set to 1.0. I assign a random color to particleColor in the update method with the hopes that it will change each time through the loop. It does choose a random color the first time through, but then the color never varies. Can someone please explain why?
Global
let emitter = SKEmitterNode(fileNamed: "squares.sks")
let colors = [SKColor.red, SKColor.green, SKColor.blue]
didMove(to view:)
emitter?.particleColorBlendFactor = 1.0
emitter?.particleColorSequence = nil
addChild(emitter!)
update(_ currentTime:)
let random = Int(arc4random_uniform(UInt32(self.colors.count)))
emitter?.particleColor = colors[random]
This hardly counts as an answer but I couldn't fit it all into a comment so...please bear with me.
The good news is that your code seems to work!
I tried creating a new Sprite Kit project and pasted your code into that so I ended up with a GameScene class of type SKScene looking like this:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
let emitter = SKEmitterNode(fileNamed: "squares.sks")
let colors = [SKColor.red, SKColor.green, SKColor.blue]
var lastUpdateTime: TimeInterval?
override func didMove(to view: SKView) {
emitter?.particleColorBlendFactor = 1.0
emitter?.particleColorSequence = nil
addChild(emitter!)
}
override func update(_ currentTime: TimeInterval) {
var delta = TimeInterval()
if let last = lastUpdateTime {
delta = currentTime - last
} else {
delta = currentTime
}
if delta > 1.0 {
lastUpdateTime = currentTime
let random = Int(arc4random_uniform(UInt32(self.colors.count)))
emitter?.particleColor = colors[random]
}
}
}
And then I created a new SKEmitterNode from the template (I used fire...just to chose something) and named it squares.sks.
When I run that, I can see this:
So...where does that leave us?
I'm thinking there must be something different in your setup.
If you try to create a new example project like mine, are you able to get it to work?
Yeah...hardly an answer I know, but think of it as a reassurance that you are on the right path at least :)

How to make enemy have independent lives?

PLEASE HELP!!! I have been trying to figure this out for along time. I have searched the internet and i cannot find anything that will help me.
I am currently making a game in which you are a space ship in the middle and enemy ships are moving towards you and you have to shoot them. some enemies have different lives. for example: a red ship takes one shot to explode, the blue ship takes 3, etc. I have everything to work only the lives. for example: whenever a blue ship is called on to the screen i shoot it once so its life goes down to 2. but whenever a another blue ship is called the first blue ship has its life reset back to 3 again. Is there anyway I can make it so that whenever a ship looses lives it remains that way even if other ships are called ?
this is my ship function that gets called and adds enemy space ships onto the screen:
func VillainRight(){
let TooMuch = self.size.width
let point = UInt32(TooMuch)
life = 3
let VillainR = SKSpriteNode(imageNamed: "BlueVillain")
VillainR.zPosition = 2
VillainR.position = CGPoint(x: self.frame.minX,y: CGFloat(arc4random_uniform(point)))
//This code makes the villain's Zposition point towards the SpaceShip
let angle = atan2(SpaceShip.position.y - VillainR.position.y, SpaceShip.position.x - VillainR.position.x)
VillainR.zRotation = angle - CGFloat(M_PI_2)
let MoveToCenter = SKAction.move(to: CGPoint(x: self.frame.midX, y: self.frame.midY), duration: 15)
//Physics World
VillainR.physicsBody = SKPhysicsBody(rectangleOf: VillainR.size)
VillainR.physicsBody?.categoryBitMask = NumberingPhysics.RightV
VillainR.physicsBody?.contactTestBitMask = NumberingPhysics.Laser | NumberingPhysics.SpaceShip
VillainR.physicsBody?.affectedByGravity = false
VillainR.physicsBody?.isDynamic = true
VillainR.run(MoveToCenter)
addChild(VillainR)
}
This is the code that calls this function:
_ = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(Level1.VillainRight), userInfo: nil, repeats: true)
I am using the spritekit in Swift.
Thank You Very Much in advance!
That is happening because life variable is declared as a property of a scene and it is not local to a specific node (enemy ship). You can solve this in a few ways... First way would be using node's userData property:
import SpriteKit
let kEnergyKey = "kEnergyKey"
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
let blueShip = getShip(energy: 3)
let greenShip = getShip(energy: 2)
let redShip = getShip(energy: 1)
if let blueShipEnergy = blueShip.userData?.value(forKey: kEnergyKey) as? Int {
print("Blue ship has \(blueShipEnergy) lives left")
//hit the ship
blueShip.userData?.setValue(blueShipEnergy-1, forKey: kEnergyKey)
if let energyAfterBeingHit = blueShip.userData?.value(forKey: kEnergyKey) as? Int {
print("Blue ship has \(energyAfterBeingHit) lives left")
}
}
}
func getShip(energy:Int)->SKSpriteNode{
//determine which texture to load here based on energy value
let ship = SKSpriteNode(color: .purple, size: CGSize(width: 50, height: 50))
ship.userData = [kEnergyKey:energy]
return ship
}
}
This is what the docs say about userData property:
You use this property to store your own data in a node. For example,
you might store game-specific data about each node to use inside your
game logic. This can be a useful alternative to creating your own node
subclasses to hold game data.
As you can see, an alternative to this is subclassing of a node (SKSpriteNode):
class Enemy:SKSpriteNode {
private var energy:Int
//do initialization here
}

Swift - Adding animation and sound: Using animateWithTextures:

I am trying to create a specific type of animation that requires a sound and animation to occur for exactly 1 second per texture cycle.
Each time someone clicks a button, it appends a new texture to an array of SKTextures[], then I have an SKSpriteNode animate through each of the textures once. Only problem is, I can't seem to figure out a way to play a sound file for each texture as it animates. I would also like to create an animation for each texture the SKSpriteNode changes to as it goes through the textures.
Here is an example of the flow:
Button Clicked
Append SKTexture array
SKSpriteNode.animateUsingTextures(SKTextureArray, timePerFrame: 1)
While each individual texture displays, animate it, play sound.
Here is what I have that is not working.
(Code is just an abstraction of what I have)
var textures: [SKTexture] = []
let sprite: SKSpriteNode()
let scaleUp = SKAction.scaleTo(300, duration: 0.2)
let scaleDown = SKAction.scaleTo(200, duration: 0.2)
let popAnimation = SKAction.sequence([scaleUp, scaleDown])
func buttonClicked() {
textures.append("Specific Texture")
sprite.animation() // Calls Animation Function
}
func animation() {
let sound = SKAction.playSoundFileNamed("DisplayGesture.mp3", waitForCompletion: true)
let animation = SKAction.animateWithTextures(textures, timePerFrame: 1)
let completeAnimation = SKAction.group([sound, animation, popAnimation])
sprite.runAction(completeAnimation)
}
I think what is happening is that you play the sound once, then animate all of the textures. What you could try doing:
fund animation() {
for myTexture in textures {
let sound = SKAction.playSoundFileNamed("DisplayGesture.mp3", waitForCompletion: true)
sprite.texture = myTexture
}
}

Simple Gif like animation with Spritekit

I can't really find a simple solution for this, every example I see only shows very complex solutions, but all I want is 2-3 images that cycle so it appears as if it is animated. Same effect as an animated Gif. For now I have this to create an image
MonsterNode = SKSpriteNode(imageNamed: "MonsterNode_GameScene")
but how would I set MonsterNode variable to an animation of this sort? I am really looking for the very least amount of code needed to achieve this.
The main idea is to use animateWithTextures for this task. You need to set all the frames that the sprite needs to animated and the displayed time of each frame. Then use repeatActionForever to run the animation loop.
// Add 3 frames
let f0 = SKTexture.init(imageNamed: "MonsterNode_GameScene_0")
let f1 = SKTexture.init(imageNamed: "MonsterNode_GameScene_1")
let f2 = SKTexture.init(imageNamed: "MonsterNode_GameScene_2")
let frames: [SKTexture] = [f0, f1, f2]
// Load the first frame as initialization
monsterNode = SKSpriteNode(imageNamed: "MonsterNode_GameScene_0")
monsterNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
// Change the frame per 0.2 sec
let animation = SKAction.animateWithTextures(frames, timePerFrame: 0.2)
monsterNode.runAction(SKAction.repeatActionForever(animation))
self.addChild(monsterNode)
I was getting the same issue of big red X.
So instead of defining the monsterNode in code. I created the monstor on sks screen by dragging first image of animation from atlas folder. Then assign it a name: monsterNode from properties section. Here's the code
var runAnimation = [SKTexture]()
override func didMove(to view: SKView) {
let runAtlas = SKTextureAtlas(named: "run")
for index in 1...runAtlas.textureNames.count{
let textureName = String.init(format: "run%1d", index)
runAnimation.append(SKTexture(imageNamed: textureName))
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let monsterNode = self.childNode(withName: "monsterNode")
if(monsterNode != nil){
let animation = SKAction.animate(with: runAnimation, timePerFrame: 0.1)
monsterNode?.run(SKAction.repeatForever(animation))
}
}

Resources