removing all nodes in existence - ios

In my game, I have a method bringing down sprites. When the user loses (gameStarted == false) , I need a way to remove all these nodes I've brought into the scene. I've tried to remove these nodes two ways, by simply saying ball.removeFromParent(), and also using the removeBalls function at the bottom. Both methods stopped future balls from entering the scene, but didn't remove the ones already in existence. If anybody has a way of doing this, I'd really appreciate it.
override func didMoveToView(view: SKView) {
var create = SKAction.runBlock({() in self.createTargets()})
var wait = SKAction.waitForDuration(2)
var createAndWaitForever = SKAction.repeatActionForever(SKAction.sequence([create, wait])
self.runAction(createAndWaitForever)
}
func createTargets() {
ball = SKSpriteNode(imageNamed:"blueBlue")
let randomx = Int(arc4random_uniform(370) + 165)
ball.position = CGPoint(x: randomx, y: 1400)
addChild(ball)
let moveDown = SKAction.moveBy(CGVector(dx: 0, dy: -1300), duration: 7)
if gameStarted == false {
ball.removeAllActions()
ball.removeFromParent()
removeBalls([ball])
}
if gameStarted && spawnReady {
ball.runAction(moveDown)
if ball.parent == nil {
addChild(ball)
}
}
}
func removeBalls(balls : [SKSpriteNode]) {
for ball in balls {
ball.removeFromParent()
}
}

Where are you keeping track of each ball? Do you have a balls array?
Do this:
Give a name to your ball nodes, say "ball"
Enumerate
Swift
self.enumerateChildNodesWithName("ball", usingBlock: {
(node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
node.removeFromParent()
})
Obj-C
[self enumerateChildNodesWithName:#"ball" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];`

Related

SKAction not running on SKSpriteNode

The SKAction on my SKShapeNode isn't working, the code isn't getting executed, and the node isn't moving, even though the console is logging "Snake is moving". Is is because the node is a property of the SKScene and the actions are part of lower scope functions?
class LevelScene: SKScene, SnakeShower {
var snake: Snake {
let theSnake = Snake(inWorld: self.size)
return theSnake
}
override func didMove(to view: SKView) {
self.backgroundColor = .green
snake.delegate = self
}
var myNode: SKShapeNode {
let node = SKShapeNode(rectOf: snake.componentSize)
node.position = snake.head
node.fillColor = .red
return node
}
func presentSnake() { // function called by the snake in the delegate (self)
self.addChild(myNode)
startMoving()
}
func startMoving() {
print("snake is moving")
myNode.run(SKAction.repeatForever(SKAction.sequence([
SKAction.move(by: self.snake.direction.getVector(), duration: 0.2),
SKAction.run({
if self.myNode.position.y > (self.size.height / 2 - self.snake.componentSize.height / 2) {
self.myNode.removeAllActions()
}
})
])))
}
}
It used to work when they property was declared in the same function as the action
myNode is a computed property. self.addChild(myNode) adds a node, but there is no myNode stored property.
myNode.run First computes a new node without adding it to the scene. Then it calls the run the action on it. but since it's a different node that is not on the scene, it will never run.
Change your myNode defintion to:
var myNode : SKShapeNode!
and in didMove(to view: add:
myNode = SKShapeNode(rectOf: snake.componentSize)
myNode.position = snake.head
myNode.fillColor = .red
I had an issue almost identical to this, where I had unresponsive child nodes in a referenced node. Turns out that referenced nodes default to isPaused being true (answer thread found here)
The solution was to change the isPaused property to false.
referencedNode.isPaused = false

Finish action running on a SpriteNode without disabling touches

i have a player who's physicsBody is divided to parts (torso, legs, head, ect), now i have a button on screen that control the movement of the player, if the button is pressed to the right the player moves to the right, and that part works fine. the problem is every time the movement changes the walk() method is called and what it does is animate the player legs to look like its walking, but if the movement doesn't stop then is keeps calling the walk() without it finishing the animation, so it looks like its stuck back and forth. what i am trying to achieve is for the player to be able to walk constantly but for the walk() (animation method) to be called once, finish, then called again(as long as the button to walk is still pressed). here what i have so far:
func walk(){
let leftFootWalk = SKAction.run {
let action = SKAction.moveBy(x: 1, y: 0.1, duration: 0.1)
let actionReversed = action.reversed()
let seq = SKAction.sequence([action, actionReversed])
self.leftUpperLeg.run(seq)
self.leftLowerLeg.run(seq)
}
let rightFootWalk = SKAction.run {
let action = SKAction.moveBy(x: 0.4, y: 0.1, duration: 0.1)
let actionReversed = action.reversed()
let seq = SKAction.sequence([action, actionReversed])
self.rightUpperLeg.run(seq)
self.rightLowerLeg.run(seq)
}
let group = SKAction.sequence([leftFootWalk, SKAction.wait(forDuration: 0.2), rightFootWalk])
run(group)
}
extension GameScene: InputControlProtocol {
func directionChangedWithMagnitude(position: CGPoint) {
if isPaused {
return
}
if let fighter = self.childNode(withName: "fighter") as? SKSpriteNode, let fighterPhysicsBody = fighter.physicsBody {
fighterPhysicsBody.velocity.dx = position.x * CGFloat(300)
walk()
if position.y > 0{
fighterPhysicsBody.applyImpulse(CGVector(dx: position.x * CGFloat(1200),dy: position.y * CGFloat(1200)))
}else{
print("DUCK") //TODO: duck animation
}
}
}
First of all you can use SKAction.runBlock({ self.doSomething() })
as the last action in your actions sequence instead of using completion.
About your question, you can use hasAction to determine if your SKNode is currently running any action, and you can use the key mechanism for better actions management.
See the next Q&A
checking if an SKNode is running a SKAction
I was able to solve this, however i'm still sure there is a better approach out there so if you know it be sure to tell. here is what i did:
first i added a Boolean called isWalking to know if the walking animation is on(true) or off(false) and i set it to false. then i added the if statement to call walk() only if the animation is off(false) and it sets the boolean to on(true).
func directionChangedWithMagnitude(position: CGPoint) {
if isPaused {
return
}
if let fighter = self.childNode(withName: "fighter") as? SKSpriteNode, let fighterPhysicsBody = fighter.physicsBody {
fighterPhysicsBody.velocity.dx = position.x * CGFloat(300)
print(fighterPhysicsBody.velocity.dx)
if !isWalking{
isWalking = true
walk()
}
}
}
and i changed walk() to use completions blocks and set the Boolean to off(false) again
func walk(){
let walkAction = SKAction.moveBy(x: 5, y: 5, duration: 0.05)
let walk = SKAction.sequence([walkAction, walkAction.reversed()])
leftUpperLeg.run(walk) {
self.rightUpperLeg.run(walk)
}
leftLowerLeg.run(walk) {
self.rightLowerLeg.run(walk, completion: {
self.isWalking = false
})
}
}

Is there a way to add action to all the created nodes except one node in swift?

I want to add an action for all nodes on the scene except one or two nodes.
Or, I would like to find a way to access to all nodes with a different way of using the "name" method.
This not the complete game but I do not want to use the "name" method because every square has different name.
import SpriteKit
import GameplayKit
var Score = 0
class GameScene: SKScene {
let SquareSide = 100
var touchedNode = SKNode()
var touchLocation = CGPoint()
let ScoreLabel = SKLabelNode()
let squareNumber = 0
override func didMove(to view: SKView) {
CreatSquares()
}
func CreatSquares(){
let square = SKShapeNode(rect: CGRect(x: Int(arc4random_uniform(UInt32(self.frame.width))), y: Int(arc4random_uniform(UInt32(self.frame.height))), width: SquareSide, height: SquareSide))
if Score <= 10{
square.fillColor = UIColor.orange}
else{
square.fillColor = UIColor.blue
}
square.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: square.frame.width, height: square.frame.height), center: CGPoint(x: square.position.x, y: square.position.y))
square.physicsBody?.affectedByGravity = false
square.physicsBody?.allowsRotation = false
square.physicsBody?.categoryBitMask = 0
square.name = "square\(number)"
number++
self.addChild(square)
}
}
func CreatScoreLabel(){
let ScoreLabel = SKLabelNode()
ScoreLabel.text = "Your Score is:\(Score)"
ScoreLabel.position = CGPoint(x: self.frame.width/2, y: self.frame.height - 50)
ScoreLabel.color = UIColor.white
self.addChild(ScoreLabel)
}
func updatScore(){
ScoreLabel.text = "Your Score is:\(Score)"
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
touchLocation = touch.location(in: self)
touchedNode = self.atPoint(touchLocation)
if touchedNode.name == "square"{
Score += 1
touchedNode.removeFromParent()
}
}
}
override func update(_ currentTime: TimeInterval) {
updatScore()
}
As explained by Confused I think you forgot the real power of enumerateChildNodes(withName:
Indeed, about the name:
The name to search for. This may be either the literal name of the
node or a customized search string. See Searching the Node Tree.
This means that you also could do:
self.enumerateChildNodes(withName: "//square*") { node, _ in
// node is equal to each child where the name start with "square"
// in other words, here you can see square0, square1, square2, square3 etc...
if node.name == "square3" {
// do whatever you want with square3
}
}
// specifies that the search should begin at the root node and be performed recursively across the entire node tree. Otherwise, it performs a recursive search from its current position.
* means that the search matches zero or more characters.
The easiest way to do this is to add all the nodes you want to add the actions to into an array, then iterate over that array, adding actions when you want/need.
This has a memory hit if your objects are huge and complex, but is the most organised and rapid way to do it without using .name.
And is far faster and easier than messing around through your node hierarchy with enumerateChildNodes... etc

Removing a sprite in an if statement with SpriteKit

I want to make a game where every time a user touches, it switches between one of two "states". In order to keep track of touches, I made a variable called userTouches, which changes from true to false each time a user touches. I want to make it so that if numberOfTouches is true, it updates the texture to state0; if it's false, it updates the texture to state1. Pretty much just toggling between state0 and state1 for each touch.
var userTouches: Bool = true
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
userTouches = !userTouches
}
let centered = CGPoint(x: size.width/2, y: size.height/2)
let state0 = SKTexture(imageNamed:"state0")
let state1 = SKTexture(imageNamed:"state1")
var activeState: SKSpriteNode = SKSpriteNode(texture: state0)
//Add new state0 if it's odd, remove old state0 if it's not.
if userTouches == true {
activeState.texture = state0
println("IT'S TRUE")
} else {
activeState.texture = state1
println("IT'S FALSE")
}
self.addChild(activeState)
activeState.name = "state0"
activeState.xScale = 0.65
activeState.yScale = 0.65
activeState.position = centered
}
When I run the code, the new textures are added according to the conditions, but the old ones are still there. They are being added as new spritenodes in the scene. I do not want this. I was expecting it to simply switch between the textures (state0 and state1) of the activeState spritenode depending on my boolean variable. How can I have my code toggle between textures each time a user taps, instead of piling new spritenodes on top of one another?
Each time you create a new object, change its texture (a new object`s texture, but not the texture of object which was set up last time) and add it to the scene. That's why new objects are added and nothing happens with the old objects.
Do this and it will solve your problem:
Create textures and SKSpriteNode object outside the touchesBegan function
If you have no init at your scene, create SKSpriteNode object at didMoveToView function for example and add it to scene
Then in touchesBegan function only set texture to SKSpriteNode object
it would look something like this:
class GameScene: SKScene {
...
let state0 = SKTexture(imageNamed:"state0")
let state1 = SKTexture(imageNamed:"state1")
var activeState: SKSpriteNode?
...
override func didMoveToView(view: SKView) {
let centered = CGPoint(x: size.width/2, y: size.height/2)
activeState = SKSpriteNode(texture: state0)
self.addChild(activeState!)
activeState!.name = "state0"
activeState!.xScale = 0.65
activeState!.yScale = 0.65
activeState!.position = centered
}
...
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if userTouches {
activeState!.texture = state0
} else {
activeState!.texture = state1
}
}
...
}

How can I get the number of skspritenodes in a scene?

I want to find out what the number of nodes is in a scene. For example, I want to create an if statement so that if the number of nodes in the scene is 0 or any other number, I would call a function.
This is what i have done but it only calls the function the first time children.count is 0 but ignores the other times.
I am not removing or adding any sprite nodes anywhere else in my code.
func dot(){
var dotTexture = SKTexture (imageNamed: "dot")
dotTexture.filteringMode = SKTextureFilteringMode.Nearest
var dot = SKSpriteNode(texture: dotTexture)
dot.setScale(0.5)
dot.position = CGPoint(x: self.frame.size.width * 0.5 , y: self.frame.size.height * 1.1)
dot.physicsBody = SKPhysicsBody (circleOfRadius: dot.size.height/2.0)
dot.physicsBody?.dynamic = true
dot.physicsBody?.allowsRotation = false
self.addChild(dot)
println("done")
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if children.count == 0 {
dot()
println("dot")
}
}
if you're doing this inside the scene itself you can do
if (self.children.count == x) {
yourFunction() //call your function
}
or
if (children.count == x) {
yourFunction() //call your function
}
in response to your comment:
there's an update method that runs every frame in sprite kit. Use it like this:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (children.count == x) {
yourFunction() //call your function
}
}
use this:
if (self.children.count == someNumber) {
<#enter code here#>
}
where some number is a trigger number or expression.
Objective-C, use:
if ([self children].count == someNumber) {
// enter code here
}
Also, where it says "enter code here," call your function and do what you need to do.
I want to find out what the number of nodes is in a scene
Use this method:
func nodeCount(scene:SKScene) -> Int {
return scene[".//*"].count
}
This will give you the count of all nodes in the entire scene heirarchy.

Resources