How to inherit the SKspritenode and create by self.childNode - swift

I have a problem dealing with inheritance for the skspritenode
I have created a skspritenode in the scence and want to inherit the sksprite to a car class
Car Class
class Car : SKSpriteNode {
var CurrentLocation = 1
}
GameScene
class GameScene: SKScene {
var leftCar = Car()
func setUp() {
leftCar = self.childNode(withName: "leftCar") as! SKSpriteNode
}
}
I dont know what the meaning of Cannot assign value of type 'SKSpriteNode' to Car and how to reslove this error, thankyou for your help

leftCar is of type Car so compiler is complaining to cast childNode as Car instead of SKSpriteNode shown below.
func setUp() {
if let car = self.childNode(withName: "leftCar") as? Car {
leftCar = car
}
}
BTW, you already initialized leftCar in the same GameScene so you can simply use that instead of assigning it again.
Set custom class Car in GameScene.sks for leftCar node as shown in below image,

Related

How do you access SKNode subclass methods from SKNode.children array?

I have a scene with a number of children which are sub-classed SKNodes. I am trying to access a method for each of the child nodes, but am getting the error "Value of type 'SKNode' has no member '[member name]".
All on Swift 5 on Xcode 10.2 (if that's relevant?).
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let slim = NamedNode(name: "Slim Shady")
let snoop = NamedNode(name: "Snoop Dog")
let shaggy = NamedNode(name: "Shaggy")
self.addChild(slim)
self.addChild(snoop)
self.addChild(shaggy)
for child in self.children {
child.whatsMyName()
// ERROR: Value of type 'SKNode' has no member 'whatsMyName'
}
}
}
class NamedNode: SKNode {
var standup = false
init(name: String) {
super.init()
self.name = name
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func whatsMyName() {
print("My name is ", name as Any)
if self.name == "Slim Shady" {
standup = true
}
}
}
How can I access these methods from the Scene.children array? Is it possible or should I change tact?
Thanks!
It is said here that
The children of an SKScene are of SKNode type. You want to access a property of an SKNode subclass. At compile time, the children are seen as SKNodes.
You should try casting your child, something like
for child in self.children {
(child as? NamedNode).whatsMyName()
}
Have in mind that downcasting is usually a code smell.

Casting an SKSpriteNode in an SKReferenceNode

I've build a Scene1 in Xcode's Scene editor. And I've referenced another scene which has animation to this Scene1.
Now, I'm trying to cast-out an SKSpriteNode which is inside an SKReferenceNode.
The name of the SKSpriteNode that I'm trying to cast, on a scene which was references is: "sc01eyelid".
Any suggestions what I might do wrong here?
thank you in advance.
import SpriteKit
import GameplayKit
class Scene1: SKScene {
var misha: SKReferenceNode = SKReferenceNode()
var eyelidForScene1:SKSpriteNode = SKSpriteNode()
override func didMove(to view: SKView) {
castMishaForScene1()
castOutEyelid()
}
//Casting out misha
func castMishaForScene1() {
if let someSpriteNode:SKReferenceNode = self.childNode(withName: "mishaRefNode") as? SKReferenceNode {
misha = someSpriteNode
print("CASTED\(misha)")
}
else {
print("could not cast\(misha)")
}
}
//Casting out eyelid
func castOutEyelid() {
if let someSpriteNode:SKSpriteNode = misha.childNode(withName: "sc01eyelid") as? SKSpriteNode {
eyelidForScene1 = someSpriteNode
print("CASTED\(eyelidForScene1)")
}
else {
print("could not cast\(eyelidForScene1)")
}
}
}
In order to access any Node of the SKRefference one needs to put additional "//" in the withName statement:
if let someSpriteNode:SKSpriteNode = misha.childNode(withName: "//sc01eyelid") as? SKSpriteNode {}
So withName: "//sc01eyelid" would work to access the sc01eyelid Node.
More info here:
https://developer.apple.com/documentation/spritekit/sknode

Class extends SKScene but cannot access its attributes

I have a Menu class that extends SKScene, but I cannot access view, which is an attribute of SKScene. Instead I get the following error: "Instance member 'view' cannot be used on type Menu."
Is there anything special I need to do to access this? It seems like it should already be able to call that.
Here is a shortened version of the class:
class Menu: SKScene {
var blueButton: Button!
var touchNode: SKSpriteNode!
var newScene: SKScene?
override func didMove(to view: SKView) {
blueButton = self.childNode(withName: "Start") as! Button
}
...
class func transitionToScene(node: String) {
var scene = SKScene()
switch node {
case "NewGame":
scene = GameScene()
default:
debugPrint("transition to scene called on no button")
}
scene.scaleMode = SKSceneScaleMode.aspectFill
//(self.view as! SKView).presentScene(scene)
self.view.presentScene(scene)
}
The issue comes at the last line. Above it I commented out a different attempt, but this also did not work.
You have to note a difference between instance methods and type methods. Instance methods are called on an instance of a particular type. You define them like this:
class SomeClass {
func myInstanceMethod() {
}
}
Type methods, are methods that are called on a type itself. You define them by using static keyword, or as in your example class keyword (class keyword allows subclasses to override the superclass’s implementation of that method).
So, in short you should use an instance method, because self refers in your case to the current instance, not to the Menu type (just remove the class keyword).

Custom Class Sprite kit

I'm trying to give my main character a Custom Class so that I can define all his behaviors inside the defined class.
I already did something like this, by using protocols:
protocol CustomNodeEvents{
func didMoveToScene()
}
This protocol is in the GameScene file, before the class starts.
Then I call the function like this in the GameScene class:
override func didMove(to view: SKView) {
if let customnode = player as? CustomNodeEvents{
customnode.didMoveToScene()
}
}
Where the player is defined as:
player = self.childNode(withName: "//player") as! SKSpriteNode
also inside the didMove(to view: SKView) function.
Now I created another file (my custom class file) and I write:
import SpriteKit
class Custom: SKSpriteNode, CustomNodeEvents{
func didMoveToScene() {
print("It Worked")
}
}
I don't have any errors but it doesn't actually run the block of code inside my Custom class (in the GameScene.sks file I already attached the class to the player).
My question is how can I make it work?
And the second question is, is this the best way to define a Custom Class and "connect" it with other classes?
EDIT:
class GameScene: SKScene {
var player: SKSpriteNode!
override func didMove(to view: SKView) {
player = self.childNode(withName: "//player") as! SKSpriteNode
if let customnode = player as? CustomNodeEvents{
customnode.didMoveToScene()
}
Not sure where the problem is, but this code works for me:
import SpriteKit
protocol CustomNodeEvents
{
func didMoveToScene()
}
class Custom : SKSpriteNode,CustomNodeEvents
{
func didMoveToScene()
{
print("It Worked")
}
}
class GameScene:SKScene{
var player : SKSpriteNode!
override func didMove(to view: SKView) {
player = self.childNode(withName: "//player") as! SKSpriteNode
if let customnode = player as? CustomNodeEvents
{
customnode.didMoveToScene()
}
else
{
print("Error creating node")
}
}
}
I can only conclude that there is an issue in the sks file, and the player we are grabbing is not the player you are looking for, or the player is not of class Custom. (Note I placed the player sprite at the top most level of the scene)
Seems like you are casting player as a SpriteNode and then later trying to upcast it to a CustomNodeEvents protocol. Assuming your sprite custom class is set in the .sks file as you say, SpriteKit should be creating an instance of your custom class so you can just do:
if let customNode = self.childNode(withName: "//player") as! Custom {
customNode.didMoveToScene()
}

How do edit a sprite based on its name?

So in my game i have a function that spawns coins,they are given the name "coin", Now I have no way to reference the coins,example to kill them or move them.So what I'm trying to do is make a reference to be able to use in my code to just change its zPosition.
Everytime I run my app and have a function run that uses the coinRef [ex. to change the zPosition], the app crashes with the error:
'Thread 1 EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)'
Heres my code:
let coinRef: SKSpriteNode = self.childNodeWithName("coin")! as! SKSpriteNode
func hideCoins() {
coinRef.zPosition = -1
}
func showCoins() {
coinRef.zPosition = 101
}
func killCoins() {
coinRef.removeFromParent()
}
Looking at what you write
So in my game i have a function that spawns coins,they are given the name "coin"
it looks like there are multiple coins in your scene. As you can imagine a single name coin is not enough to univocally identify more then 1 coin :)
We'll need a way do identity multiple coins.
1. The Coin class
class Coin: SKSpriteNode {
private static var lastID: UInt = 0
let id:UInt
init() {
self.id = Coin.lastID++
let texture = SKTexture(imageNamed: "coin")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.name = "coin"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As you can see Coin has an internal mechanism to assign a new id to each new instance. You can use this id to reference the coins in your scene.
let coin0 = Coin()
coin0.id // 0
let coin1 = Coin()
coin1.id // 1
let coin2 = Coin()
coin2.id // 2
2. Managing your coins
class GameScene: SKScene {
func retrieveCoin(id:UInt) -> Coin? {
return children.filter { ($0 as? Coin)?.id == id }.first as? Coin
}
func hideCoin(id:UInt) {
retrieveCoin(id)?.hidden = true
}
func showCoin(id:UInt) {
retrieveCoin(id)?.hidden = true
}
func deleteCoin(id:UInt) {
retrieveCoin(id)?.removeFromParent()
}
}
The retrieveCoin method returns (if does exist) a coin with the specified id. Otherwise nil is returned.
The hideCoin and showCoin do change the hidden property to change its visibility.
Finally deleteCoin remove from the scene the Coin with the specified id.
Try this. Initialise coinRef before the didMoveToView function, and then give coinRef its value in the didMoveToView function.
class scene : SKScene {
let coinRef: SKSpriteNode = SKSpriteNode()
override func didMoveToView(view: SKView) {
coinRef: SKSpriteNode = self.childNodeWithName("coin")! as! SKSpriteNode
}
func hideCoins() {
coinRef.zPosition = -1
}
func showCoins() {
coinRef.zPosition = 101
}
func killCoins() {
coinRef.removeFromParent()
}
}