Subclassing SKSpriteNode - swift

i'm trying to create a custom SKSpriteNode, by subclassing SKSpriteNode, using swift
here the code:
import Foundation
import SpriteKit
class CustomNode:SKSpriteNode{
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init() {
super.init()
}
}
When i add it to the scene i got
fatal error: use of unimplemented initializer 'init(texture:color:size:)' for class 'Sandbox.CustomNode'
if i change
super.init()
for
super.init(texture: nil, color:UIColor.whiteColor(),size: CGRect(0,0,100,100))
i got the compiler error:"Extra Argument 'color' in call.
I'm using XCode 6, beta 7. Its a iOS project.

That error message isn't exactly obvious, but it can be produced by incorrectly passing arguments to a method. In this case, the problem is that you're passing a CGRect, where the argument is supposed to be CGSize. This code should work for you.
super.init(texture: nil, color:UIColor.whiteColor(),size: CGSize(width: 100.0, height: 100.0))

Related

Why am I unable to subclass the SKSpriteNode

Forgive me, I might have asked this before, but it hit me in a different manner, and am still learning Swift
In my main scene I can easily initialize a node and then manipulate it as I like:
let myGSlot = SKSpriteNode(color : .green, size: CGSize(width: 100.0, height: 100.0))
However when I try to subclass it:
class GuessSlot : SKSpriteNode{
init(color: SKColor, size: CGSize) {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
No matter what I do, the editor gives me many errors. The main one being:
Must call a designated initializer of the superclass 'SKSpriteNode'
Whether I put it in init() or super.init()
I know I'm new to Swift, but this is killing me!
********* latest update and the only way I can get it to compiles but still crashes with the error:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeec64aff0)
In the debugger I can see that zero values are coming in for the parameters
convenience init(color: SKColor, size: CGSize) {
self.init(color: color, size: size)
}
I do feel less stupid when I see all the threads out there with confusion over this info
In Swift, you need to call an initializer that is implemented directly from your super class, in this case, SKSpriteNode. super.init() is implemented by another class that is in SKSpriteNode's inheritance tree, like SKNode or NSObject. You can always check the documentation for which constructors can you call for each class. It's very important to pay understand that you need to call designated initializers in your subclass
For example, you can do
class GuessSlot : SKSpriteNode{
init(color: SKColor, size: CGSize) {
super.init(texture: nil, color: color, size: size)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I am not sure where the disconnect is, but as mentioned in the comments, you just needed to override the proper designated init texture, color,size.
class TestSpriteNode : SKSpriteNode{
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture,color:color,size:size)
}
}
This will get you access to all of the inits of your parent class.
You will now be able to do var s = TestSpriteNode(color:.red,size:CGSize(width:1,height:1))
If you need to override this convenience init, you need to refer to one of the designated inits you overrode, not the convenience init.
class TestSpriteNode : SKSpriteNode{
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture,color:color,size:size)
}
convenience init(color: UIColor, size: CGSize) {
self.init(texture: nil,color:color,size:size)
}
}

Initialize a Custom SKSpriteNode with its properties from the editor

I made my own simple custom class called SKGlowNode as a subclass of SKSpriteNode. I originally wanted to be able to create a custom class and input all the init parameters in the editor like you can with a SKSpriteNode. However, I found out that this is not possible yet and I had to try to do it with user data. I set up my node in the editor like this:
And simply did the custom class like this:
Here is my class for SKGlowNode
import Foundation
import SpriteKit
class SKGlowNode: SKSpriteNode{
let colors = [UIColor.blue,UIColor.green,UIColor.red,UIColor.orange,UIColor.purple,UIColor.yellow,UIColor.white,UIColor.magenta]
required init?(coder aDecoder: NSCoder) {
super.init(texture: texture, color: .black, size: size)//error here
let glowIndex = self.userData?.value(forKey: "glowColorIndex") as? Int ?? 0
let glowTexture = self.userData?.value(forKey: "glowTexture") as? String ?? "squareBg"
addGlow(radius: 75, texture: SKTexture(imageNamed: glowTexture), color: colors[glowIndex])
//This glow function works and is an extension from a SKSpriteNode where it creates a glow from a texture with certain color
}
}
Obviously I get an error on the line specified because I cant initialize the superclass using those variables, but I have no idea what to put in order to get it to initialize with the values already defined in the editor as it would work if there was no custom class. Thanks
You need to have the appropriate Super.init for your class init.
If you are instantiating the subclassed SKSpriteNode in an init() whether it be the default or a custom init with custom parameters you use
super.init(texture: SKTexture, color: SKColor, size: CGSize)
if you are instantiating it from an sks scene file then required init?(coder aDecoder: NSCoder) gets called and you need to have
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }

Good day, In Spritekit my code fails because I am obliged by GKComponent to implement:
a. a "required init" I do not need.
b. At run time it calls this instead of my normal init() and fails.
c. super.init(coder: aDecoder) does not solve my problem of calling it
Question: A Solution to call my init instead of this forced required init
In other answers suggest a solution to use super.init(coder: aDecoder) but it has not solved my problem of not calling it.
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//This code is supposed to add a simple eplipse under the sprite to make //a shadow effect by making it a GKComponent and add it to a GKEntity.
import Foundation
import GameplayKit
import SpriteKit
//GKEntity to add GKComponents
class Entity: GKEntity{
//A variable that is a GKComponent defined as ShadowComponent: GKComponent
var shadow: ShadowComponent
//My init
override init() {
shadow = ShadowComponent(size: CGSize(width: 50, height: 50), offset: CGPoint(x: 0, y: -20))
super.init()
addComponent(shadow)
}
//Forced by compiler to have it
**required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}**
}
The required init is required by the system; it will be called when the system autoload your component. With the interface builder for example. You can take a look at this answer for more informations. Is your entity added in the Scene editor?
So you need to focus on the way your Entity is created. If you want to call your custom init, you need to init it programmatically.
I can suggest to make the required init work, if you want to keep using your Entity in your Scene editor:
required init?(coder aDecoder: NSCoder) {
shadow = ShadowComponent(size: CGSize(width: 50, height: 50), offset: CGPoint(x: 0, y: -20))
super.init(coder: aDecoder)
addComponent(shadow)
}
All variables need to have a value assigned to it before init. Because your shadow does not have a value, the compiler is forcing you to override required init so that you can give shadow a value.
To fix this, just make shadow lazy:
lazy var shadow =
{
[unowned self] in
let shadow = ShadowComponent(size: CGSize(width: 50, height: 50), offset: CGPoint(x: 0, y: -20))
addComponent(shadow)
return shadow
}()
Then, the first time shadow is used, it will create and add the component for you.
The only reason we need to made it lazy is because of the addComponent aspect of it. Your code could be written like this to avoid having to use a lazy, but you would need to call a function to add your component.
let shadow = ShadowComponent(size: CGSize(width: 50, height: 50), offset: CGPoint(x: 0, y: -20))
func addComponents(){
addComponent(shadow)
}

Editing properties of an SKSpriteNode custom class?

In my custom SKSpriteNode class, I want to be able to change properties such as anchorPoint, posistion, etc. within the custom class so I don't need to elsewhere.
import Foundation
import SpriteKit
open class Crank:SKSpriteNode {
init() {
super.init(texture: SKTexture(imageNamed: "crank"), color:
NSColor.white, size: CGSize(width: 155.0, height: 188.0))
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
How would I edit other properties?
import Foundation
import SpriteKit
class Crank: SKSpriteNode {
init(imageNamed image: String, position at: CGPoint, withAnchor anchor: CGPoint) {
super.init(imageNamed: image)
self.position = position
self.anchorPoint = anchor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
(Written from memory, so may contain small errors, but is broadly correct).
and then in your main code:
let myCrank = Crank(imageNamed: "Crank.png", at: CGPoint(300, 500), withAnchor: CGpointZero)
My answer notwithstanding, Alessandro has a point. I think that it's better to set 'standard' properties in the normal place.
If you are going to set any of the standard SKSpriteNode properties inside the actual class, then I think specifying them in an initialiser is good practice as it makes the m more visible,. Debugging a program where you don't appears to set a node's position, or texture etc. would be problematic.
Another way to do it would be to create a protocol and apply it to the standard SpriteKit classes that you are subclassing (or their ancestor).
For example:
protocol CustomSKNode
{
var position:CGFloat { get set }
var zRotation:CGFloat { get set }
// ...
}
extension SKNode: CustomSKNode {}
Once you've done this somewhere in your project, you'll be able to access these properties of on any of your custom SKNode or SKSpriteNode without having to import SpriteKit.

Instantiating a base class inheriting from SKSpriteNode in Swift

I'm trying to write a generic Alien 'blueprint' class for my first iPhone game. This class will contain a handful of properties and methods that will be inherited by all the actual alien subclasses. That said, Alien really shouldn't have a texture and shouldn't have its own set of defined values.
//Generic alien type: a blue-print of sorts
class Alien:SKSpriteNode{
let velocityVector:CGVector
let startPos:CGPoint
init(texture:SKTexture, startPosition startPos:CGPoint,moveSpeed: CGFloat,velocityVector:CGVector){
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.velocityVector = normalizeVector(velocityVector)
self.position = position
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Every time I try setting this up I run into a handful of errors such as :
"Use of instance member 'normalizeVector' on type GameScene; did you mean to use a value of type GameScene instead?"
normalizeVector is a function that is written above the Alien class (within GameScene). I'm not sure what this means, as this is a normal function within the GameScene. If I remove this bit, I still receive the error:
"Property self.velocityVector is not initialized at super.init call"
I'm confused because I had thought that super.init() was only necessary because I'm making a subclass of SKSpriteNode and that it wouldn't need to have the subclass-specific properties passed to it.
Lastly, is there a way to call super.init() without having an actual texture as this blueprint class isn't ever going to be "displayed"?
After looking through Apples Documentation on classes/initialization I'm still stuck. Any help would be great, thanks.
You have a couple of issues, first of all, as the error message says, you need to initialize all variables before you call super.init:
init(texture:SKTexture, startPosition startPos:CGPoint,moveSpeed: CGFloat,velocityVector:CGVector){
self.velocityVector = Alien.normalizeVector(velocityVector)
self.startPos = startPos
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.position = position
}
Second, it sounds like you're defining normalizeVector in a subclass or somewhere else? What I did was to define it as a static method (since it's used before this is initialized, it can't be an instance method) and then you can use it in the init method. Put it all together and it looks like:
class Alien:SKSpriteNode {
static func normalizeVector(vector:CGVector) -> CGVector {
let len = sqrt(vector.dx * vector.dx + vector.dy * vector.dy)
return CGVector(dx:vector.dx / len, dy:vector.dy / len)
}
let velocityVector:CGVector
let startPos:CGPoint
init(texture:SKTexture, startPosition startPos:CGPoint,moveSpeed: CGFloat,velocityVector:CGVector){
self.velocityVector = Alien.normalizeVector(velocityVector)
self.startPos = startPos
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.position = position
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You declared velocityVector as a non optional property so of course if your don't populate it within your initializer you get a compiler error.
This is why when you comment this line your get a compile error.
self.velocityVector = normalizeVector(velocityVector)