我想创建一个比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
,但您可以将它们分解为指定的初始化程序。
但我甚至不会这样做。 SKShapeNode
的path
属性是可设置的,因此您可以创建一个默认的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中执行它,那么实例变量将无法访问。您添加的最新代码应该正常工作,因为您在尝试生成环之前创建了实例。我希望这是有道理和有帮助的。