绕过子类中的Initialization:Swift和SpriteKit

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了绕过子类中的Initialization:Swift和SpriteKit相关的知识,希望对你有一定的参考价值。

我想创建一个比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

....

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

 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() ......这个工作!

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

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

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

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
    }
答案

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

无法在属性初始化程序中使用实例成员'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,但您可以将它们分解为指定的初始化程序。

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

这是我可能写你的课程的方式:

import SpriteKit

class Circle: SKSpriteNode {

    var ringSize: CGFloat = 96 {
        // Use an observer to update myRing if this changes.
        didSet { configureMyRing() }
    }

    var ringColor = SKColor.white {
        didSet { configureMyRing() }
    }

    var ringWidth: CGFloat = 8 {
        didSet { configureMyRing() }
    }

    // This can be a let instead of a var because I'm never going to
    // set it to a different object. Note that I'm not bothering to
    // initialize myRing's path or any other property here, because
    // I can just call configureMyRing in my init (after the call to
    // super.init).
    let myRing = SKShapeNode()

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

        // Call this now to set up myRing's path and other properties.
        configureMyRing()
    }

    convenience init() {
        self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))

        // No need to do anything to myRing now, because my designated
        // initializer set it up completely.

        addChild(myRing)
        print("I'm on the screen")

        // Commented out because you didn't provide this property
        // or method in your question.
//        explodeGroup = create_explosionActionGroup()
        }

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

        // Commented out because you didn't provide this property
        // or method in your question.
//        explodeGroup = create_explosionActionGroup()
    }

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

    private func configureMyRing() {
        myRing.path = CGPath(ellipseIn: CGRect(x: -ringSize / 2, y: -ringSize / 2, width: ringSize, height: ringSize), transform: nil)
        myRing.strokeColor = ringColor
        myRing.lineWidth = ringWidth
    }
}
另一答案

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

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

let ball = Balls()
let myRing = ball.createRing()
另一答案

我对你把它放在哪里感到有点困惑

 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之前在init中执行它,那么实例变量将无法访问。您添加的最新代码应该正常工作,因为您在尝试生成环之前创建了实例。我希望这是有道理和有帮助的。

以上是关于绕过子类中的Initialization:Swift和SpriteKit的主要内容,如果未能解决你的问题,请参考以下文章

c++虚类、子类和自引用

“filename+CoreDataProperties.swift”和“filename.swift”NSmanagedObject 子类有啥用?

如何通过XAML,WPF中的数据绑定设置VisualState INITIALIZATION

SSTI漏洞学习(下)——Flask/Jinja模板引擎的相关绕过

SSTI漏洞学习(下)——Flask/Jinja模板引擎的相关绕过

节:类的加载过程之三:Initialization