绕过子类中的Initialization:Swift和SpriteKit

Con*_*sed 6 initialization subclass sprite-kit skspritenode swift

我想创建一个比SpriteNode的touchesBe更高级别的SKShapeNode,所以当我想从这个sprite上的touchesBegun事件将SKShapeNode添加到屏幕时,形状已经存在,我只是从内部将它添加到屏幕touchesBegan覆盖.

TL; DR,在我的SKSpriteNode中,我正在尝试预先构建SKShapeNode,当触摸Sprite时,该SKShapeNode将用作动画环.

我想用变量/常量创建SKShapeNode,这样我就可以轻松编辑它的值了......

因此,在子类的根目录中,我有颜色,大小和线宽的变量,可以在创建SKShapeNode时使用...

class Balls: SKSpriteNode {

    var ringSize: CGFloat = 64
    var ringColor: SKColor = SKColor.white
    var ringWidth: CGFloat = 16

....
Run Code Online (Sandbox Code Playgroud)

再往下,但仍然是班级的根,我创建我的戒指:

 let ring = SKShapeNode(circleOfRadius: ringSize)
Run Code Online (Sandbox Code Playgroud)

我立即受到了深情的怀疑:

无法在属性初始化程序中使用实例成员'ringSize',属性初始化程序在'self'可用之前运行.

精细.好.我知道了.您希望在将值分配给self之前,应该在创建属性时对类进行函数调用.无论是在这里还是在那里,我认为我变得狡猾,并且可以通过将所有内容包装在函数中来解决这个问题:

class Balls: SKSpriteNode {

        var ringSize: CGFloat = 64
        var ringColor: SKColor = SKColor.white
        var ringWidth: CGFloat = 16

        var myRing = SKShapeNode()    

    func createRing() -> SKShapeNode{
            let ring = SKShapeNode(circleOfRadius: ringSize)
                ring.strokeColor = ringColor
                ring.lineWidth = ringWidth
            return ring
        }
Run Code Online (Sandbox Code Playgroud)

这不会产生任何错误,我的兴奋也会增加.

所以我添加另一行,创建实际的戒指:....

 myRing = createRing()
Run Code Online (Sandbox Code Playgroud)

又死了:

!预期的声明

我完全不知道这意味着什么,并开始随机尝试奇怪的事情.

其中一个正在进入我已经凌乱的便利初始化程序并添加myRing = createRing()到那里...... 这个工作!

这是如何以及为什么这样做,这是绕道初始化的最好/正确/正确的方法吗?

::编辑::更新::完整代码背景::

这是我的奇怪和误解的初始化者的完整课程.

import SpriteKit

class Circle: SKSpriteNode {

    var ringSize: CGFloat = 96
    var ringColor: SKColor = SKColor.white
    var ringWidth: CGFloat = 8

    var myRing = SKShapeNode()


    override init(texture: SKTexture?, color: UIColor, size: CGSize) {
        super.init(texture: texture, color: color, size: size)
    }

    convenience init() {
        self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))
        myRing = createRing()
        addChild(myRing)
        print("I'm on the screen")
        explodeGroup = create_explosionActionGroup()
        }

    convenience init(color: UIColor, size: CGSize, position: CGPoint) {
        self.init(color: color, size: size)
        self.position = position

        myRing = createRing()
        explodeGroup = create_explosionActionGroup()

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

    func createRing() -> SKShapeNode{
        let ring = SKShapeNode(circleOfRadius: ringSize)
        ring.strokeColor = ringColor
        ring.lineWidth = ringWidth
        return ring
    }
Run Code Online (Sandbox Code Playgroud)

rob*_*off 1

\n

我立即受到了亲切而神秘的欢迎:

\n\n
\n

无法在属性初始值设定项内使用实例成员“ringSize”,属性初始值设定项在“self”可用之前运行。

\n
\n
\n\n

因此,解决此问题的一种方法是通过ringSize另一种方式提供默认值,例如

\n\n
static let defaultRingSize: CGFloat = 64\n\nvar ringSize: CGFloat = Circle.defaultRingSize\nlet ring = SKShapeNode(circleOfRadius: Circle.defaultRingSize)\n
Run Code Online (Sandbox Code Playgroud)\n\n

...但我质疑为什么你有var ringSize这样的财产。难道你不应该didSet在它上面有一个观察者,这样如果你改变它的值,你就可以更新 的形状吗ring

\n\n
\n

又死了:

\n\n
\n

!预期声明

\n
\n
\n\n

在你的问题中,你不清楚你实际上是如何触发这个的,但我猜你尝试过这样的事情:

\n\n
class Circle: SKSpriteNode {\n\n    var ringSize: CGFloat = 96\n\n    var myRing = SKShapeNode()\n    myRing = createRing() // \xe2\x80\x9cExpected declaration\xe2\x80\x9d\xc2\xa0error on this line\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里的问题是,您已在类的主体中放置了一条语句,但主体中只允许使用声明。

\n\n
\n

其中之一是进入我已经很混乱的便利初始化程序并在其中添加 myRing = createRing() ......并且这有效!

\n\n

这是如何以及为什么起作用的

\n
\n\n

类自己的所有实例变量都必须在调用之前初始化super.init。由于myRing有默认值,编译器会在指定的初始值设定项中有效地插入myRing调用之前的初始化super.init,如下所示:

\n\n
override init(texture: SKTexture?, color: UIColor, size: CGSize) {\n    // Compiler-inserted initialization of myRing to the default\n    // value you specified:\n    myRing = SKShapeNode()\n\n    super.init(texture: texture, color: color, size: size)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

既然您声明了var myRing,您可以稍后将其更改为SKShapeNode您真正想要的定制。

\n\n
\n

这是循环初始化的最佳/正确/正确方法吗?

\n
\n\n

好吧,\xe2\x80\x9c绕排水管\xe2\x80\x9d意味着\xe2\x80\x9c失败\xe2\x80\x9d,所以我猜你在问这是否是\xe2\x80\x9c最好/正确的/proper way\xe2\x80\x9d\xc2\xa0to 在初始化时失败\xe2\x80\xa6 我认为这不是失败的最佳方式,因为你最终并没有真​​正失败。

\n\n

或者也许你的意思是 \xe2\x80\x9c 我讨厌 Swift 初始化的方式,所以我要抛出一些阴影 \xe2\x80\x9d,在这种情况下,你什么也看不到。

\n\n

但也许你真的意味着 \xe2\x80\x9cis 这是初始化我的实例\xe2\x80\x9d 的最佳/正确/正确的方法,在这种情况下,\xe2\x80\x9cbest\xe2\x80\x9d 和 \ xe2\x80\x9cright\xe2\x80\x9d 和 \xe2\x80\x9cproper\xe2\x80\x9d 非常主观。

\n\n

但我可以客观地指出,您正在创建一个SKShapeNode(作为 的默认值myRing)只是为了立即丢弃它并创建另一个SKShapeNode. 所以这是一种浪费。您还可以在两个便利初始化程序中调用它们createRing,但您可以将它们分解到指定的初始化程序中。

\n\n

但我什至不会那样做。SKShapeNode\ 的path属性是可设置的,因此您只需创建一个默认值SKShapeNode,然后path在调用super.init. 这也使得处理对属性ringSize和其他属性的更改变得更加容易,因为您可以通过知道如何匹配myRing属性的单个方法来汇集所有更改。

\n\n

我可能会这样写你的课程:

\n\n
import SpriteKit\n\nclass Circle: SKSpriteNode {\n\n    var ringSize: CGFloat = 96 {\n        // Use an observer to update myRing if this changes.\n        didSet { configureMyRing() }\n    }\n\n    var ringColor = SKColor.white {\n        didSet { configureMyRing() }\n    }\n\n    var ringWidth: CGFloat = 8 {\n        didSet { configureMyRing() }\n    }\n\n    // This can be a let instead of a var because I\'m never going to\n    // set it to a different object. Note that I\'m not bothering to\n    // initialize myRing\'s path or any other property here, because\n    // I can just call configureMyRing in my init (after the call to\n    // super.init).\n    let myRing = SKShapeNode()\n\n    override init(texture: SKTexture?, color: SKColor, size: CGSize) {\n        super.init(texture: texture, color: color, size: size)\n\n        // Call this now to set up myRing\'s path and other properties.\n        configureMyRing()\n    }\n\n    convenience init() {\n        self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))\n\n        // No need to do anything to myRing now, because my designated\n        // initializer set it up completely.\n\n        addChild(myRing)\n        print("I\'m on the screen")\n\n        // Commented out because you didn\'t provide this property\n        // or method in your question.\n//        explodeGroup = create_explosionActionGroup()\n        }\n\n    convenience init(color: SKColor, size: CGSize, position: CGPoint) {\n        self.init(color: color, size: size)\n        self.position = position\n\n        // Commented out because you didn\'t provide this property\n        // or method in your question.\n//        explodeGroup = create_explosionActionGroup()\n    }\n\n    required init?(coder aDecoder: NSCoder) {\n        fatalError("init(coder:) has not been implemented")\n    }\n\n    private func configureMyRing() {\n        myRing.path = CGPath(ellipseIn: CGRect(x: -ringSize / 2, y: -ringSize / 2, width: ringSize, height: ringSize), transform: nil)\n        myRing.strokeColor = ringColor\n        myRing.lineWidth = ringWidth\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n