Using properties of SKSpriteNode for subclass - swift

this is more of a general question about subclasses and inheritance within Swift 4.
In Apple's developer guide, a subclass ("anySubclass") of a custom class ("anySuperclass") can directly access/inherit the parent's properties, e.g.:
anySubclass.anyProperty = xyz (anyProperty was only defined within the Superclass)
To my question:
I'm currently working on my first game using SpriteKit and wanted to create subclasses of SKSpriteNode, one of which should be clBackGround.
If I just define it as a subclass of SKSpriteNode, i.e.
class clBackGround:SKSpriteNode {
}
I would think that I could simply use all properties associated with SKSpriteNode just like this:
var bg1 = clBackGround()
bg1.size = ...
.
.
.
This does not seem to be working however. So my questions is:
Do I need to initialize all the properties of SKSpriteNode I want to use within clBackGround (using a designated initializer)? How can I then assign an image to an instance of this subclass?
Any help would be much appreciated!

Not sure if this is what you're asking, but, generally, if you create a subclass of an object, you don't need to do anything special to access the superclass's properties. They'll be initialized in the usual fashion.
However, you DO have to ensure that the superclass's initializers get called in a proper manner if you have your own initializers for your subclass.
In the case of a subclass of SKSpriteNode, if you define your own initializer for the subclass, that initializer will need to call the designated superclass initializer, which passes in the texture, color, and size to use for the sprite:
init(enemyType:String) {
let texture = SKTexture(imageNamed: "enemy_\(enemyType)")
let color = UIColor.black
let size = texture.size()
super.init(texture: texture, color: color, size: size)
}

Related

How do you add a child of a custom class to GameScene?

I have a custom class named Test that extends SKShapeNode with 4 SKShapeNodes as its child, and I would like to add them as a child of GameScene. However, when I try to execute addChild() in GameScene, I end up getting the error signal SIGABRT.
In my custom class I declare:
let shapeR = SKShapeNode()
let shapeG = SKShapeNode()
let shapeY = SKShapeNode()
let shapeB = SKShapeNode()
Then in override init(), I call the addChild() methods:
addChild(shapeR)
addChild(shapeG)
addChild(shapeY)
addChild(shapeB)
Finally in GameScene, I iterate through the children with (test is defined as Test()):
for child in test.children {
addChild(child)
}
How do I fix this?
Edit: I need 4 SKShapeNodes since I want 4 different colors in my custom SKShapeNode object.
Nevermind I just had to addChild() the entire class and not just separate SKShapeNodes

Reuse same sprite node within multiple scenes in sprite kit using Swift

I create sprite node in my GameScene as the following. I would like to reuse createNodeA1 or nodeA1 in other SKScene. How can I do that?
import SpriteKit
class GameScene: SKScene {
var nodeA1: SKNode!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
// Add sprite node to the scene
nodeA1 = createNodeA1()
addChild(nodeA1)
}
}
// Create dot 1
func createNodeA1() -> SKNode {
let spriteNode = SKNode()
spriteNode.position = CGPointMake(CGRectGetMidX(self.frame)/1.5, CGRectGetMidY(self.frame)/2.0)
let sprite = SKSpriteNode(imageNamed: "dot_1")
sprite.zPosition = 3.0
sprite.name = "A1_Broj"
spriteNode.addChild(sprite)
return spriteNode
}
}
There is a few ways to do this.
You could subclass your other scenes to be subclass of the scene with the loadNode function which gives those scenes access to that function.
I asked a question about this last year
Swift multiple level scenes
Another way that might be a bit easier if you are not comfortable with scene subclassing is to just create a subclass of the node itself.
So you create a class
enum EnemyType {
case Normal
case Special
}
class NodeA1: SKSpriteNode {
init(imageNamed: String, enemyType: EnemyType) {
let texture = SKTexture(imageNamed: imageNamed)
if enemyType == .Normal {
super.init(texture: texture, color: SKColor.clearColor(), size: texture.size())
else {
// other init
}
self.zPosition = 1
self.name = ""
// add physics body, other properties or methods for the node
}
}
Than in your SKScenes you can add the node in the init method like so
nodeA1 = NodeA1(imageNamed: "ImageName", enemyType: .Normal)
nodeA1.position = ....
addChild(nodeA1)
this way ever scene where you add the node will use the subclass and therefore include all the properties, set up etc for that node. Another benefit with subclassing is that you could loop through all your nodes using
self.enumerateChildNodesWithName...
and than call custom methods on all nodes.
If you want to subclass your scenes than you would create your baseScene
class BaseScene: SKScene {
// set up all shared stuff in didMoveToView
// have your node function here
// touches began
// physics word and contact collision
// all other stuff that needs to be shared between all level scenes
}
Than your subsequent level scenes would look something like this
class Level1Scene: BaseScene {
override func didMoveToView(view: SKView) {
super.didMoveToView(view) // This lines imports all stuff in BaseScene didMoveToView
// do level 1 specific setUps.
// you can call any function or property from BaseScene, e.g the loadNode function.
}
You than load you level scenes as usual, e.g you transition to level 1 scene and it will automatically use/have access to all the superclass methods and sprites (BaseScene).
So you never call baseScene directly, its gets called automatically.
This applies for other methods in baseScene too, so say you have a Update method in BaseScene.
override func update(currentTime: CFTimeInterval) {.... }
This will work across all your level scenes which are subclasses of BaseScene.
But what happens if you need to add some specific stuff to the update method only relevant in 1 level scene and not all level scenes?
It would be the same process, you create a new update func in the LevelScene and call super.
override func update(currentTime: CFTimeInterval) {
super.update(currentTime) // this calls the baseScene Update method
/// specific stuff for that level only
}
Super simply means the super class of the currentScene, which is BaseScene if the scene is a subclass of it.
Is this helping?
This is additional answer information in terms of subclass of the baseScene. We can create node1thru node10 all in baseScene. Then in Leve1Scene which is subclass of the baseScene, all we have to do is in didMoveToView function state node1.position = CGPointMake(....) for each node that we need in Level1Scene where we would specify node's position.
If we do not need to load all of the 10 nodes in Level1Scene, for example, let's say we don't need to load to the scene node10 we can simply in didMoveToView function just state node10.removeFromParent() and this node will not be loaded to Level1Scene but rest of 9 nodes will.
Note that this example uses only 10 nodes, but you can go with any number of nodes in your baseScene.
This way of subclassing will save you a lot repeatable code in subclasses.

Is it possible for a subclass of SKShapeNode ot use one of its convenience initializers?

I have a class that inherits from SKShapeNode because I want to add additional properties. The problem I am having is that I want to be able to create a "circle" by using SKShapeNode's "convenience" initializer SKShapeNode.init(circleOfRadius:CGFloat), but since it is a convenience initializer the compiler complains with a message " Must call a designated initializer of the class SKShapeNode", because it only allows calling of a "designated initializer" of the parent class, but the only "designated" initializer of SKShapeNode is SKShapeNode.init(), which would not create the circle shape i want.
Below is my code with the compiler error.
So the question is, can I subclass SKShapeNode and still somehow have access to the "convenience" initializer to initialize it with a circle shape? Seems to me that it is not possible. And so are there any workarounds?
thanks
The designated initializer for SKShapeNode is just plain old init, so that's what we have to call in our custom init method. To create a circle shape then, we will have to create the path ourselves. Fortunately, CGPath has a convenience initializer we can use to create the circle. Here ya go:
import SpriteKit
class CustomSKShapeNode:SKShapeNode{
var groupName:String!
init(groupName:String,radius:CGFloat){
super.init()
self.groupName = groupName
let rect = CGRect(x:-radius,y:-radius,width:radius * 2,height:radius * 2)
self.path = CGPath(ellipseIn: rect, transform: nil)
self.fillColor = UIColor.yellow
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
var node = CustomSKShapeNode(groupName:"group1",radius:40)
node.position = CGPoint(x:40,y:40) // bottom left corner
addChild(node)

Swift SpriteKit "Extra Arguement "size" in call

When trying to transition between a SKScene to another SKScene. For example when its game over I use the code below and get the following error:-
Extra Arguement "size" in call
Below is a sample of my code used when this error occurs.
let scene = GameOverScreen(size: self.scene!.size) //<<---- Error throws here
scene.scaleMode = SKSceneScaleMode.AspectFill
view!.scene?.paused = true
self.scene!.view!.presentScene(scene, transition: transition)
This has never happened before in previous games of mine, and I cant seem to figure out why?
Using XCode 7, swift 2 and SpriteKit
Thanks in advance,
Rachel
I'm guessing this is happening because you have other initializers defined in GameOverScene that you haven't mentioned. Since you do that, you don't automatically inherit all of SKScene's initializers. You probably just have to add the following to GameOverScene:
override init(size: CGSize) {
// Set up your properties
super.init(size: size)
// Do whatever else you need to
}
If you didn't have any initializers defined in GameOverScene, you wouldn't be seeing this behavior. I'd suggest doing some further reading on Automatic Initializer Inheritance. The section I've linked to on that page describes the rules for when a class inherits the initializers of a superclass. They are not inherited by default.

super.init() recursive issue in Swift

I am working on a SpriteKit game using Swift and I need to subclass SKSpriteNode. In the init() (which is not an override) function I initialize the subclass's properties then call the super:
init(selector: Selector, delegateScene: SKScene, text: String, position: CGPoint) {
self.delegateScene = delegateScene
self.labelNode = SKLabelNode()
self.labelNode.position = CGPointZero
self.labelNode.text = text
self.selector = selector
super.init()
}
I set a breakpoint at the super.init() and a EXC_BAD_INSTRUCTION exception occurs right after it at the beginning of the subclass's init. Here's the error:
(file path).swift: 12: 7: fatal error: use of unimplemented initializer 'init(texture:color:size:)' for class 'Energies.Button'
If you inherit from SKSpriteNode, you should call the designated initializer. In this case that needs to be init(texture:color:size:). So if you were using a image or texture, you would need to call super.init like this:
super.init(texture: texture, color: nil, size: CGSize(width: 100, height: 100))
Note that you'll also need to set a size when initializing.
But in this case you can forget the above, because you're not looking for a subclass of SKSpriteNode, but a subclass of SKNode. If you change the superclass in the declaration from SKSpriteNode to SKNode it should work immediately.
You'll also need to add your add your label to the node after super.init(), else your label wouldn't be shown and your node would be empty.