Make SKSpriteNode subclass using Swift
You have to call the designated initializer for SKSpriteNode
. I'm actually surprised you didn't get another error about not full implementing SKSpriteNode
, are you using an older Xcode6 beta maybe?
Since you have to use the designated initializer, you need to call super.init(texture: '', color: '', size: '')
.
It would be something like this:
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "ball")
super.init(texture: texture, color: nil, size: texture.size())
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Note: I also added the init
for NSCoder
which Xcode will require.
How to create subclass of SKSpriteNode in Swift 3
Turns out my code is fine, the bugs were due to something else. If anyone sees this - it is now that easy to add new attributes to a subclass - you don't have to faff around with super inits etc.
print(Ci.pitch)
will now give "middle C".
Am unable to subclass SKSpriteNode using an image name
Just use the full initializer on the super, like so...
class MyBall : SKSpriteNode{
init(iNamed: String) {
let texture = SKTexture(imageNamed: iNamed)
super.init(texture: texture, color: .clear, size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Creating an SKSpriteNode class with Swift 4
you are declaring your init to have two parameters (texture: SKTexture, size: CGSize)
but you are not passing the parameters in your initialization call
let player = Vaisseau()
you either need to change the initialization to...
let texture = SKTexture(imageNamed: "player")
let player = Vaisseau(texture: texture, size: texture.size())
and change the init to
init(texture: SKTexture, size: CGSize) {
super.init(texture: texture, color: UIColor.clear, size: size)
}
OR change the init in the class to...
init() {
let texture = SKTexture(imageNamed: "player")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
}
and leave your initialization call as...
let player = Vaisseau()
player.position = CGPoint(x: 500, y: 500)
addChild(player)
EDIT added the above 2 lines to show you that those need to be in the scene
but other items such as alpha, zPosition, actions, zRotation etc. can be inside of the class
What you need to ask yourself to figure out which one to use is "will the texture for the player ever be different?" if so you may want to consider the first option where you pass in the texture.
Changing texture for subclass of SKSpriteNode?
Your code works well. In fact this SO answer is correct.
About SKSpriteNode
subclass, the update
method is a new custom function added by you, because the instance method update(_:) is valid only for SKScene
class, this just to say that this function is not performed if it is not called explicitly.
To make a test about your class you can change your code as below( I've changed only textureForLevel
method only to make this example):
class CharacterNode: SKSpriteNode {
let spriteSize = CGSize(width: 70, height: 100)
var level = 0
init(level: Int) {
self.level = level
super.init(texture: nil, color: UIColor.clear, size: spriteSize)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(level: Int) {
self.level = level
self.texture = textureForLevel(level: level)
}
func textureForLevel(level: Int) -> SKTexture {
if level == 3 {
return SKTexture(imageNamed: "TestTexture3.jpg")
}
return SKTexture(imageNamed: "TestTexture.jpg")
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
let characterNode = CharacterNode(level:2)
characterNode.update(level:2)
addChild(characterNode)
characterNode.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
characterNode.run(SKAction.wait(forDuration: 2.0), completion: {
characterNode.update(level:3)
})
}
}
As you can see when you launch GameScene
a characterNode sprite will be shown in the center of the screen. After 2 seconds, the texture will change.
Why am I unable to subclass the SKSpriteNode
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)
}
}
How to inherit the SKspritenode and create by self.childNode
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,
Creating Initializers for SKSpriteNode Subclass
The compiler is right: your override would be fine except that you haven't initialized your properties. If you need to have parameters to do that, then you'll need to create a new designated initializer instead: your init(texture: SKTexture, color: UIColor, size: CGSize, rangeOfMovement: CGFloat, originalPosition: CGFloat, platformNumber: Int)
will do; simply remove convenience
from it, as it is a new designated initializer, not a convenience initializer. (Convenience initializers, as the compiler points out, must delegate across to self.something
, not up as you wish to do.)
As for init?(coder:)
, you're fine to use the fatalError
version as long as you are never going to encode or decode instances of your subclass. If you do need to make use of NSCoding
, then you'll need to provide a working implementation of init?(coder:)
and override encodeWithCoder(_:)
, too.
How to add a sprite from another class on the gameScene
You don't generally subclass the scene for instances such as this. More likely you are meaning to make boss a subclass of SKSpriteNode
and adding it to your scene. While there is probably dozens of ways that you could subclass this, this is just 1 way.
Also worth noting that it is not generally acceptable practice to make your variable names capitalized, classes yes, variables no.
class Boss: SKSpriteNode {
init() {
let texture = SKTetxure(imageNamed: "boss1")
super.init(texture: texture , color: .clear, size: texture.size())
zPosition = 2
//any other setup such as zRotation coloring, additional layers, health etc.
}
}
...meanwhile back in GameScene
class GameScene: SKScene {
let boss1: Boss!
override func didMove(to view: SKView) {
boss1 = Boss()
boss1.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
self.gameScene.addChild(boss1)
}
}
in GameScene you create an instance of the new class and position it and add it in the GameScene.
edit
the init function has a short cut in swift.
Boss()
is the same as
Boss.init()
you can also add custom parameters to your init in order to further clarify or add more features to your class. for example...
class Boss: SKSpriteNode {
private var health: Int = 0
init(type: Int, health: Int, scale: CGFloat) {
let texture: SKTetxure!
if type == 1 {
texture = SKTetxure(imageNamed: "boss1")
}
else {
texture = SKTetxure(imageNamed: "boss2")
}
super.init(texture: texture , color: .clear, size: texture.size())
self.health = health
self.setScale(scale)
zPosition = 2
//any other setup such as zRotation coloring, additional layers, health etc.
}
}
Using properties of SKSpriteNode for subclass
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 Topics
Swift: Protocol VS. Struct VS. Class
How to Add a Link for a Rate Button with Swift
Timer Label Not Updated After Switching Views (Swift)
Swift Continuous Rotation Animation Not So Continuous
Drawing an Infinite Grid in iOS
Wake Up Application in Background Using Audiosession Like Alarmy iOS App
How Would I Store a Video into Firebase Storage from Swift
It Is Possible to Know If a String Is Encoded in Base64
In Swift, Can a Function Be a Type
How to Manage and Free Memory Through Viewcontrollers
How to Convert Data Dictionary into an Array Swift
Include Pods in Main Target and Not in Watchkit Extension
How to Create a Ntlm Authentication Header to Use with Alamofire
Using Completionselector and Completiontarget with Uiimagewritetosavedphotosalbum