Nothing Shows up on scene in SpriteKit - ios

I wanted to create a game in Xcode 7.2. I wanted to create a info screen which will have some labels inside. But nothing shows up on the newly created scene. Whatever I put on the new scene, like images, sprites, labels. Nothing appears on the screen when I run it. But the code I wrote to create the label works perfectly fine in the "GameScene" which the game initially contains.
The code that I put in the InfoScene is the following:
import SpriteKit
class InfoScene: SKScene {
override func didMoveToView(view: SKView) {
let thanksLabel = SKLabelNode(fontNamed:"Arial")
thanksLabel.text = "Thank you for Playing!"
thanksLabel.fontSize = 45
thanksLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(thanksLabel)
}
}
Nothing seems wrong to me. If I put the above code in the GameScene which the template originally has, them the label will appear. Also, if I add a sprite on the scene, it doesn't show up as well. This really confuses me.

I think you've simply forgot to put your code to an instance method like:
Swift 2.x:
override func didMoveToView(view: SKView) {
// put your stuff here
}
Swift 3.x:
override func didMove(to view: SKView){
// put your stuff here
}
didMove is called immediately after a scene is presented by a view. According to the actual documentation you might use this method to create the scene’s contents.
Update: (after your corrections and comment):
I think InfoScene is not launched for some reason.
Make sure your InfoScene is launched using breakpoints inside didMoveToView or simply add this line between your lines:
print("∙ \(type(of: self))")

Are you sure you are using your InfoScene class? How are you making this instance? Through an SKS file? if so, you need to change the Custom class field to point to your InfoScene, by default it will go to SKScene

Related

SpriteKit Scene referenced with SKReferenceNode does not load custom class

I have a GameScene.sks a custom class GameScene.swift which is connected via the Custom Class Inspector (on the right in Xcode). This is working fine.
Now in the GameScene.swift where I want to reference another scene that I want to reuse in many scenes in the future (oversimplified code):
class GameScene: SKScene {
// ...
override func didMove(to view: SKView) {
//...
guard let gameMenuPath = Bundle.main.path(
forResource: "GameMenu", ofType: "sks") else { return }
let url = NSURL(fileURLWithPath: gameMenuPath) as URL
let gameMenu = SKReferenceNode(url: url)
addChild(gameMenu)
//...
where I am loading a reusable GameMenu.sks.
Everything works fine till here. Now the custom class issue for the GameMenu.sks (the scene to be reused). For the GameMenu.sks I also have a GameMenu.swift which is referenced in the custom class inspector as custom class. So exactly as for the GameScene. The problem is that it seems like the GameMenu.swift is not found/loaded. I get no error - so it looks like I misspelled the custom class - but I checkt it many times and also cleared the Xcode cache.
Any ideas how to set custom class for a scene (.sks) that is referenced in another scene?
I would think this is something that is done for reusable scene parts suche as the GameMenu in my case that is visible in every GameScene.
Thank you for any hints.
Is it necessary to add GameMenu in GameScene via code? You can also simply use the inspector. In your GameScene.sks drop the 'Reference' (SKReferenceNode) object. Select it from the hierarchy and select GameMenu as in the 'reference' dropdown.
SKReferenceNodes behave as nodes, not as scenes, which may cause confusion since you are creating a scene file.
This means none of the functions that a view calls on a scene will be called.
If you need some of these functions to call, you need to do it manually.
Look at this example to get your didMoveToView to fire:
override func didMove(to view: SKView) {
let gameMenu = SKReferenceNode(fileNamed:"GameMenu") as? SKScene // (or your custom class)
addChild(gameMenu)
gameMenu.didMove(to:view)
}

UIView doesn't change at runtime

I've had this working in other variations but something seems to elude me in the change from objective-c to swift as well as moving some of the setup into it's own class.
So i have:
class ViewController: UIViewController, interfaceDelegate, scrollChangeDelegate{
let scrollControl = scrollMethods()
let userinterface = interface()
override func viewDidLoad(){
super.viewDidLoad()
loadMenu("Start")
}
func loadMenu(menuName: String) {
userinterface.delegate = self
userinterface.scrollDelegate = self
userinterface.removeFromSuperview() //no impact
scrollControl.removeFromSuperview() //no impact
userinterface.configureView(menuName)
view.addSubview(scrollControl)
scrollControl.addSubview(userinterface)
}
}
This sets everything up correctly but the problem occurs when I change loadMenu() at runtime. So if the user calls loadMenu("AnotherMenu") it won't change the UIView. It will call the right functions but it won't update the view. Although if I call loadMenu("AnotherMenu") at the start, the correct menu will display. Or if I call loadMenu("Start") and then loadMenu("AnotherMenu") then the menu displayed will be "AnotherMenu". As in:
override func viewDidLoad(){
super.viewDidLoad()
loadMenu("Start")
loadMenu("AnotherMenu")
}
When I list all the subviews each time loadMenu() is called, they look correct. Even during runtime. But the display is not updated. So something isn't getting the word. I've tried disabling Auto Layout after searching for similar issues but didn't see a difference.
Try adding setNeedsDisplay() to loadMenu
Eg
func loadMenu(menuName: String) {
userinterface.delegate = self
userinterface.scrollDelegate = self
userinterface.removeFromSuperview() //no impact
scrollControl.removeFromSuperview() //no impact
userinterface.configureView(menuName)
view.addSubview(scrollControl)
scrollControl.addSubview(userinterface)
view.setNeedsDisplay()
}
setNeedsDisplay() forces the view to reload the user interface.
I didn't want to post the whole UIView class as it is long and I thought unrelated. But Dan was right that he would need to know what was going on in those to figure out the answer. So I created a dummy UIView class to stand in and intended to update the question with that. I then just put a button on the ViewController's UIView. That button was able to act on the view created by the dummy. So the problem was in the other class. Yet it was calling the methods of the ViewController and seemingly worked otherwise. So then the issue must be that its acting on an instanced version? The way the uiview class worked, it uses performSelector(). But in making these methods into their own class, I had just lazily wrote
(ViewController() as NSObjectProtocol).performSelector(selector)
when it should have been
(delegate as! NSObjectProtocol).performSelector(selector)
so that was annoying and I wasted the better part of a day on that. But thanks again for the help.

Adding a screen before GameScene.swift in SpriteKit

I'm making a game in XCode using Swift and I want to add a starting screen before the actual game. I don't want just an image that fades out, I want a screen with "play", "select character", and some buttons. I don't know if i should add a new swift file, add a sks file, edit the GameScene.swift file? I'm just starting to develop in SpriteKit and it would be great if you could explain me how to do this properly. Thanks.
Follow this Step by step and it should work:
Open up your Interface Builder (main.storyboard) and add a UIViewController.
Select the new UIViewController, go to Editor > Embed In > UINavigationController
a. To remove the UINavBar for your game scene you'll be able to do this using the property navigationBarHidden in the Game Scene View Controller
Add the UIButtons and/or other controls of your choice to your new View Controller
Left Click (ctrl) and drag the PLAY UIControl to the View Controller displaying your Game Scene.
Connect the other UIControls after adding other View Controllers that are associated with their UIControl counterpart. (see below)
If you've only ever programmed using the SpriteKit Template file, I would recommend looking over this Apple Documentation
~ MORE Details ~
Having successfully added your Start Screen, or your Root View Controller, add one ViewController for each page, or screen, that you'll need the Start Screen to display, by dragging one at a time into the Storyboard file.
Having all the Screens you'll need represented by the View Controllers within your main.storyboard file, the next step is Control Clicking from each UIButton on your "Start Screen" to the ViewController that will display its target screen.
You can just create new sprite kit scene and new swift file,
then put class of your swift file into Custom Class inspector in the
gamescene.sks so it will work just like default GameScene.
then initialise your menu scene in viewDidLoad instead of original GameScene
if let menu = SKScene(fileNamed: "MainMenu") {
menu.scaleMode = .aspectFill
view.presentScene(menu)
}
You can switch to your original scene like this(for example):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let posistion = touch.location(in: self)
let node = self.atPoint(posistion)
if node == yourNode {
if let originalScene = SKScene(fileNamed: "GameScene") {
originalScene.scaleMode = .aspectFill
view?.presentScene(originalScene)
}
}
}
}

How do you call a function defined in another class?

I've created a SpriteKit project and defined a function in the GameScene.swift file. Said function creates a SKSpriteNode and has no problem running when called within it's class.
func generateShip() {
self.removeAllChildren()
let SKSprite1 = SKSpriteNode(imageNamed: "Spaceship")
SKSprite1.size = CGSize(width: CGFloat(arc4random_uniform(UInt32(200))), height: CGFloat(arc4random_uniform(UInt32(200))))
SKSprite1.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
self.addChild(SKSprite1)
}
//if I called it here with touchesBegan, it worked.
Now, in my ViewController, I created a simple button and connected it to its swift file. I did a few things before the following code, but this is the part I'm having trouble with.
let gameScene = GameScene()
#IBAction func newShip(sender: AnyObject) {
gameScene.generateShip() // am I doing this wrong?
}
When I run the app, and click the button, nothing happens. What do I need to change/ do to make function.... well ... function?
Links: Full Code , Iphone
The problem is in this code:
let gameScene = GameScene()
override func viewDidLoad() {
super.viewDidLoad()
let skView = self.view as! SKView
skView.presentScene(GameScene(fileNamed: "GameScene"))
}
Your gameScene property isn't the same as the scene you presented. Those are two separate objects. You need to do two things.
Change your gameScene declaration to be like the new one you created in your presenting code. Like so:
let gameScene = GameScene(fileNamed: "GameScene")
In viewDidLoad, present that GameScene object instead of making a new one, like this:
skView.presentScene(gameScene)
This way you create one GameScene object, present it, and call generateShip() on the same object.
In theory Swift should be able to link all the files automatically that are inside of your project, so your code should be able to work on its own. However, the only issue that might arise is because you are now relying on the StoryBoard as well, so you have to make sure thats linked correctly.
Make sure that its linked correctly.
Check out this question for more information on linking with Swift.
EDIT: Also, look at your code. You are calling the function for the space ship on the class itself and not on an instance of it, so thats why its probably not working. You need to first initialize a gameScene and then use that scene to call a method. I think it might be a lot more work so my advice would be to keep everything inside of your gameScene class and create a spriteNode button inside of there.
Your code seems fine to me.
If there was any 'access' problem with code from an unknown class / module etc. you'd get a compiler warning about it. This seems not to be the case.
My guess: your button is not wired up correctly!
Doublecheck that the #IBAction func newShip(sender: AnyObject) really gets called when you press the button: set a breakpoint inside of the newShip function.
Edit: And if it does stop there: step into the function using the debugger.
You are incorrectly using two different instances of GameScene in your view controller! One is added to the view hierarchy, and the OTHER instance is called when the button is tapped.
This can not be seen from the code in the original question, but from the code you provided in a comment: screenshot of full code
In your view controller your are first creating a GameScene with:
let gameScene = GameScene()
but then you are creating and adding a second instance of GameScene to the view hierarchy with:
skView.presentScene(GameScene(fileNamed:"GameScene"))
and your button action calls the generateShip() method on the first object stored in gameScene, that has not been presented by the SKView.
So the solution is to change the above code to:
let gameScene = GameScene(fileNamed:"GameScene"))
and later:
skView.presentScene(gameScene)

Showing keyboard on SKScene and performing action on a sprite node based on key tapped

I am trying to learn SpriteKit and play with some simple sample apps.
Currently I am trying to achieve below things:
Show keyboard over SKScene
If user taps 'A', perform some action on a sprite node, if user taps 'B' perform some other action on other sprite nodes
To achieve 1st requirement I tried below steps:
Step 1: In GameViewController.swift, added a reference to the view.
var keyView: KeyboardDummyView?
Step 2: In viewDidLoad(), created the view, assigned it to the ivar, and added it to the controller's view. Also assigned "self" to a backreference ivar in GameScene.
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// So scene can forward touch event back here.
scene.gvc = self
// Other config
keyView = KeyboardDummyView(frame: self.view.frame)
self.view.addSubview(keyView!)
}
}
Step 3: Added a function to handle a tap/touch.
func handleTap() {
// Show the keyboard
keyView!.becomeFirstResponder()
}
Step 4: In GameScene.swift added the reference to the controller.
var gvc: GameViewController?
Step 5: In touchesBegan handled tap.
gvc!.handleTap()
But for some reasons it is not showing the keyboard when screen is tapped :(
Here is the code base: KeyboardOverSKScene
Please suggest if I am doing any thing wrong?
Note: I am trying this in Xcode 6.0.1

Resources