How to completely remove SKAction from the view - sprite-kit

So I have a game where you need to shoot enemies. When touchesBegan a bullet comes under your finger, when touchesEnded – it fires at enemy. I made it with SKActions. It's working well until it's game over. I don't have a special scene for it, it's just a node with buttons. But when it appears, SKActions on bullet and enemies are still running by touch. I want to disable them when it's game over and don't know how to do it. For example, one of my enemis I created like this:
func addMiddleHeart() {
middleheart = SKSpriteNode(imageNamed: "redh")
middleheart.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) + 100)
middleheart.zPosition = 1
middleheart.physicsBody = SKPhysicsBody(circleOfRadius: middleheart.size.width / 2)
middleheart.physicsBody?.dynamic = true
middleheart.physicsBody?.categoryBitMask = PhysicsCategory.MiddleHeart
middleheart.physicsBody?.contactTestBitMask = PhysicsCategory.Arrow
middleheart.physicsBody?.collisionBitMask = PhysicsCategory.None
addChild(middleheart)
let moveToPoint = SKAction.moveTo(CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) + leftHeart.size.height * 1.5), duration: 0.5)
SKActionTimingMode.EaseOut
middleheart.runAction(moveToPoint)
}
override func didMoveToView(view: SKView) {
runAction(SKAction.runBlock(addMiddleHeart))
}

gameScene.removeAllActions() is the statement that you want to use. It'll indiscriminately remove all running SKActions.

Related

Swift: SKEmitterNode affected by PhysicsWorld gravity?

I'm trying to have a rain particles which are affected by wind aka physicsWorld gravity.
I can see that the gravity does has an affect on my SKSpriteNodes but I can't achieve the same affect on an SKEmitterNode.
I'm just wondering if it's possible.
Here's what I've been trying...
override func didMove(to view: SKView) {
if let rainParticles = SKEmitterNode(fileNamed: "Rain.sks") {
rainParticles.position = CGPoint(x: size.width/2, y: size.height)
rainParticles.name = "rainParticle"
rainParticles.targetNode = scene
rainParticles.particlePositionRange =
CGVector(dx: frame.size.width, dy: frame.size.height)
rainParticles.zPosition = -1
// I don't think this is right
rainParticles.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
rainParticles.physicsBody?.affectedByGravity = true
addChild(rainParticles)
}
physicsWorld.contactDelegate = self
// gravity is pushing to the right here
physicsWorld.gravity = CGVector(dx: 20, dy: 0)
physicsWorld.speed = 0.85
}
Yes I have added SKPhysicsContactDelegate.
Obviously I want to ignore collisions so I haven't set collisionBitMask, also I don't want to have rain bouncing off anything with contactTestBitMask. I don't believe I need to set a categoryBitMask.
Particles are not represented by objects in SpriteKit. This means you cannot perform node-related tasks on particles, nor can you associate physics bodies with particles to make them interact with other content. Although there is no visible class representing particles added by the emitter node, you can think of a particle as having properties like any other object.
This is straight from SKEmitterNode documentation. Particles won't get any gravity acceleration from the physicsWorld of the scene.
Also rainParticles.physicsBody refers to the SKEmitterNode physicsBody, not its particles.
If you simply want the particles to simulate the current physicsWorld's gravity:
rainParticles.xAcceleration = self.physicsWorld.gravity.dx
rainParticles.yAcceleration = self.physicsWorld.gravity.dy

Remove a function from SKScene when Touches Begin

I have a blue sky texture moving from the top of the screen to the bottom, repeating forever in a function. However, when touches begin, I would like the blue sky to completely fade out after a duration, then completely remove from the scene.
I am able to get the sky to fade out momentarily, then it seems as though half of the loop is removed, but I still get the sky coming down every other time. Here's a video of the problem: Blue Sky Video
In the video, touches begin when the pause button appears at the top left of the screen - before touches begin, the blue sky moves from top to bottom seamlessly as it should.
FUNCTION
func createBlueSky() {
let blueSkyTexture = SKTexture(imageNamed: "blueSky")
for i in 0 ... 1 {
let blueSky = SKSpriteNode(texture: blueSkyTexture)
blueSky.name = "blueSky"
blueSky.zPosition = -60
blueSky.anchorPoint = CGPoint.zero
blueSky.position = CGPoint(x: 0, y: (blueSkyTexture.size().height * CGFloat(i)))
worldNode.addChild(blueSky)
let moveDown = SKAction.moveBy(x: 0, y: -blueSkyTexture.size().height, duration: 10)
let moveReset = SKAction.moveBy(x: 0, y: blueSkyTexture.size().height, duration: 0)
let moveLoop = SKAction.sequence([moveDown, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
blueSky.run(moveForever)
}
}
I've added the above function to didMove(toView)
override func didMove
createBlueSky()
Then in my touches began I added this code to fade out and remove from parent.
I saw in another post that to access the blue sky from another function, I'd need to give it a name, which I did. Still no luck :/
override func touchesBegan
let blueSky = worldNode.childNode(withName: "blueSky") as! SKSpriteNode
let blueSkyFade = SKAction.fadeOut(withDuration: 3)
let blueSkyFadeWait = SKAction.wait(forDuration: 3)
let removeBlueSky = SKAction.removeFromParent()
blueSky.run(SKAction.sequence([blueSkyFadeWait, blueSkyFade, removeBlueSky]))
I'm very new to Swift, but I hope the question was specific enough. I'm happy to provide any other necessary information.
You are creating 2 blueSky nodes, but you only fade and remove 1 of them. You need to enumerate through all of the nodes with the name you are looking for. To do this, you call enumerateChildNodes(withName:}
let blueSkyFade = SKAction.fadeOut(withDuration: 3)
let blueSkyFadeWait = SKAction.wait(forDuration: 3)
let removeBlueSky = SKAction.removeFromParent()
enumerateChleNodes(withName:"blueSky")
{
(blueSky,stop) in
blueSky.run(SKAction.sequence([blueSkyFadeWait, blueSkyFade, removeBlueSky]))
}

Swift Sprite Collision. Objects passing Through walls

I'm making a simple game(using Swift & SpriteKit), where I have a circle that can be dragged around. but the circle is not allowed to go through walls.
My collision BitMask works perfectly, but when I drag fast enough, the circle ends up going through the walls.
The Initialisation of the Player Sprite goes Like this:
func initPlayerSprite(){
let playerTexture = SKTexture(imageNamed: "player.png")
let originX = CGRectGetMidX(self.frame)
let originY = CGRectGetMidY(self.frame)
player = SKSpriteNode(texture: playerTexture, size: CGSize(width: 26, height: 26))
player.position = CGPoint(x: originX , y: originY)
player.physicsBody = SKPhysicsBody(circleOfRadius: playerTexture.size().height/2)
player.physicsBody!.dynamic = true
player.physicsBody?.allowsRotation = false
player.physicsBody!.categoryBitMask = ColliderType.Player.rawValue
player.physicsBody!.contactTestBitMask = ColliderType.Floor.rawValue + ColliderType.Gap.rawValue
player.physicsBody!.collisionBitMask = ColliderType.Wall.rawValue + ColliderType.Floor.rawValue
self.addChild(player)
}
My code for moving the Sprite goes like this:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?){
var nodeTouched = SKNode()
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let dx = -player.position.x + location.x
let dy = -player.position.y + location.y
let movePlayer = SKAction.moveBy(CGVector(dx: dx, dy: dy), duration: 0.02)
}
}
Any Idea how to make sure the collision detection work even at high velocities?
From your code its hard to judge.
Why are you calling the collisions like
ColliderType.Wall.rawValue + ColliderType....
I haven't seen this before with a "+" as a separator, usually you use a "|"
to separate them.
ColliderType.Wall.rawValue | ColliderType....
Using a plus should add the two values together and will not treat it as 2 collision types, as far as I understand. I might be wrong here.
Also did you try using precise collision detection like so?
...physicsBody.usesPreciseCollisionDetection = true.
Apple describes this bool as follows
The default value is NO. If two bodies in a collision do not perform precise collision detection, and one passes completely through the other in a single frame, no collision is detected. If this property is set to YES on either body, the simulation performs a more precise and more expensive calculation to detect these collisions. This property should be set to YES on small, fast moving bodies.
Not sure this is helping

Endless scrolling background

I am trying to get my background to move endless from right to left.
My let:
let background = SKSpriteNode(imageNamed: "background")
let background2 = SKSpriteNode(imageNamed: "background")
my didMoveToView
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
background.zPosition = 2
background2.position = CGPointMake(CGRectGetMidX(self.frame)+(background.position.x/2),CGRectGetMidY(self.frame))
background2.zPosition = 2
background.setScale(2)
background2.setScale(2)
self.addChild(background)
self.addChild(background2)
var backgroundMovement = SKAction.moveByX(background.size.width, y: 0, duration: 1)
background.runAction(backgroundMovement, completion: {() -> Void in
self.background.position = CGPointZero
})
var backgroundMovement2 = SKAction.moveByX(background2.size.width, y: 0, duration: 1)
background2.runAction(backgroundMovement2, completion: {() -> Void in
self.background2.position = CGPointZero
})}
My update func is empty.
I have uploaded a picture of how it looks like when running on device: http://s18.postimg.org/kbn83tvmx/i_OS_Simulator_Screen_Shot_09_Aug_2015_23_33_39.png
The image is still on half of the screen, and the image does not move now either.
The reason your background only takes up half of the screen is because you are using the regular size of the image in pixels. You need to edit the xScale and yScale properties in order to make it make it bigger. What you need is:
background.setScale(2)
Next, to move the background sprites, use SKAction instead since you have more control over timing. The motion within update won't be consistant because, unless you have an algorithm to control exactly when update: is called, you will see that the movement speeds up or slows down. To move the background, use code that looks something like this after your didMoveToView method:
var backgroundMovement = SKAction.moveByX(background.size.x, y: 0, duration: someDuration)
background.runAction(background, completion: {() -> Void in
//place background image at start again
background.position = ...
})
Loop this code if you want the background to move continuously and edit it in any way you'd like to make it look better (like having both backgrounds move with SKAction). Hope this helps
I'm going to copy and paste my code from another game I've made. This code moves a background from right-left forever. I'll do best to explain this code.
func createBackground () {
var backgroundTexture = SKTexture(imageNamed: "bg")
var moveBackgroundByX = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: backgroundSpeed)
var replaceBackground = SKAction.moveByX(backgroundTexture.size().width, y: 0, duration: 0)
var moveBackgroundForever = SKAction.repeatActionForever(SKAction.sequence([moveBackgroundByX, replaceBackground]))
for var i:CGFloat = 0; i < 3; i++ {
background = SKSpriteNode(texture: backgroundTexture)
background.position = CGPoint(x: backgroundTexture.size().width/2 + (backgroundTexture.size().width * i), y: CGRectGetMidY(self.frame))
background.size.height = self.frame.height
background.runAction(moveBackgroundForever)
movingObjects.addChild(background)
}
}
What you need to do is:
Move background (moveBackgroundByX). This moves the background in the X direction. You need to set a duration.
Replace background1 (replaceBackground).
Create an ACTION forever that moves background1, then replaces it automatically with NO delay.
Run a LOOP that decides how many backgrounds you will need. Maybe you might need more than 3.
Inside that loop, adjust the background.position.
Call runAction on your SKSpriteNode instance.
Add the new SKSpriteNode into your another SKView.
If you have any questions please let me know.

Swift making sksprite node move faster with time

I have a sprite node that spawns in via a function:
let NBrick1 = SKSpriteNode(texture: Brick1)
NBrick1.setScale(0.04)
NBrick1.position = CGPointMake(0.0, CGFloat(y1 + bounds/2))
NBrick1.physicsBody = SKPhysicsBody(rectangleOfSize: NBrick1.size)
NBrick1.physicsBody?.dynamic = true
BrickPairs.addChild(NBrick1)
I am using this SKAction to move the bricks:
let moveBricks = SKAction.moveByX(-distanceToMove, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removeBricks = SKAction.removeFromParent()
BricksMoveAndRemove = SKAction.sequence([moveBricks,removeBricks])
I was wondering if there was a way to make the brick move faster as time goes on.
Its actually quite simply and you do not need to get involved with SKActions. The code will below will speed up the movement of a sprite, or multiple sprites, (if you apply the same code.) What it is doing is changing the position of the sprite every frame by a little bit more each time. This is the moveFactor variable. To change the start speed and rate that it speeds up, change the value that I put in.
var moveFactor:CGFloat = 0.5
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
sprite.position = CGPoint(x: sprite.position.x + moveFactor, y: sprite.position.y)
moveFactor *= 1.005
}