Could not cast value of type 'SKSpriteNode' to CustomClass from .sks

Hi all,


This is my first attempt at a SpriteKit project and Swift so please bear with me.


I have a SpriteKit scene and I've added a sprite with a Custom Class 'Player', which subclasses SKSpriteNode. In my scene swift file I am trying to call the Player setup function but when I run the project I get the error "Could not cast value of type 'SKSpriteNode' to MyAppName.Player"

Here is the following code I'm using, nothing major at the moment:


Level.swift

import SpriteKit
class LevelOne: SKScene {

    var thePlayer:Player = Player()

    override func didMoveToView(view: SKView) {
    
    
        if (self.childNodeWithName("Player") != nil) {
        
            thePlayer = self.childNodeWithName("Player") as! Player
            thePlayer.setupPlayer()
        
        }
    
    
    }
... etc


Player.swift

import Foundation
import SpriteKit
class Player: SKSpriteNode {

    func setupPlayer() {
     
        print("this actually ran")
     
    }
}


I've named my sprite 'Player' and added 'Player' as my Custom Class for the Sprite, but I get the can't cast error.


I'm using xCode 7.1.1 and I've also tried in 7.2 beta but still the same. I'm building in tvOS. I have an example project from a lesson which works and I've compared everything and there is no difference. I've looked at Targets and everything has been added as a Target to my project.


Any thoughts?


Regards

Paul

Accepted Reply

I'm not saying this is the correct answer but if I add my app name into the Module under Custom Class in the Inspector of the sks file when my sprite is selected, I now get it to build.


Surely I shouldn't need to add it to the Modules if it's targetted correctly???

Replies

Your code snippet is missing things that makes it hard to understand what you have/haven't coded and how it works....for example you don't acutally set the name property of Player in that code nor do you do scene.addChild(...) to add the sprite to the scene.


So, your scene code is looking up a child node by name and then assigning it to the already created (non-optional) thePlayer variable - why is doing that?


In your scene, is it not just the case that you want to do something like this rough example:


var thePlayer: Player?  //optional to start with

override func didMoveToView(view: SKView) {

     thePlayer = Player()
     if let sprite = thePlayer // unwrap the optional player
     {
          sprite.setupPlayer()
          self.addChild(sprite)
     }

...
}


Also, you might want to consider using init or convenience init methods for your subclass instead of setupPlayer ... unless the setup needs to be done later on after creation of course.

Hi there,


Many thanks for your reply. I'm actually following a tutorial, so only using the code that they have written. The actual name of the Player is set in the .sks. I've added a Sprite in the .sks, given it a name in the Inspector and added the Custom Class there too.


I'm guessing the condition is just checking to see if that Player sprite has been added to the sks, and then assigning it to the thePlayer variable. Not saying this is the best/right way of doing it, just how it was written.


I probably will use an init method, I was just putting a function there to see if I could get it to run. But getting the can't cast error before it even calls the function.


The code from the tutorial works, just not in my project. I just can't see where the problem is.

I'm not saying this is the correct answer but if I add my app name into the Module under Custom Class in the Inspector of the sks file when my sprite is selected, I now get it to build.


Surely I shouldn't need to add it to the Modules if it's targetted correctly???

EDIT: Works for iOS 9, but not for OS X 10.11, even though DemoBots has no problem.


--------


Thank you so much! This did the trick for me, too. I explain my issue in details, so people could google this thread easier.


This is also my first SpriteKit project and it is pretty much copy-pasted from inspired by DemoBots. In the original HomeScene.sks file the ProceedToNextScene (SKSpriteNode) button has just ButtonNode as a Custom Class and the Module field is empty, but it works just like that.


I tried to do everything the same, but the required init?(coder aDecoder: NSCoder) initializer from ButtonNode.swift was never called. I struggled a lot to track down the real issue, because the original code (line 11 below) just returns nil if the type casting fails. It acts like there were no buttons in the scene at all. Finally I managed to track down the issue by trying this (line 6):


/// Searches the scene for all `ButtonNode`s.
func findAllButtonsInScene() -> [ButtonNode] {

    // FIXME: Should work on Xcode7 and iOS 9, but doesn't.
    // Reference: https://forums.developer.apple.com/thread/24996
    print(self.childNodeWithName("//ProceedToNextScene") as! ButtonNode)
    // => Could not cast value of type 'SKSpriteNode' to 'Project_Name_Changed.ButtonNode'.

    // This is the original code:
    return ButtonIdentifier.allButtonIdentifiers.flatMap { buttonIdentifier in
        return childNodeWithName("//\(buttonIdentifier.rawValue)") as? ButtonNode
    }


I really didn't believe that the Module field would make any difference, but for some reason it is required for my project. For example, if your project name is "Mad Scientist", just write "Mad_Scientist" to the Module field and voila.


So, it works for now, but I hope someone can explain the reason and possible solution for the misbehavior.

I would guess that it works without specifing the module name when the module name is equal to the Project Name. The module name replaces chars like the whitespace in your project name with an underscore, maybe that is the reason why it doesn't work in your project.

UPDATE --- 100% working solution for me:

I'm not sure if the Product Module Name was the key here or if the Product Name or Project Name matters, too. At first I just changed the Product Name fields for both targets and it changed the Product Module Name at the same time. I also removed the Module Name values from the buttons in .sks files. Before these steps the type casting to ButtonNode worked for iOS 9, but failed in the OS X target. Now it was opposite. I tried to debug this and I had a feeling that maybe the problem could be related to the Project Name or Product Name, because they were written like "Mad Scientist", but the Product Module Name was "Mad_Scientist".


Finally I decided to played safe and camelize all the Mad Scientists, so the Project Name, Product Name, Product Module Name and directories and everything had equal format. So everything was MadScientist. And guess what...? It works on all targets now!


I'm sure that the project name can contain spaces and maybe other special characters too, but because I don't understand all the configurations and module suff, I think it's just easier to go with with the camelized project names right from the start.


----------


Thank you, Nightmare_82!


Haven't tested that out yet, but your thoughts makes a lot of sense to me. My project is Universal just like DemoBots, but if I compare the Build Settings => Packaging of the projects I can see that in my project the Product Name varies with the target, but in DemoBots the Project Name is always DemoBots. Moreover DemoBots has the value DemoBots in the field Product Module Name, which is hidden from the view by default if the field is empty (choose All from upper left corner instead of Basics to see the Product Module Name in your own project).


I wonder if I should have created the project and targets with equal names in the first place. We'll see... Hopefully I won't mess up the whole project by changing the packaging settings. I'll tell you how that worked out.