在子类中绕过Initialization:Swift和SpriteKit

时间:2016-10-28 12:25:11

标签: swift sprite-kit initialization subclass skspritenode

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

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

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

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

class Balls: SKSpriteNode {

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

....

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

 let ring = SKShapeNode(circleOfRadius: ringSize)

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

  

无法在属性初始值设定项中使用实例成员'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
        }

这不会产生任何错误,我兴奋不已。

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

 myRing = createRing()

再次死亡:

  

!预期   声明

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

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

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

:: EDIT :: UPDATE ::完整代码上下文::

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

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
    }

3 个答案:

答案 0 :(得分:4)

你的createRing()方法在Ball类中,所以你需要先创建一个Ball实例。

简单方法 - 您可以将实例创建更改为

let ball = Balls()
let myRing = ball.createRing()

答案 1 :(得分:2)

我对你放置

的位置感到有些困惑
 myRing = createRing()

代码行,但我想知道此设置是否有助于解决您的问题

lazy var myRing: SKShapeNode = {
    let ring = SKShapeNode(circleOfRadius: ringSize)
    ring.strokeColor = ringColor
    ring.lineWidth = ringWidth
    return ring
}()

这种方式myRing将在访问时创建,它应该在实例化Balls类之后,这意味着ringSize,ringColor和ringWidth都将存在。

根据您的更新,我认为您最好的选择可能是让您的三个环变量“静态让”。这样它们将在初始化主类之前存在并具有设定值。您看到的错误是因为您创建了实例变量。这些仅在实例初始化时才存在。因此,如果您尝试将ring方法作为变量的声明来调用,或者如果您在之前在之前调用了self / super init,那么实例变量将无法访问。您添加的最新代码应该正常工作,因为您在尝试生成环之前创建了实例。我希望这是有道理和有帮助的。

答案 2 :(得分:1)

  

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

     
    

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

  

因此解决这个问题的另一种方法是以默认的ringSize提供另一种方式,例如

static let defaultRingSize: CGFloat = 64

var ringSize: CGFloat = Circle.defaultRingSize
let ring = SKShapeNode(circleOfRadius: Circle.defaultRingSize)

...但我质疑为什么你甚至拥有这样的var ringSize属性。你不应该有一个didSet观察者,所以如果你改变它的值,你可以更新ring的形状吗?

  

再次死亡:

     
    

<强>!预期声明

  

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

class Circle: SKSpriteNode {

    var ringSize: CGFloat = 96

    var myRing = SKShapeNode()
    myRing = createRing() // “Expected declaration” error on this line

这里的问题是你在班级的正文中放置了一个陈述,但只允许在正文中声明。

  

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

     

这是如何以及为何起作用

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

override init(texture: SKTexture?, color: UIColor, size: CGSize) {
    // Compiler-inserted initialization of myRing to the default
    // value you specified:
    myRing = SKShapeNode()

    super.init(texture: texture, color: color, size: size)
}

自您声明var myRing后,您可以稍后将其更改为您真正想要的自定义SKShapeNode

  

这是绕过初始化的最佳/正确/正确方法吗?

好吧,“绕过排水管”意味着“失败”,所以我猜你问这是否是“最好/正确/正确的方式”在初始化时失败...我想这不是最好的失败方法,因为你最终没有失败。

或许你的意思是“我讨厌Swift初始化的方式,所以我要抛出一些阴影”,在这种情况下,you ain't seen nothin' yet.

但也许你的意思是“这是初始化我的实例的最佳/正确/正确方式”,在这种情况下,“最好”,“正确”和“正确”都是非常主观的。

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

但我甚至不会这样做。 SKShapeNode的{​​{1}}属性是可设置的,因此您只需创建默认path,然后在调用SKShapeNode后更改其path。这也可以更轻松地处理super.init和其他属性的更改,因为您可以通过一个知道如何使ringSize与属性匹配的方法来汇集所有更改。

以下是我写作课程的方法:

myRing