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)
Related
I am trying to implement a main menu to my Spritekit game, but whenever I try to present the scene I get a blank gray screen. Here is the code right now, which presents the game itself and not a menu:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsDrawCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = SKSceneScaleMode.AspectFill
skView.presentScene(scene)
}
}
I'm new to this, and I wasn't sure what to do so I tried replacing GameScene with the menu scene, which gave me the gray screen. Any help will be appreciated.
Use this code to load a SKScene file that is created in code only and not in the Scene editor
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let skView = self.view as? SKView {
if skView.scene == nil {
let scene = MenuScene(size: skView.bounds.size)
skView.showsFPS = false
skView.showsNodeCount = true
skView.showsPhysics = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}
}
}
and then in your MenuScene file you will need an init func
init(size: CGSize) {
super.init(size: size)
name = "menuScene"
}
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 have two views, the initial one that works fine, and a second one that I am trying to display after I transition to it from a button on the first view.
However, when I click the button, the transition happens and the screen goes blank.
Below is the viewDidLoad inside of the UIViewController class for the second view.
Also when it hits the
let sKView = view as! SKVIEW
line it spits out
Could not cast value of type 'UIView' (0x10a313eb0) to 'SKView' (0x1094d5718).
(lldb)
How do I get it to display the view?
class PlayViewController: UIViewController {
var scene2: PlayScene!
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
scene2 = PlayScene(size: skView.bounds.size)
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene2.scaleMode = .AspectFill
skView.presentScene(scene2)
}
first class
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("GameScene") as? 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 = .AspectFill
skView.presentScene(scene)
}
}
the second scene
import SpriteKit
class PlayScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "SCENE 2!";
myLabel.fontSize = 65;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(myLabel)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
The issue is your view controllers view is your everyday run of the mill UIView instead of an SKView. Assuming you are using a storyboard click on the view controller and then click on the view and change the class to SKView instead of UIView.
Hopefully that helps.
I am not sure what are you trying to archive, you can try change your super class from UIViewController to SpriteViewController or you can create a new SKView and append to your view.
You cannot downcast a UIView to a SKView as a SKView inherit from UIView as you can see in the apple documentation
Inherits From
NSObject
UIResponder
UIView
SKView
I am a beginner in swift, I have made a simple game and I wanted to add a menu scene.
This is what I have so far.
In menueScene.swift
import Foundation
import spritekit
class MenuScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let Label = SKLabelNode(fontNamed:"Chalkduster")
Label.text = "Choose the Device";
Label.fontSize = 25;
Label.position = CGPoint(x:CGRectGetMidX(self.frame), y:665.523254394531);
self.addChild(Label)
let Label1 = SKLabelNode(fontNamed: "Chalkduster")
Label1.text = "light's";
Label1.fontSize = 20;
Label1.position = CGPointMake(401.463256835938,545.199401855469)
self.addChild(Label1)
}
}
and in GameViewController.swift
import UIKit
import SpriteKit
extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as MenuScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MenuScene.unarchiveFromFile("MenuScene") as? MenuScene {
//if scene = MenuScene.unarchiveFromFile("MenuScene") as? MenuScene {
// 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 = .AspectFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
And this displays nothing in the iOS simulator and no errors. What am I doing wrong.
I think it will be much easier for you to use visual tools.
You can create new scene using File -> New file -> SprikeKitScene.
In this case you will have "MenuScene.sks" file.
You can drag and drop elements to this scene (labels, sprites etc).
All your logic you will put into "ManuScene.swift".
Code in your main ViewController to show your menu on a startup:
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MainMenuScene(fileNamed:"MenuScene") {
let skView = self.view as! SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFit
skView.presentScene(scene)
}
}
...
}
Later if you need to open your menu again from another scene, simply call this code:
let nextScene = GameScene(fileNamed: "ManuScene")!
nextScene.scaleMode = .AspectFit
scene?.view?.presentScene(nextScene)
Seems like the CGPoints you want for the Label.position are placed out of the view. This is because the x and y values are to big. I don't know why your values are this big, I assume you where working with an iPad as simulator device. Try replacing it with this:
override func didMoveToView(view: SKView) {
let Label = SKLabelNode(fontNamed:"Chalkduster")
Label.text = "Choose the Device";
Label.fontSize = 25;
Label.position = CGPoint(x:CGRectGetMidX(self.frame), y:20.0);
self.addChild(Label)
let Label1 = SKLabelNode(fontNamed: "Chalkduster")
Label1.text = "light's";
Label1.fontSize = 20;
Label1.position = CGPointMake(CGRectGetMidX(self.frame) ,300.0)
self.addChild(Label1)
}
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.