So I'm currently creating a game. I have a number of Scenes: Menu, settings, gameScene and GameOverScene.
Transition between ALMOST ALL scenes is without a problem, the code I'm using to transition is:
(e.g. for menu to GameScene)
let secondScene = GameScene(size: self.size)
let transition = SKTransition.pushWithDirection(.Up, duration: 0.7)
secondScene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view?.presentScene(secondScene, transition: transition)
However, my GameScene to GameOverScene is extremely glitchy. There is usually a lengthy pause.
The entire contents of GameOverScene is:
import Foundation
import SpriteKit
class GameOverScene: SKScene {
override func didMoveToView(view: SKView) {
let myLabel = SKLabelNode(fontNamed:"American Typrewriter")
myLabel.text = "Game Over!";
myLabel.fontSize = 45;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(myLabel)
}
}
Therefore I believe it's unlikely that this is the cause of the delay in transition.
The code I'm using to invoke a 'game over' is:
override func update(currentTime: CFTimeInterval) {
if sprite.position.y < 100 {gameOver()}
}
func gameOver() {
// Loads GameOver Scene //
let secondScene = GameOverScene(size: self.size)
// Creates vibration //
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
func boatSink() {
sprite.physicsBody = nil
sprite.physicsBody?.affectedByGravity = false
sprite.physicsBody?.dynamic = false
let spritefly = SKAction.moveTo(CGPointMake(400,20), duration:2.0)
car.runAction(spritefly) }
let transition = SKTransition.pushWithDirection(.Up, duration: 3.0)
let nextScene = GameOverScene(size: scene!.size)
nextScene.scaleMode = .AspectFill
scene?.view?.presentScene(nextScene, transition: transition)
}
I've experimented with the use of Delays, such as using:
let skView = self.view! as SKView
let scene = GameOverScene(size: self.scene!.size)
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 1 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
let sceneTransition = SKTransition.doorsCloseHorizontalWithDuration(2.0)
sceneTransition.pausesOutgoingScene = true;
skView.presentScene(scene, transition: sceneTransition)}
To give time to load but this does not work.
I've also tried using 'pausesOutgoingScene' and pausesIncomingScene' to no avail.
I finally tried slowing the current scene to 0.0 using:
func pauseGameScene() {
self.physicsWorld.speed = 0.0
self.speed = 0.0
}
though this just stopped the code from progressing any further.
My questions are:
1. Is there anything really obvious I'm not thinking about with my transitions?
2. Is there a way to load all Scenes in advance, I'm sure that this probably will only provide minor improvement but it is definitely an area I've not yet looked at.
Related
Tried to perform an SKTransition for the very first loading of GameScene from GameViewController but nothing seemed to happen. Can't we perform some transition while presenting a scene directly from the GameViewController? It seemed to me quite hilarious that the normal present scene is working but not the one with transition. Here is the code :
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let scene = GameScene(size: view.bounds.size)
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
//transition
let transition = SKTransition.doorsOpenVertical(withDuration: 2)
// Present the scene
view.presentScene(scene, transition: transition)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = SKColor.red
}
}
Please solve the query.
I have a problem after upgrading to XCode 7.2 that a SKAudioNode just plays like one second and then stops playing. I changed nothing in the code.
In my GameViewController I call the MenuScene like:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = MenuScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}...
And in my MenuScene I call my GameScene like this:
func launchScene() {
let gameView = view! as SKView
let gameScene = GameScene(size: self.size)
gameView.ignoresSiblingOrder = true
let reveal = SKTransition.fadeWithDuration(0.5)
gameView.presentScene(gameScene, transition:reveal)
}
And then in my GameScene I add a SKAudioNode:
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
...
let backgroundMusic = SKAudioNode(fileNamed: "main.mp3")
backgroundMusic.autoplayLooped = true
addChild(backgroundMusic)
...
}
}
So, the problem is that when I hit a Button that calls the launchScene() function the background Music starts playing but stops playing after approximately 1 second.
Edit: It seems that the backgroundMusic starts playing before! the transition to the other scene begins and when the other scene (gameScene) is "finally there" (dunno how to describe it) the music stops playing. I don't know why since I add the backgroundMusic in the gameScene in the "didMoveToView" function.
What am I doing wrong here since it works flawlessly in XCode 7.1?
In my project with a scene transition, adding even a 0.1 sec delay to the sound seem to have fixed the problem. Such as
runAction(SKAction.waitForDuration(0.1), completion: {
self.backgroundMusic = SKAudioNode(fileNamed: "main.mp3")
self.backgroundMusic.autoplayLooped = true
self.addChild(self.backgroundMusic)
})
I Have the same problem as here: SpriteKit GameScene function won't add SKSpriteNode after being called by button.
I Didn't understand the answer. How do I create an outlet for skView ?
Thank you very much for any clarification you could bring me !
Here is my code:
In my GameScene.swift
override func didMoveToView(view: SKView) {
loadMyView()
}
func loadMyView(){
currentScore = 0
loadSpecialView()
}
func loadSpecialView(){
Person = SKSpriteNode(imageNamed: "Person")
Person.size = CGSize(width:40, height: 7)
Person.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 120)
Person.zRotation = 3.14/2
Person.zPosition = 2.0
self.addChild(Person)
}
When the player dies, a Restart Button appears with the following code in my GameviewController.swift:
var currentGame: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
currentGame = GameScene()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
scene.viewController = self
skView.presentScene(scene)
}
}
#IBAction func restartGame(sender: UIButton) {
outBuy.hidden = true
outRestart.hidden = true
currentGame.loadMyView()
}
When I launch the simulator, all works well and the Person is added. When I press restart, the person doesn't appear. What happened ?
I found the solution ! Finally !
I changed currentGame = GameScene() to currentGame=scene and moved it just above skView.presentScene(scene)
Hello my problem is that when I click on a button to restart my game it will restart, but then if I click on a button that transitions to the MainMenuViewController than the scene will freeze. The interesting thing is that if I restart the game and click on the segue button first it will work properly, but then if I click the restart button the game will then freeze. When clicking on the segue button and it crashes I get the error has no segue with identifier 'GameToMain' when it indeed does work the first time. When I click on the restart game it will just crash with no error. Here is the relevant code for this problem:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
let scene = GameScene(size: skView.bounds.size)
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = false
//skView.showsPhysics = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
scene.viewController = self
}
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var viewController: GameViewController!
This is set up in a func that is called when the player loses:
let tryAgain = SKLabelNode(fontNamed: "Chalkduster")
tryAgain.text = "Try Again?"
tryAgain.color = SKColor.yellowColor()
tryAgain.name = "retryLabel"
tryAgain.fontSize = 28
tryAgain.position = CGPoint(x: size.width/2, y: size.height/2)
playerLayerNode.addChild(tryAgain)
let mainMenuTransition = SKLabelNode(fontNamed: "Chalkduster")
mainMenuTransition.text = "Main Menu"
mainMenuTransition.color = SKColor.yellowColor()
mainMenuTransition.name = "mainMenuTransitionSeque"
mainMenuTransition.fontSize = 20
mainMenuTransition.position = CGPoint(x: size.width/2, y: size.height/2 - 60)
playerLayerNode.addChild(mainMenuTransition)
This is how the labels register and react to being tapped
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if (node.name == "retryLabel") {
print("restart")
let gameScene = GameScene(size: self.size)
let transition = SKTransition.doorsCloseHorizontalWithDuration(0.5)
gameScene.scaleMode = SKSceneScaleMode.AspectFill
gameScene.viewController = GameViewController()
self.scene!.view?.presentScene(gameScene, transition: transition)
print("complete Reload")
}
if (node.name == "mainMenuTransitionSeque") {
print("go to main menu")
self.viewController!.performSegueWithIdentifier("GameToMain", sender: self)
print("complete2")
}
I solved the problem with the current code:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
let scene = GameScene(size: skView.bounds.size)
//skView.showsFPS = true
//skView.showsNodeCount = true
skView.ignoresSiblingOrder = false
//skView.showsPhysics = true
scene.scaleMode = .AspectFill
scene.viewController = self
skView.presentScene(scene)
}
}
In GameScene
var viewController: GameViewController!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if (node.name == "retryLabel") {
let skView = view as SKView!
let scene = GameScene(size: skView.bounds.size)
skView.ignoresSiblingOrder = false
scene.scaleMode = .AspectFill
scene.viewController = viewController.self
skView.presentScene(scene)
}
if (node.name == "mainMenuTransitionSeque") {
self.viewController.dismissViewControllerAnimated(false, completion: nil)
print("complete2")
}
}
}
}
So basically to restart the scene I copy and pasted the code from the GameViewController into GameScene so that it would restart the scene exactly, but the key was redeclaring the viewController variable. Then to get to the mainMenu I dismissed the view controller (GameScene) instead of creating a new segue so that I do not keep adding more and more scenes on top of each other.
I am creating a SpriteKit game and I am having a lot of trouble. The first screen is a menu, when you click the play button, the player is put into the game itself using the code:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location:CGPoint = touch.locationInNode(self)
if self.nodeAtPoint(location).name == "play" {
playButton.fontColor = colorPressed
var scene = PlayScene(size: self.size)
let skView = self.view! as SKView
skView.ignoresSiblingOrder = true
skView.showsFPS = false
skView.showsNodeCount = false
scene.scaleMode = .AspectFill
gameStart(scene, skView: skView)
}
}
}
func gameStart(scene:SKScene, skView:SKView) {
let scale = SKAction.scaleTo(0.0, duration: 0.17)
about.runAction(scale, completion: {
self.scene?.removeAllChildren()
self.scene?.removeFromParent()
skView.presentScene(scene)
})
}
(PlayScene is the scene for where the player plays the game) This transition works fine. The player is able to play the game fine. Next, when the player loses the game, I go to a game over scene using this code:
func lineHitBadDot(dot: SKSpriteNode) {
var sceneGG = OverScene(size: self.size)
let skViewGG = self.view! as SKView
skViewGG.ignoresSiblingOrder = true
skViewGG.showsFPS = false
skViewGG.showsNodeCount = false
sceneGG.scaleMode = .AspectFill
let scale = SKAction.scaleTo(0.0, duration: 0.17)
scoreCounter.runAction(scale, completion: {
self.scene?.removeAllChildren()
self.scene?.removeFromParent()
skViewGG.presentScene(sceneGG)
})
}
(OverScene is the game over scene) Then, the player has two options, to try again, or return to the menu scene. This uses this code:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location:CGPoint = touch.locationInNode(self)
if self.nodeAtPoint(location).name == "retry" {
var sceneCookie = PlayScene(size: self.size)
let skViewCookie = self.view! as SKView
skViewCookie.ignoresSiblingOrder = true
skViewCookie.showsFPS = false
skViewCookie.showsNodeCount = false
sceneCookie.scaleMode = .AspectFill
gameStart(sceneCookie, skViewD: skViewCookie)
}
else if self.nodeAtPoint(location).name == "menu" {
var sceneCookieB = GameScene(size: self.size)
let skViewCookieB = self.view! as SKView
skViewCookieB.ignoresSiblingOrder = true
skViewCookieB.showsFPS = false
skViewCookieB.showsNodeCount = false
sceneCookieB.scaleMode = .AspectFill
gameStart(sceneCookieB, skViewD: skViewCookieB)
}
}
}
func gameStartE(sceneD:SKScene, skViewD:SKView) {
let scale = SKAction.moveToY(self.size.height + 300, duration: 0.17)
homeB.runAction(scale, completion: {
self.scene?.removeAllChildren()
self.scene?.removeFromParent()
skViewD.presentScene(sceneD)
})
}
The problem arrises when going back to the menu or re-trying the game. When going back to the menu, I see a blank screen, meaning the buttons have animated away, so it is displaying the initial menu scene, instead of deallocating the old one, and creating a new one like I want it to. The same thing happens when re-trying the game, it brings back the old scene instead of deallocating it and creating a new one. I have looked all over the internet and have found no solutions to this problem. Help would be very much appreciated.
Thanks.