Detect if a SKSpriteNode left the screen at the bottom and call a function - swift

I have tried to find a way to detect if a SKSpriteNode left the screen (I would like to call a Game Over function).
I have declared the node within a function (It is not global if I get that right?) and made and SKAction that moves the Node out of the screen and removes it afterwards.
This is what I came up with:
var node = SKSpriteNode()
let nodeTexture = SKTexture(imageNamed: "node")
nodeTexture.filteringMode = .Nearest
node = SKSpriteNode(texture: nodeTexture)
let nodeFalling = SKAction.moveToY(-70, duration: 1.6)
let nodeRemoving = SKAction.removeFromParent()
node.runAction(SKAction.sequence([nodeFalling, nodeRemoving]))
self.addChild(node)
Now what I need is to call GameOver() if the node left the screen.
I am very thankful for every answer!

In your scene you have to remember the reference for node you want to check and then in update method you just do the following:
if node.position.y < -node.size.height/2.0 {
node.removeFromParent()
gameOver()
}
Edit:
class MyScene: SKScene {
// ....
//here is the list of nodes which you want to check
var nodesToCheck = [SKSpriteNode]()
//here is your spawn function
func spawnNewNode() {
var node = SKSpriteNode()
let nodeTexture = SKTexture(imageNamed: "node")
nodeTexture.filteringMode = .Nearest
node = SKSpriteNode(texture: nodeTexture)
let nodeFalling = SKAction.moveToY(-70, duration: 1.6)
let nodeRemoving = SKAction.removeFromParent()
node.runAction(SKAction.sequence([nodeFalling, nodeRemoving]))
self.addChild(node)
nodesToCheck.append(node)
}
//and here is the update method
override func update(currentTime: NSTimeInterval) {
super.update(currentTime)
// ... every other update logic
for node in nodesToCheck {
if node.position.y < -node.size.height/2.0 {
node.removeFromParent()
gameOver()
}
}
}
func gameOver() {
println("Damn!")
}
}
Dont forget to remove your node from nodeToCheck array when they are no longer scene members.

Related

Get node last position

It's the third time I'm looking for help in this question.
I have a class name BallNode of kind SKShapeNode. Inside my code I also have a function the spawn ball every 3 second from the top side of the screen. Now, I want to set a function that locate the ball position every 1 second, and so, if the ball.position.y > 200 to print a message to the console.
The purpose of this is that if any ball will be at this position (not while it's falling down) the I will call another function. I tried to do it via SKAction, update(_ currentTime: CFTimeInterval), Timer but I didn't succeed and I really have no idea what to do...
update - my current code:
var timeType1: CFTimeInterval = 0.0
var timeType2: CFTimeInterval = 2.0
override func update(_ currentTime: CFTimeInterval) {
if (currentTime - timeType1 > timeType2){
print("time")
self.checkBallsPosition()
self.timeType1 = currentTime
}
self.enumerateChildNodes(withName: "color.BallNode") { node, _ in
self.checkBallsPosition()
}
}
func checkBallsPosition() {
self.enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
let x = self.createTopBorder()
x.isHidden = true
let wait2 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run {
let position = Double(node.position.y)
if position < 200 {
}
else if position > 200 {
print("bolbo")
node.removeFromParent()
}
}
self.run(SKAction.sequence([wait2,action2]))
}
}
thats what I try do to so far, as I said the problem is that I want to get the ball last position. because the ball fall down the screen the last position should be when it touch the bottom border of the screen or when it touches another ball. if I set it at update I get the ball position every frame or (as I did) every second - but not the last. another problem is that the ball position can always change depends on another balls (when collision occurs).
update #2 - another functions:
func spawnBalls() {
let wait = SKAction.wait(forDuration: 3)
let action = SKAction.run {
self.createBall()
}
run(SKAction.repeatForever((SKAction.sequence([wait, action]))))
}
func createBall(){
let ball = BallNode(radius: 65)
print(ball.Name)
print(ball._subName!)
ball.position.y = ((frame.size.height) - 200)
let ballXPosition = arc4random_uniform(UInt32(frame.size.width))
ball.position.x = CGFloat(ballXPosition)
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.collisionBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.topBorder
ball.delegate = self
addChild(ball)
}
You did not post the entire code you are using. Not knowing how you are spawning your balls, how is the hierarchy and how you are moving them, I can't reproduce it here.
I am not sure if I understand it right, but I believe it might be simpler than what you are doing. See if the code below helps you, I am making the balls fall with SKAction (you can do it with physics also), and checking when they go below zero, when I remove them.
private let ballSpeed : CGFloat = 400
private let ballRadius : CGFloat = 10
private func spawnBall(atPoint pos : CGPoint){
let b = SKShapeNode(circleOfRadius: ballRadius)
b.name = "ball"
b.position = pos
addChild(b)
b.run(SKAction.moveTo(y: -size.height, duration: TimeInterval((pos.y+size.height)/400)))
}
override func update(_ currentTime: TimeInterval) {
super.update(currentTime)
enumerateChildNodes(withName: "ball") { (node, _) in
if node.position.y < 0 {
node.removeFromParent()
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let pos = touch.location(in: self)
spawnBall(atPoint: pos)
}
}

SpriteKit having trouble removing nodes from scene

I have a method that creates a object that moves across the screen, and i run this method a lot of times to produce a lot of objects, but what i can't do now is remove them when i need to. I've tried
childNodeWithName("monster")?.removeFromParent()
but that doesn't work, they still complete their action. This is the method
func spawn() {
let ran = Int(arc4random_uniform(1400));
var monster = SKSpriteNode(imageNamed: "spike")
monster = SKSpriteNode(texture: text)
monster.position = CGPoint(x: ran, y: 800);
monster.zPosition = 1;
monster.physicsBody = SKPhysicsBody(texture: text, size: text.size())
monster.physicsBody?.categoryBitMask = PhysicsCategory.Monster
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Player
monster.physicsBody?.collisionBitMask = 0
monster.physicsBody?.dynamic = false
monster.name = "monster"
self.addChild(monster);
let move = SKAction.moveTo(CGPointMake(monster.position.x, -100), duration: 1.5);
let remove = SKAction.runBlock { () -> Void in
monster.removeFromParent()
self.score += 1
}
monster.runAction(SKAction.sequence([move,remove]))
}
How can i remove every "monster" node at once when i need to?
To remove every monster node at once you can use SKNode's enumerateChildNodesWithName:usingBlock: method, like this:
self.enumerateChildNodesWithName("monster") {
node, stop in
node.removeAllActions()
node.removeFromParent()
}
Here, self is a scene because you've added monsters to the scene. If you for example added monsters to some container node, then you should run this method on that node, eg. containerNode.enumerateChildNodesWithName("monster"){...}

Why my node is invisible

I'm currently trying to make a node that can change it's texture, but it's not visible on the simulator. I know it's there, cause I set showNodesCount to true and it displays that 2 nodes are on scene. It's displaying a cannon node, but not rgyBox node, that should change it's texture if I tap on cannon node. Here is my code:
class GameScene: SKScene {
var gameOver = false
let cannon = SKSpriteNode(imageNamed: "cannon")
let rgyArray = ["redBox", "greenBox", "yellowBox"]
var rgyBlock = SKSpriteNode()
func changeRgyColor() {
var randomRGY = Int(arc4random_uniform(3))
rgyBlock.texture = SKTexture(imageNamed: rgyArray[randomRGY])
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
cannon.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(cannon)
rgyBlock.texture = SKTexture(imageNamed: rgyArray[1])
rgyBlock.position = CGPointMake(self.cannon.position.x, self.cannon.position.y + 20)
self.addChild(rgyBlock)
rgyBlock.zPosition = 10
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.cannon {
changeRgyColor()
}
}
}
What am I doing wrong?
You need to recreate the rgyBlock by calling rgyBlock = SKSpriteNode(imageNamed: rgyArray[1]). Where it is defined at the top, it is defined as nothing. You then need to recreate it in didMoveToView. You can remove the texture set in didMoveToView for the rgyBlock.
You could do it a different way, and put imageNamed: "aTextureNameHere" into the define of rgyBlock. This will give rgyBlock a value. Then you can set the texture (as you already do) in didMoveToView. This way may be quicker and easier.

Spawning an Enemy

In my game I want an enemy to spawn every 10 seconds. I attempt to accomplish this by, in the GameViewController, writing
var secondEnemyTimer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: "secondEnemyFunction", userInfo: nil, repeats: false)
in the viewWillLayoutSubviews method. Then in the secondEnemyFunction I write:
let skView = self.view as! SKView
let gameScene = GameScene(size: skView.bounds.size)
gameScene.enemy2Function()
Then in the enemy2Function in the GameScene class I write:
println("Called!")
enemy2.name = enemyCategoryName
enemy2.size.width = 57
enemy2.size.height = 57
let randomX = randomInRange(Int(CGRectGetMinX(self.frame)), hi: Int(CGRectGetMaxX(self.frame)))
let randomY = randomInRange(Int(CGRectGetMinY(self.frame)), hi: Int(CGRectGetMaxY(self.frame)))
let randomPoint = CGPoint(x: randomX, y: randomY)
enemy2.position = randomPoint
self.addChild(enemy2)
enemy2.physicsBody = SKPhysicsBody(circleOfRadius: enemy1.size.width / 2)
enemy2.physicsBody?.friction = 0
enemy2.physicsBody?.restitution = 1
enemy2.physicsBody?.linearDamping = 0
enemy2.physicsBody?.allowsRotation = false
enemy2.physicsBody?.applyImpulse(CGVectorMake(50, -50))
enemy2.physicsBody?.categoryBitMask = enemyCategory
In the log "Called!" appear yet the enemy is not spawned. Just so you know I did create the enemy at the top of the class by doing:
let enemy2 = SKSpriteNode(imageNamed: "enemy")
Does anyone know how I can spawn my second enemy? Thank you in advance!
-Vinny
You should keep things simple and just do everything inside GameScene. Another thing is to drop NSTimer and use SKAction to spawn enemies. NSTimer don't respect scene's paused state, so you can get into some trouble eventually. This is how you can spawn enemies using SKAction:
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
generateEnemies()
}
func stopGeneratingCoins(){
if(self.actionForKey("spawning") != nil){removeActionForKey("spawning")}
}
func generateEnemies(){
if(self.actionForKey("spawning") != nil){return}
let timer = SKAction.waitForDuration(10)
//let timer = SKAction.waitForDuration(10, withRange: 3)//you can use withRange to randomize duration
let spawnNode = SKAction.runBlock {
var enemy = SKSpriteNode(color: SKColor.greenColor(), size:CGSize(width: 40, height:40))
enemy.name = "enemy" // name it, so you can access all enemies at once.
//spawn enemies inside view's bounds
let spawnLocation = CGPoint(x:Int(arc4random() % UInt32(self.frame.size.width - enemy.size.width/2) ),
y:Int(arc4random() % UInt32(self.frame.size.height - enemy.size.width/2)))
enemy.position = spawnLocation
self.addChild(enemy)
println(spawnLocation)
}
let sequence = SKAction.sequence([timer, spawnNode])
self.runAction(SKAction.repeatActionForever(sequence) , withKey: "spawning") // run action with key so you can remove it later
}
}
When it comes to positioning, I assumed that your scene already has the correct size. If scene is not correctly initialized and has different size (or more precisely, different aspect ratio) than a view, it could happen that enemy get off-screen position when spawned. Read more here on how to initialize the scene size properly.

How to pause an SKSpriteNode, Swift

I created this game using sprite kit. During the game sprite nodes are moving. When the "Game Over" Label pops up, I would like the monster sprite node to stop moving or pause, but the rest of the scene to still move on. I know where to put the code, I just don't know how to write it. This is my monster code.
func addMonster() {
// Create sprite
let monster = SKSpriteNode(imageNamed: "box")
monster.setScale(0.6)
monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size)
monster.physicsBody?.dynamic = true
monster.physicsBody?.categoryBitMask = UInt32(monsterCategory)
monster.physicsBody?.contactTestBitMask = UInt32(laserCategory)
monster.physicsBody?.collisionBitMask = 0
monster.name = "box"
var random : CGFloat = CGFloat(arc4random_uniform(320))
monster.position = CGPointMake(random, self.frame.size.height + 20)
self.addChild(monster)
}
EDIT
override func update(currentTime: CFTimeInterval) {
if isStarted == true {
if currentTime - self.lastMonsterAdded > 1 {
self.lastMonsterAdded = currentTime + 3.0
self.addMonster()
}
self.moveObstacle()
} else {
}
self.moveBackground()
if isGameOver == true {
}
}
If I understand your question correctly, you want to pause a single (or small number of) nodes. In that case...
monster.paused = true
Is probably what you want.