Gamescene.swift and Gamescene.sks not working together - swift

I am having a problem with my xCode project. I have a whole gamescene.swift file worth of code (600 lines). But the code will not run. When i run my game in simulator the simulator shows the standard gamescene.sks color, alongside with my ads, but it is not showing any of my code. I have already checked that the custom class in gamescene.sks is correct. Everything is working, expect for the gamescene.swift code, it will not run.
import UIKit
import SpriteKit
import GameplayKit
import GoogleMobileAds
class GameViewController: UIViewController {
#IBOutlet weak var GoogleBannerView: GADBannerView!
#IBOutlet weak var MainMenu: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
GoogleBannerView.adUnitID = "ca-app-pub-13***46014918193/236762****"
GoogleBannerView.rootViewController = self
GoogleBannerView.load(GADRequest())
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .resizeFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
view.showsPhysics = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}

i managed to fix the problem after countless hours of trying.
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .resizeFill
this is the working code. the problem was that instead of specifying what scene i was suppose to lead i had written
if let scene = SKScene(fileNamed: "GameScene")
the simple fix was changing the "SKScene" to my SKScene name.
if let scene = GameScene(Filenamed: "GameScene")
Hope this helps the countless other out there that are having trouble with this problem!

I've had similar problems to this on SOME of my projects but not all. Even on projects that were working and I duplicated the code and scene files for another project they would stop working. I discovered that if there are any spaces or special characters in the your project name the scene files and items in the sks files will not load unless you put in the "Module" name below the Custom Class type.
For me my project was named "Crag & Pig" before any of the sks file would register I had to enter "Crag_Pig" in the "Module" for all of the items in the sks file.
Interestingly, on any projects that didn't have spaces or special characters I didn't have to enter any thing for Module

Thanks. Even the default template in Xcode for SpriteKit Games has this very issue. That since the release of swift 4 + iOS 11.
I always thought it was me somehow, but never took the time to find why. Until now out of curiosity. I changed the line you wrote/fixed and voila.
Just changed the capitals in the parameter fileNamed: of your line
if let scene = GameScene(fileNamed: "GameScene")

Related

SKView does not cover the entire screen area

I started studying SpriteKit recently with one famous textbook and immediately a problem arose. In all video lessons, SKView covers the entire display area. I have the opposite situation and I don't know how to fix it. Here are the screenshots:
iPhone with notch
classic iPhone
Here is the source code:
GameViewController.swift:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set the scale mode to scale to fit the window
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
let scene = GameScene(size: CGSize(width: 2000, height: 1500))
scene.scaleMode = .aspectFill
// Present the scene
skView.presentScene(scene)
}
override var shouldAutorotate: Bool {
return true
}
override var prefersStatusBarHidden: Bool {
return true
}
}
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
backgroundColor = .brown
}
}
I don't know if anyone had such problems before the macOS 11 and Xcode 12, but now they are.
How to stretch SKView across all screen?
Set your project's Lauch screen file to "Main".

Adding a custom GKComponent to an entity in Xcode SpriteKit scene editor sets GKScene.rootNode to nil

When I add a CustomComponent (GKComponent) to an entity in Xcode SpriteKit scene editor and try to load that .sks file using a GKScene.init, GKScene.rootNode is not set. Even stranger, this happens only on iOS 13 and not on iOS 12.
I have a small sprite kit github project setup that demonstrates this issue clearly. Just run the app on an iOS 13 emulator to reproduce the issue. https://github.com/hdsenevi/answers-gkscene-rootnode-nil-bug
If I remove CustomComponent from SpriteKit scene editor entity/sprite, then it runs fine. ie: loads SKScene into GKScene.rootNode
Is there any other special modifications that needs to happen when adding GKComponents from Xcode SpriteKit scene editor?
Am I missing something obvious here?
And why would this code work without an issue on iOS 12 and not iOS 13?
Has SpriteKit functionality changed with regards to this in iOS 13?
For reference
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Load 'GameScene.sks' as a GKScene. This provides gameplay related content
// including entities and graphs.
if let scene = GKScene(fileNamed: "GameScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! GameScene? {
// Copy gameplay related content over to the scene
sceneNode.entities = scene.entities
sceneNode.graphs = scene.graphs
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFill
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
} else {
print("Error. No GameScene was found on GKScene.rootNode")
}
} else {
print("Error loading GKScene file GameScene")
}
}
}
import SpriteKit
import GameplayKit
class CustomComponent: GKComponent {
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didAddToEntity() {
guard let gkSkNodeComponent = self.entity?.component(ofType: GKSKNodeComponent.self) else {
print("Error. Cannot obtain a reference to GKSKNodeComponent")
return
}
if let sprite = gkSkNodeComponent.node as? SKSpriteNode {
sprite.texture?.filteringMode = .nearest
}
}
}
Update
Few details on my setup
macOS Mojave 10.14.6
Xcode 11.0 (tried on 10.1 and 10.3, same behaviour)
To solve the problem, your component needs to override variable supportsSecureCoding and it must return true.
This worked for me:
override class var supportsSecureCoding: Bool {
return true
}
I have also just come across this and have sent a Feedback with a link to this article as it's a great description of the problem.
It was happening to me too.
I have a Color Sprite with body type Alpha Mask, I found out that if I change the body type to any other type it fixes this and the rootNode works again.
Prior to iOS 13, I accessed the root node of GKScene. For reasons still unclear to me, that results in nil with iOS 13+. Lo and behold, though, you can access the legacy "root node" directly from the GameScene. I did not have to alter any custom classes in any way in order to restore full functionality.
//From GameViewController (UIViewController):
- (void)viewDidLoad {
[super viewDidLoad];
GameScene *sceneNode;
NSOperatingSystemVersion ios13_0_0 = (NSOperatingSystemVersion){13, 0, 0};
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios13_0_0]) {
// iOS 13.0.0 and above logic for rootNode
// Load the SKScene from 'GameScene.sks'
GameScene *scene = (GameScene *)[SKScene nodeWithFileNamed:#"GameScene"];
SKView *skView = (SKView *)self.view;
// The fix for use in iOS 13.0+
sceneNode = scene;
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene
[skView presentScene:scene];
} else {
// prior to iOS 13.0.0 logic
GKScene *scene = [GKScene sceneWithFileNamed:#"GameScene"];
SKView *skView = (SKView *)self.view;
// This will result in nil if used in iOS 13.0+
sceneNode = (GameScene *)(scene.rootNode);
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene
[skView presentScene:sceneNode];
}
}
This instantiates an instance of GameScene (SKScene). In GameScene, I am able to access custom nodes that I've added into the scene with a custom name via:
for (SKNode *node in self.children)
{
if ([node.name containsString:#"NameOfNodeHere"])
{
// Access your custom node in your .sks here
}
}
So I've got a rather hacky workaround if you want to keep the same style of defining components in the SK Scene Editor by using UserData.
In the SK Scene Editor populate nodes UserData with attributes that your code then uses to turn into components, and then automate setup GKSKNodeComponents.
I've made a class TemplateScene which I put as the scene of the sks file.
import Foundation
import SpriteKit
import GameplayKit
class TemplateScene: SKScene {
lazy var entities: [GKEntity] = {
var entities: [GKEntity] = []
// applyAllChildren is an extension I made that simple recurses all children, children's children etc
self.applyAllChildren { node in
if let userData = node.userData {
var components: [GKComponent] = []
if userData["c.BasicComponent"] as? Bool == true {
components.append(BasicComponent())
}
if components.count > 0 {
components.append(GKSKNodeComponent(node: node))
let entity = GKEntity()
for component in components {
entity.addComponent(component)
}
entities.append(entity)
}
}
}
return entities
}()
}
Just keep extending the code to handle more components. If you have component attributes then have UserData in the form of c.BasicComponent.x etc
(To be clear, this is a hack to get around the fact that Apple has a serious bug that makes a large chunk of SpriteKit Editor functionality unsuable in an entire version of iOS. Maybe it's fixed in iOS 14?)

Swift blank app

I want to learn SpriteKit and am following this tutorial, https://www.raywenderlich.com/187645/spritekit-tutorial-for-beginners-2
I have copied and pasted the same code from the tutorial into GameScene.swift as well as moved the picture of the ninja into Assets.xcassets
However, when I get two errors,
Before I run it I get
Forced cast of SkView to same type has no effect
After I run it I also get
Thread 1: EXC_BAD_ACCESS(code=2, address=0x7ffeed594fa8
When I run it, the app is simply blank... The app should show FPS count and NodeCount?
Here's my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
skView.presentScene(scene)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
All of this code is in GameScene.swift
If someone could please help me that would be great!
UPDATE: I am using Swift 4
It seems you have an infinite recursion there. Note that you already have a GameScene instance but in the didMove you are creating another GameScene which you are presenting again which again is presented in the view and didMove is called again and so on. I think your code should look something like this:
override func didMove(to view: SKView) {
self.size = view.bounds.size
view.showsFPS = true
view.showsNodeCount = true
view.ignoresSiblingOrder = true
self.scaleMode = .resizeFill
}

Changing the initial game scene in Swift

I want to change the initial scene being presented to be another class other than the default GameScene class. From reading other questions, I understand I must change this part from the GameViewController:
if let scene = SKScene(fileNamed: "GameScene") {
print(scene)
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
So within the GameScene.swift file I am creating a new class:
class MainMenu : SKScene {
override func didMove(to view: SKView) {
print("At least it ran")
self.scene?.view?.presentScene(GameScene())
}
}
However, when I change the scene to:
if let scene = SKScene(fileNamed: "MainMenu")
When I run the project, it gets stuck, but when I run it with the string "GameScene" then it works perfectly. I am doing something wrong loading the MainMenu?
In my opinion you should separate your scenes into their own files...
Do you have an corresponding SKS file for MenuScene? You need to create one if you are trying to load it with fileNamed:
or -
Use this code to load a SKScene file that is created in code only and not in the Scene editor
if let skView = self.view as? SKView {
if skView.scene == nil {
let scene = MenuScene(size: skView.bounds.size)
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"
}

Deleting the Gamescene.sks file raises CPU to 25% on device

I just created a brand new Xcode Game Project. I deleted the Gamescene.sks file. Ran it and the cpu instantly to 25-30%. I made another Xcode project just to see if I could replicate it. Ran it before deleting the file and cpu was basically 0%, but sure enough when I deleted the file and ran it again, the cpu jumped up to the same high numbers. My goal is to subclass skscene and use that without the Gamescene.sks file. Can someone explain why this happens?
I was able to replicate this behavior just deleting the .sks file.
The problem is GameViewController is looking for that file at first loading time, as you can see here:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") { <--------------!!
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
So you need to initialize it another way. Just as an example, you could init an empty scene like this:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
let scene = SKScene(size: CGSize(width: 1000, height: 1000))
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
and you'll see 1% of CPU load.