SKEmitterNode isn't removing itself from parent? - ios

I added SKEmitterNode as a childNode of my mainScene and than expected that It would be removed when particleLifetime ends, described like apple docs.
Added emitters like this;
var emitterPath : String = NSBundle.mainBundle().pathForResource("ShipFire", ofType: "sks")!
var emitter : SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(emitterPath) as! SKEmitterNode
emitter.position = position
emitter.particleLifetime = 0.1;
self.addChild(emitter)
My SKEmitterNode properties like image below
When I run it emitters aren't removed from screen.
I don't know what to add more if you need more information please ask any help would be appreciated thanks.

particleLifetime determines the average lifetime of a particle, in seconds. That doesn't affect on removal of SKEmitterNode from parent.
numOfParticlesToEmit which refers to Maximum field in Particles area of Particle Editor determines the number of particles that emitter should emit before stopping. That doesn't affect on removal of SKEmitterNode from parent too. Also note that you've set 0 in this field which will enable infinitely emitting.
So, if you want to remove node from parent when emitter is done with emitting, you can set the number of particles to emit (field called Maximum in Particles area inside editor) and run an SKAction sequence which will:
start an emitter
wait for some duration of time
and remove the emitter from parent (at this point emitter should finish with emitting)
Here is an simple example to show you how to do this with SKAction sequence:
class GameScene: SKScene {
let emitter : SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(NSBundle.mainBundle().pathForResource("MyParticle", ofType: "sks")!) as SKEmitterNode
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
}
func addEmitter(position:CGPoint){
var emitterToAdd = emitter.copy() as SKEmitterNode
emitterToAdd.position = position
let addEmitterAction = SKAction.runBlock({self.addChild(emitterToAdd)})
var emitterDuration = CGFloat(emitter.numParticlesToEmit) * emitter.particleLifetime
let wait = SKAction.waitForDuration(NSTimeInterval(emitterDuration))
let remove = SKAction.runBlock({emitterToAdd.removeFromParent(); println("Emitter removed")})
let sequence = SKAction.sequence([addEmitterAction, wait, remove])
self.runAction(sequence)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch: AnyObject? = touches.anyObject()
let location = touch?.locationInNode(self)
self.addEmitter(location!)
}
}
And here is the result (note how node's count is changing after emitting is done) :
Hope this helps
EDIT:
For those interested in how to make similar effect like from the video above, try with something like this:
The point is to use Color Ramp, and to choose Add for a blend mode.
Here is the dropbox link to the .sks file : Effect.sks

Set your Particle "BirthRate" and "Maximum" both to 20. Setting the max to 0 will repeat the birth.

Related

SpriteKit: how to smoothly animate SKCameraNode while tracking node but only after node moves Y pixels?

This question and others discuss how to track a node in SpriteKit using a SKCameraNode.
However, our needs vary.
Other solutions, such as updating the camera's position in update(_ currentTime: CFTimeInterval) of the SKScene, do not work because we only want to adjust the camera position after the node has moved Y pixels down the screen.
In other words, if the node moves 10 pixels up, the camera should remain still. If the node moves left or right, the camera should remain still.
We tried animating the camera's position over time instead of instantly, but running a SKAction against the camera inside of update(_ currentTime: CFTimeInterval) fails to do anything.
I just quickly made this. I believe this is what you are looking for?
(the actual animation is smooth, just i had to compress the GIF)
This is update Code:
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
SKShapeNode *ball = (SKShapeNode*)[self childNodeWithName:#"ball"];
if (ball.position.y>100) camera.position = ball.position;
if (fabs(ball.position.x-newLoc.x)>10) {
// move x
ball.position = CGPointMake(ball.position.x+stepX, ball.position.y);
}
if (fabs(ball.position.y-newLoc.y)>10) {
// move y
ball.position = CGPointMake(ball.position.x, ball.position.y+stepY);
}
}
I would not put this in the update code, try to keep your update section clutter free, remember you only have 16ms to work with.
Instead create a sub class for your character node, and override the position property. What we are basically saying is if your camera is 10 pixels away from your character, move towards your character. We use a key on our action so that we do not get multiple actions stacking up and a timing mode to allow for the camera to smoothly move to your point, instead of being instant.
class MyCharacter : SKSpriteNode
{
override var position : CGPoint
{
didSet
{
if let scene = self.scene, let camera = scene.camera,(abs(position.y - camera.position.y) > 10)
{
let move = SKAction.move(to: position, duration:0.1)
move.timingMode = .easeInEaseOut
camera.run(move,withKey:"moving")
}
}
}
}
Edit: #Epsilon reminded me that SKActions and SKPhysics access the variable directly instead of going through the stored property, so this will not work. In this case, do it at the didFinishUpdate method:
override func didFinishUpdate()
{
//character should be a known property to the class, calling find everytime is too slow
if let character = self.character, let camera = self.camera,(abs(character.position.y - camera.position.y) > 10)
{
let move = SKAction.move(to: character.position, duration:0.1)
move.timingMode = .easeInEaseOut
camera.run(move,withKey:"moving")
}
}

Can I have physics bodies collide without pushing each other?

I have a game made of little SKNodes moving around. They can not overlap, so I use physics bodies, so that they move around each other. However, in the case that one sknode is animated to follow another, it pushes the sknode ahead. Setting the collision bitmask to 0 makes them overlap, so that is not an option. But otherwise, they push each other way beyond my desired speed. I need a way to get rid of the 'pushing' without overlapping using skphysics bodies. Is there a property I can set to fix this?
Notes: I use skanimations to move my nodes around. If you need any pertinent code, tell me... I don't know where to start. I am using Swift 3.0, but I will accept answers with 2.2/2.3 syntax.
EDIT: The real solution was to change the node's velocity instead of animating movement with SK Actions.
Change the restitution on your physics body of the moving object to 0 when a contact happens, and set the weight of the object you do not want to move really high
Have you tried setting the dynamic property to false on the body being pushed?
After you have set contactTestBitMask you could set this:
myHero.physicsBody!.collisionBitMask = 0
to prevent collisions but permit contacts.
Updating position of dynamic physics body with SKAction doesn't work well. It's just like saying - Hey, no matter what physics world rules say just move my node like I want.
First solution - to make one node follow another with some particular distance use SKConstraint.
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let nodeA = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 50, height: 50))
nodeA.name = "nodeA"
addChild(nodeA)
let nodeB = SKSpriteNode(color: SKColor.redColor(), size: nodeA.size)
nodeB.name = "nodeB"
addChild(nodeB)
let followConstraint = SKConstraint.distance(SKRange.init(lowerLimit: nodeB.size.width * 1.5, upperLimit: nodeB.size.width * 1.5), toNode: nodeA)
nodeB.constraints = [followConstraint]
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else {
return
}
childNodeWithName("nodeA")!.runAction(SKAction.moveTo(touch.locationInNode(self), duration: 2))
}
}
If you still want physics bodies and SKActions...
Set "dynamic" property of every physics body to false.
Check in didBeginContact (SKPhysicsContactDelegate protocol) if they are overlapping. If it's true than remove all the actions that are changing their positions. Tip - make the physics body little bigger than it's node.

Track the position of SKSpriteNode while doing a SKAction moveTo

I'm trying to figure a way to track a postions for multiple nodes, that spwan randomly on the screen so i can make a changes to them while moving when the reach random postion.
the nodes just move along the x axis and i want to be able to generate random number from 0 to postion.x of the ball, and change the color when it reachs the postion
override func update(currentTime: CFTimeInterval)
i tried tacking changes in update method but as soon as new node appers i lost track of the previos one
i also tried
let changecolor = SKAction.runBlock{
let wait = SKAction.waitForDuration(2, withRange: 6)
let changecoloratpoint = SKAction.runBlock { self.changecolorfunc(self.ball)}
let sequence1 = SKAction.sequence([wait, changecoloratpoint])
self.runAction(SKAction.repeatAction(sequence1, count: 3))
}
but it doesn't give me any control over the random postion
You have already all you needed.
Suppose you have a reference for your node:
var sprite1: SKSpriteNode!
And you want to spawn it to a random position (an example method..):
self.spawnToRandomPos(sprite1)
And suppose you want to moveTo your sprite1:
self.sprite1.runAction( SKAction.moveToY(height, duration: 0))
Everytime you check his position you know where is it:
print(sprite1.position)
So to know always your sprite1 position you could do this code and you see all it's movements:
override func update(currentTime: CFTimeInterval) {
print(sprite1.position)
}
P.S.:
In case you dont have references about your object spawned you can also give a name to a generic object based for example by a word followed to a counter (this is just an example):
for i in 0..<10 {
var spriteTexture = SKTexture(imageNamed: "sprite.png")
let sprite = SKSpriteNode(texture: spriteTexture, size: spriteTexture.size)
sprite.name = "sprite\(i)"
self.addChild(sprite)
}
After this to retrieve/re-obtain your sprite do:
let sprite1 = self.childNodeWithName("sprite1")
There are probably a hundred different ways of doing this. Here is how I would do it.
in your update func
checkForCollisions()
this will just scroll through "obstacles" that you randomly generate, and place whoever you want the color trigger to happen. They can be anything you want just change the name to match. if they overlap your "ball" then you can color one or the other.
func checkForCollisions() {
self.enumerateChildNodesWithName("obstacles") { node, stop in
if let obstacle: Obstacle = node as? Obstacle {
if self.ball.intersectsNode(obstacle) {
//color node or ball whichever you want
}
}
}
}

How to make a shot in spritekit?

I have created a method called attackButton. Inside this function, I wrote this way in order to fire a shot. hero below is a node where I want a shot to be born.
func attackButton() {
shot.physicsBody = SKPhysicsBody(edgeLoopFromRect: shot.frame)
shot.position = CGPointMake(hero.position.x, hero.position.y)
shot.physicsBody?.categoryBitMask = shootingCategory
shot.physicsBody?.contactTestBitMask = enemyCategory
addChild(shot)
let run = SKAction.moveByX(2, y: 0, duration: 1)
shot.runAction(run)
}
The shot node is displayed on the scene, but the node never moves and after a button is pressed twice, an error occurs. I guess it is because of lack of code which is removeFromSuperview(), or removeFromParent, though I am unsure about that. So, why does the node not move to x direction? Should I write this inside the update(currentTime: CFTimeInterval) method? Though to me this sound a bit too technical. This attackButton is detected by being written in the touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)by specifying the name of the node like this:
for touch in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "attackButton" {
attackButton()
}
}
If I write this function in the update(currentTime: CFTimeInterval), how is the method is triggered? I can't figure out as the method does not know if I press the attackButton, because this attackButton is a node and the name of the node has to be specified, right? Maybe what I say does not make sense, but I appreciate if you understand me, and could you give me some code that enables firing a shot? Thanks!

how to change the speed of falling nodes in SWIFT

I would like to know how to change the speed of falling nodes in sprite kit using swift, I have tried by changing the gravity, but when it goes very fast it starts to crash.
I have done this, it works, but as I said it crashes:
var velocity:CGFloat = 0
override func update(currentTime: CFTimeInterval) {
velocity = CGFloat(score*3)
self.physicsWorld.gravity = CGVectorMake(0, -velocity)
}
Thank you!
Instead of changing the scene's gravity, you can apply a force on the nodes.
Disable the gravity
self.physicsWorld.gravity = CGVectorMake(0,0)
Set the name property of each falling node with it's declaration
node.name = #"fallingNode"
Then, in the update Function
self.enumerateChildNodesWithName("fallingNode", usingBlock: {
(node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
// do something with node or stop
node.physicsBody?.applyForce(velocity)
return
})

Resources