I am trying to create a pause menu on the game layer. I'm doing this just by adding two buttons on top when the pause button is pressed so the player can still see the stage of the game
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "pause" {
let button: SKSpriteNode?
let button2: SKSpriteNode?
button = SKSpriteNode(imageNamed: "button_continue")
button?.name = "button_continue"
button?.size.height = 55
button?.size.width = 215
button?.physicsBody = SKPhysicsBody(rectangleOf: button!.size)
button?.physicsBody?.allowsRotation = false
button?.position = CGPoint(x: 0, y: 160)
button?.zPosition = 100
self.addChild(button!)
button2 = SKSpriteNode(imageNamed: "button_finish")
button2?.name = "button_finish"
button2?.size.height = 55
button2?.size.width = 215
button2?.physicsBody = SKPhysicsBody(rectangleOf: button2!.size)
button2?.physicsBody?.allowsRotation = false
button2?.position = CGPoint(x: 0, y: -50)
button2?.zPosition = 100
self.addChild(button2!)
// The two buttons are created but here I am trying to stop everything until the continue button is pressed and I don't find a way
}
}
}
I have tried sleep() or timer.invalidate() but neither of them work with if statements, and I can't use while loops because then buttons don't appear until the while loop finishes:
while atPoint(location).name != "button_cotinue" {
timer1.invalidate()
}
That is what I tried and doesn't work.
Then when the continue button is pressed I will also delete the buttons but I can code that correctly. As well the finish button sends the player to the main menu scene.
what I do is put the controls (buttons) in a layer named controlsLayer that is separate from all of the game play items such as player, enemies and obstacles. Then I put all of the game items that I want to be paused in a layer named gameLayer. I make the controlsLayer have a high zPosition like 100.
then when I click my pause button I call gameLayer.isPaused = true and when I click continue I would call the opposite. gameLayer.isPaused = false. by just pausing the gameLayer, you can still run other actions like transitions or effects on your pause buttons. If you pause the whole scene you won't be able to run any actions or effects on any items.
Related
I have Sound button (On/Off) in main scene and its works perfectly
but I have problem , off the background music playing when I was disqualified I return to the main screen, but the image of the button changes the music is turned on and stays on the image of the music is off.
Main Scene :
var SoundOnOff = SKSpriteNode()
override func didMoveToView(view: SKView) {
backgroundColor = UIColor(red:0.09, green:0.63, blue:0.52, alpha:1.0)
//Main Scene:
SoundOnOff.texture = SKTexture(imageNamed:"Sound-on.png")
SoundOnOff.position = CGPoint(x: self.size.width/2 - 40 , y: self.size.height/2 - 500)
SoundOnOff.size = CGSizeMake(60 , 60)
SoundOnOff.runAction(SKAction.moveToY(140, duration: 0.5))
SoundOnOff.removeFromParent()
addChild(SoundOnOff)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let location = touch!.locationInNode(self)
if(SoundOnOff.containsPoint(location)) {
// ---------------------------------------------
// Play Background Music
// ---------------------------------------------
if ((NSUserDefaults.standardUserDefaults().objectForKey("onoroff")) !== true)
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
Singleton.sharedInstance().pauseBackgroundMusic()
SoundOnOff.texture = SKTexture(imageNamed:"Sound-off.png")
}else {
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "onoroff")
Singleton.sharedInstance().resumeBackgroundMusic()
SoundOnOff.texture = SKTexture(imageNamed:"Sound-on.png")
}
}
} }
}
Basically, you have to check in your Menu scene if music is off or on, and based on that to show appropriate texture. Right now, it seems that you are not making that check inside of a menu scene.
HINT: Obviously, your Singleton class has been implemented as a singleton. And if everything is done correctly, it is instantiated once and alive through the whole app's life time. So it has an info about music on/off state. What I wanted to point is that, when the app is closed, this singleton gets deallocated. So before that happen, you might store the info about music on/off into persistent storage, like NSUserDefaults, so the next time when user open up the app, he has his settings saved. Just a though... This really depends on you and what you want to offer in your app.
I have a car that is a SKShapeNode. It is moving. When I touch it, I want to stop it for 1 second and then go back to movement.
I have this code... But it just stop, a3 is never reached, the car don't start moving again
let a1 = SKAction.speedTo(0.0, duration: 0.0)
let a2 = SKAction.waitForDuration(0.5)
let a3 = SKAction.speedTo(1.0, duration: 0.0)
Here is an example of how to move a node from point A to point B and stop it for one second when touch it.
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
//Create a car
let car = SKSpriteNode(color: UIColor.purpleColor(), size: CGSize(width: 40, height: 40))
car.name = "car"
car.zPosition = 1
//Start - left edge of the screen
car.position = CGPoint(x: CGRectGetMinX(frame), y:CGRectGetMidY(frame))
//End = right edge of the screen
let endPoint = CGPoint(x: CGRectGetMaxX(frame), y:CGRectGetMidY(frame))
let move = SKAction.moveTo(endPoint, duration: 10)
car.runAction(move, withKey: "moving")
addChild(car)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
if let location = touch?.locationInNode(self){
//Get the node
let node = nodeAtPoint(location)
//Check if it's a car
if node.name == "car" {
//See if car is moving
if node.actionForKey("moving") != nil{
//Get the action
let movingAction = node.actionForKey("moving")
//Pause the action (movement)
movingAction?.speed = 0.0
let wait = SKAction.waitForDuration(3)
let block = SKAction.runBlock({
//Unpause the action
movingAction?.speed = 1.0
})
let sequence = SKAction.sequence([wait, block ])
node.runAction(sequence, withKey: "waiting")
}
}
}
}
}
Everything is pretty much commented. So basically, what is happening here is that:
node movement is done by action associated with "moving" key
when user touch the node, action associated by the "moving" key is paused; when this happen, another action called "waiting" is started "in parallel"
"waiting" action waits for one second, and unpause the "moving" action; thus car continue with movement
Currently, when car is touched, the "moving" action is paused...So if you touch the car again, it will stay additional second where it is (the new "waiting" action will overwrite previous "waiting" action). If you don't want this behaviour, you can check if if car is waiting already, like this:
if node.actionForKey("waiting") == nil {/*handle touch*/}
Or you can check if car has stopped by checking the value of speed property of an action associated by the "moving" key.
I am having trouble figuring out the solution to this and am starting to get very frustrated with it.
I have a pause button and an unpause button in my game scene to allow the player to pause the game, which is the following code
else if (node == pauseButton) {
pauseButton.removeFromParent()
addChild(unpauseButton)
addChild(restartButton)
self.runAction (SKAction.runBlock(self.pauseGame))
}
func pauseGame(){
pauseButton.hidden = true
unpauseButton.hidden = false
scene!.view!.paused = true // to pause the game
}
Problem
The problem is that when I pause the game then unpause the game my player sprite seems to move two spaces forward automatically
I also have a tap and swipe gesture that allows me to move the player up left right and down when I tap anywhere on the screen.
func tapUp(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
menubutton.hidden = true
settingsButton.hidden = true
highscoreLabel.hidden = true
pauseButton.hidden = false
thePlayer.runAction(move)
}
func swipedRight(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(amountToMove, y: 0, duration: 0.1)
thePlayer.runAction(move) // links the action with the players
}
As the member above me said the player is not really moving 2 spaces.
Also you should maybe change your strategy when pausing your game, because pausing the scene.view makes it very hard to add SpriteKit elements afterwards.
I think a better way is to create a worldNode in your GameScene and add all the sprites that need to be paused to that worldNode. It basically gives you more flexibility pausing a node rather than the whole scene.
First create a world node property
let worldNode = SKNode()
and add it to the scene in ViewDidLoad
addChild(worldNode)
Than add all the sprites you need paused to the worldNode
worldNode.addChild(sprite1)
worldNode.addChild(sprite2)
...
Create a global enum for your game states
enum GameState {
case Playing
case Paused
case GameOver
static var current = GameState.Playing
}
Than make a pause and resume func in your game scene
func pause() {
GameState.current = .Paused
// show pause menu etc
}
func resume() {
GameState.current = .Playing
self.physicsWorld.speed = 1
worldNode.paused = false
}
And finally add the actual pause code to your updateMethod. This way it will not resume the game even if spriteKit itself tries to resume (e.g. reopened app, dismissed alert etc)
override func update(currentTime: CFTimeInterval) {
if GameState.current == .Paused {
self.physicsWorld.speed = 0
worldNode.paused = true
}
}
In regards to your tapGesture recogniser, in the method that gets called after a tap you can add this before the rest of the code
guard GameState.current != .Paused else { return }
....
Hope this helps
I want a game over screen and the game to stop when the screen is touched during a specific animation.
let lightTexture = SKTexture(imageNamed: "green light.png")
let lightTexture2 = SKTexture(imageNamed: "red light.png")
let animateGreenLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeGreenLight = SKAction.repeatActionForever(animateGreenLight)
let animateRedLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeRedLight = SKAction.repeatActionForever(animateRedLight)
let greenLight = SKSpriteNode(texture: lightTexture)
greenLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
greenLight.runAction(changeGreenLight)
self.addChild(greenLight)
let redLight = SKSpriteNode(texture: lightTexture2)
redLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
redLight.runAction(changeRedLight)
self.addChild(redLight)
When the animation for the red light is on the screen, I want it to be game over. Do I have to make an if statement, and if so for what specifically?
Thank You in advance!
You can make yourself another node which has the same size and position as the red light. Additionally, that node is able to handle touch events. Then, add that node before the animation runs. This can be done with a sequence of actions, e.g.:
let addAction = SKAction.runBlock({ self.addChild(touchNode) })
let animationAction = SKAction.repeatActionForever(animateRedLight)
redLight.runAction(SKAction.sequence([ addAction, animationAction ]))
Update
Since you want the game to end when you touch anywhere, alter the code such that the block sets a variable which indicates that the animation is executed and implement touchesBegan which checks for that variable, e.g.
let addAction = SKAction.runBlock({ self.redLightAnimationRuns = true })
[...]
// In touchesBegan
if touched
{
if redLightAnimationRuns
{
endGame()
}
}
use the touchesBegan() function to call a GameOver() function when the red light is on the screen (which you can control with a variable).
So, when the red light comes on to the screen, variable redLightCurrent is set to true. in TouchesBegan(), when redLightCurrent is true, then call the gameOver() function where you can include what to do when the game is over. This will only occur when a touch has began on the screen.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
let array = Array(touches)
let touch = array[0] as UITouch
let touchLocation = touch.locationInNode(self)
if redLightCurrent {
gameOver()
}
}
This code works with the new xCode 7 and Swift 2.0
I am doing a small for fun project in Swift Xcode 6. The function thecircle() is called at a certain rate by a timer in didMoveToView(). My question is how do I detect if any one of the multiple circle nodes on the display is tapped? I currently do not see a way to access a single node in this function.
func thecircle() {
let circlenode = SKShapeNode(circleOfRadius: 25)
circlenode.strokeColor = UIColor.whiteColor()
circlenode.fillColor = UIColor.redColor()
let initialx = CGFloat(20)
let initialy = CGFloat(1015)
let initialposition = CGPoint(x: initialx, y: initialy)
circlenode.position = initialposition
self.addChild(circlenode)
let action1 = SKAction.moveTo(CGPoint(x: initialx, y: -20), duration: NSTimeInterval(5))
let action2 = SKAction.removeFromParent()
circlenode.runAction(SKAction.sequence([action1, action2]))
}
There are many problems with this.
You shouldnt be creating any looping timer in your games. A scene comes with an update method that is called at every frame of the game. Most of the time this is where you will be checking for changes in your scene.
You have no way of accessing circlenode from outside of your thecircle method. If you want to access from somewhere else you need to set up circlenode to be a property of your scene.
For example:
class GameScene: BaseScene {
let circlenode = SKShapeNode(circleOfRadius: 25)
You need to use the method touchesBegan. It should have come with your spritekit project. You can detect a touch to your node the following way:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
// detect touch in the scene
let location = touch.locationInNode(self)
// check if circlenode has been touched
if self.circlenode.containsPoint(location) {
// your code here
}
}
}