Search code examples
swiftinitializationskphysicsbodyextending

How to call a designated initializer of the superclass SKPhysicsBody?


Scenario: Since I need to know hitpoints and score of my game objects, I want extend SKPhysicsBody with some more stats. I have asked some related questions alread here and here.

Problem: I get this error "Must call a designated initializer of the superclass 'SKPhysicsBody'". And I do not know what I am doing wrong. I did almost same with SKSpriteNode and super.init worked. But there I had the problem that collision detection only returns physicsBody and not SKSpriteNode.

import Foundation
import SpriteKit

class Bubble: SKPhysicsBody {

    let bubbleSize = ["large": CGFloat(1.0), "medium": CGFloat(0.5), "small": CGFloat(0.25)]

    let playerCategory: UInt32 = 1 << 0
    let worldCategory: UInt32 = 1 << 1
    let bubbleCategory: UInt32 = 1 << 2
    let bulletCategory: UInt32 = 1 << 3

    var hitpoints: Int?
    var score: Int?

    init(bubble: (id: Int, name: String, scale: String, image: String, hitpoints: Int, score: Int)) {
        super.init(circleOfRadius: 50)
        self.hitpoints = bubble.hitpoints
        self.score = bubble.score

        self.dynamic = true
        self.friction = 0.5
        self.restitution = 1
        self.linearDamping = 0
        self.angularDamping = 0
        self.mass = 0.3
        self.categoryBitMask = bubbleCategory
        self.collisionBitMask = worldCategory
        self.contactTestBitMask = worldCategory | playerCategory
    }

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

Edit: When I use super.init() I do not get any errors. But how can I set its size like circleOfRadius or rectangleOfSize? Normally it should work with super.init(circleOfRadius: <CGFloat>)!


Solution

  • The initializer you are attempting to call is derived from conversion of the Objective C factory method:

    + (SKPhysicsBody *)bodyWithCircleOfRadius:(CGFloat)r;
    

    By command clicking on the class in your code, you can see that it is presented to Swift as:

    /*not inherited*/ init(circleOfRadius r: CGFloat)
    

    For contrast, the UIView initializer:

    - (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
    

    is seen in Swift as:

    init(frame: CGRect)
    

    without the /*not inherited*/ comment.

    The reason for the difference is that the factory method underlying the SKPhysicsBody initializer always returns a concrete instance of SKPhysicsBody, while initWithFrame is an instance method returning instancetype, allowing it to be used in subclass initialization.

    Your subclass therefore cannot rely on any of the standard initializers for SKPhysicsBody.