What is the problem with this init(coder:) function? - swift

I don't get it guys.
I'm trying to create a 2D game and i'm stuck with a very basic issue.
Here is my code:
import SpriteKit
class GameScene: SKScene {
let box: SKSpriteNode
let hero: SKSpriteNode
override init(size: CGSize){
hero = SKSpriteNode(color: UIColor.red, size: CGSize(width: size.width / 5, height: size.height / 5))
box = SKSpriteNode(color: UIColor.blue, size: CGSize(width: size.width, height: size.height))
super.init(size: size)
self.addChild(box)
self.addChild(hero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didMove(to view: SKView) {
// Setting up the scene
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
Xcode indicates me the following error :"Property 'self.box' not initialized at super.init call" at the init (coder:) function.
Then i tried to replace it with:
required init?(coder a Decoder: NSCoder){
fatalError("init(coder:) has not been implemented)
}
But guess what... when i launch the game it indicates me that i have a fatal error.
When i delete all initializers it tells me that i need initialisers.
Do you have any idea of how to do it?

Given that box and hero are not optional, they must be initialized in all initializers.
You have done it for your custom initializer but you also need to do it for init with coder since that initializer can be invoked separate from your custom initializer.

Related

Am unable to subclass SKSpriteNode using an image name

I am having trouble subclassing SKSpriteNode when I need to use an .png image and all the help I can find on Google only mentions SKTexture.
In my regular class this code works:
let circle = SKSpriteNode(imageNamed: "slot")
circle.setScale(1.0)
circle.anchorPoint = CGPoint(x: 0, y: 0.5)
circle.position = CGPoint(x: 1000, y: 500)
self.addChild(circle)
I would like to move it to a subclass, but no matter what combination I try am always getting errors such as:
Cannot convert value of type 'SKTexture' to expected argument type
'String'
or
Must call a designated initializer of the superclass 'SKSpriteNode'
I am able to subclass SKSpriteNode if I want to use SKTexture. Yet at this juncture the init I want to subclass is SKSpriteNode(imageNamed: String)
Here is what I am trying to do, but of course I get errors
class MyBall : SKSpriteNode{
init(iNamed: String){
super.init(imageNamed: iNamed)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
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")
}
}

Custom Class Not Drawing at Center - Swift

I have a UIView with a custom class of TimerView. I am trying to draw a dot in the exact center of the UIView but it is appearing at the bottom slightly off center:
Custom class is here:
import UIKit
class TimerView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
drawDot()
}
func drawDot() {
let midViewX = self.frame.midX
let midViewY = self.frame.midY
let dotPath = UIBezierPath(ovalIn: CGRect(x: midViewX, y: midViewY, width: 5, height: 5))
let dotLayer = CAShapeLayer()
dotLayer.path = dotPath.cgPath
dotLayer.strokeColor = UIColor.blue.cgColor
self.layer.addSublayer(dotLayer)
}
}
Here is the re-implementation of TimerView class. For UIView, init is not the best place to get the frame/bounds values because it might change once the autolayout applies the constraints in runtime. layoutSubviews is the best place to get the correct frame/bounds values for parent/child views and to setup the child space properties. Secondly you should be using parent view bounds to setup the child's frame.
class TimerView: UIView {
private var dotLayer: CAShapeLayer?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
dotLayer = CAShapeLayer()
dotLayer?.strokeColor = UIColor.blue.cgColor
self.layer.addSublayer(dotLayer!)
}
override func layoutSubviews() {
super.layoutSubviews()
drawDot()
}
func drawDot() {
let midViewX = self.bounds.midX
let midViewY = self.bounds.midY
let dotPath = UIBezierPath(ovalIn: CGRect(x: midViewX, y: midViewY, width: 5, height: 5))
dotLayer?.path = dotPath.cgPath
}
}

Cannot invoke 'SKSpriteNode.init' with an argument list of type '(texture: SKTexture, color: UIColor, size: CGSize, () -> ())'

Can anyone help me solve this issue? The error shown is being generated even though I am using the designated initializer.
class OtherOrb: SKSpriteNode {
override init() {
let texture = SKTexture(imageNamed: "Orb")
super.init(texture: texture, color: UIColor.clear, size: texture.size()){
self.position = CGPoint(x: 0.0, y: 500.0)
self.physicsBody = SKPhysicsBody(circleOfRadius: 20)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You are getting this error because of the closure you have trailing your initialiser.
In Swift, the compiler interprets a closure directly after a method call as the final argument to said method call, and hence the error you are getting effectively says that no initialiser that includes that closure argument exists.
Furthermore, your code looks very off, and I'm not convinced it's valid Swift. What are you trying to accomplish?
Based on my suspicions, I believe your code should resemble the following:
class OtherOrb: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "Orb")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
self.position = CGPoint(x: 0.0, y: 500.0)
self.physicsBody = SKPhysicsBody(circleOfRadius: 20)
}
#available(*, unavailable)
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
}
#available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
That is to say that you want an initialiser with no args to be your designated initialiser.

Sprite Kit Creating Separate Class

Attempting to design a game using Sprite-Kit I found that it would be easier to create a seperate class for one of my game objects being a laser from a ship. Within that class I would have some functions maybe such as updating, shooting etc. But whenever I create another class none of the variables I make are "declared". This is my code
import UIKit
import SpriteKit
class laser: SKSpriteNode {
let laser : SKSpriteNode = SKSpriteNode(imageNamed:"playerShip")
laser.position = CGPoint(x: 100, y: 200)//This is where it says no declaration
}
This is because you need to initiate the class.
In your example you would need to do the following which would allow you instantiate a laser like this laser()
class laser: SKSpriteNode {
let laser: SKSpriteNode = SKSpriteNode(imageNamed: "playerShip")
init() {
super.init(texture: nil, color: .clear, size: laser.size)
laser.position = CGPoint(x: 100, y: 200)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But you probably really wanted this, which allows you to instantiate a laser like this let aLaser = laser("playerShip"). Then you can change the position like this alaser.position = CGPoint(x: 100, y: 200).
This method allows you to change the sprite and position easily for different lasers. Unless your game only has one laser.
class laser: SKSpriteNode {
init(_ imageName: String) {
let texture: SKTexture = SKTexture(imageNamed: imageName)
super.init(texture: texture, color: .clear, size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Error: "Property 'self.gameArea' not initialised at super.init call"

I get the error written in the title. I'm new to this all. Any help appreciated.
This is the GameScene.swift file:
class GameScene: SKScene {
let player = SKSpriteNode(imageNamed: "shuttle")
let bulletSound = SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false)
let gameArea: CGRect
override init(size: CGSize) {
let maxRatio: CGFloat = 16.0/9.0
let playableWidth = size.height / maxRatio
let margin = (size.width - playableWidth) / 2
gameArea = CGRect(x: margin - size.width / 2, y: size.height / 2, width: playableWidth, height: size.height)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
I would recommend you to read about initialization process. Swift preforms two step initialization. First phase is intialization of all stored properties. Also Swift compiler perform few safety checks to make sure two phase initialization is performed correctly. This is from the docs:
Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a
superclass initializer.
So you can either initialize your property inside of an initializer:
required init?(coder aDecoder: NSCoder) {
self.gameArea = CGRect()
super.init(coder: aDecoder)
}
or at property declaration (and optionally declaring it as var):
var gameArea = CGRect()
If you don't make it var, after initialization (at declaration) you won't be able to change it later on. Same goes when you initialize a property inside of an initializer. If it is a let constant, further modifications are not possible.