在调用 super.init() 之前实例化一个调用自身的 属性
Instantiating a property that calls on self before super.init() is called
我在我的第一个 Swift 游戏中构建了 Ship
和 GenericGun
classes,我只是 运行 遇到了实例化问题Ships
属性之一。有问题的 属性,aGun
调用 self
作为它的值,但出现错误是因为虽然 属性 必须在调用 super.init()
之前设置,但属性 依赖于 self
,它只能在 之后 super.init()
被调用。我玩了一堆,发现在变量上使用可选会使错误消失,但我不确定为什么也不知道它是否会长期有效。这是我的枪 class:
class genericGun{
var theShip:Ship
var theGameScene:GameScene
init(gameScene:GameScene, shipInstance:Ship){
theShip = shipInstance
theGameScene = gameScene
}
func addLaser(){
let aLaser = Laser(laserPosition: theShip.position)
theShip.lasers.append(aLaser)
}
//If statement on user touch to call this
func shoot(){
//Pull out the laser from the ship
let availableLaser = theShip.lasers.removeLast()
let constY:CGFloat = theShip.position.y
availableLaser.position = CGPoint(x: theShip.position.x, y:constY)
//Set its speed
availableLaser.physicsBody?.velocity = CGVector(dx: 400.0,dy: 0)
//Add it to the scene
theGameScene.addChild(availableLaser)
theShip.canShoot = false
func printHey(){print("Hey!!!!!!!!!!!!!!!!!!!!!!")}
let sayHey = SKAction.runBlock{printHey()}
let reloadTime = SKAction.waitForDuration(1)
let loadGun = SKAction.sequence([reloadTime, sayHey])
theShip.runAction(SKAction.repeatActionForever(loadGun))
}
}
激光Class:
class Laser:SKSpriteNode{
init(laserPosition:CGPoint){
let laser = SKTexture(imageNamed: "Sprites/laser.jpg")
super.init(texture: laser, color: UIColor.clearColor(), size: laser.size())
//Laser physics
self.physicsBody = SKPhysicsBody(circleOfRadius: laser.size().width/2)
self.physicsBody?.dynamic = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Laser
self.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
self.physicsBody?.collisionBitMask = PhysicsCategory.None
self.physicsBody?.collisionBitMask = 0;
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.linearDamping = 0.0;
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
飞船Class:
class Ship:SKSpriteNode{
static var shipState = "norm"
//A dictionary with String keys and AnyType array values
static var shipTypes: [String: [Any]] = [
"norm":[SKTexture(imageNamed:"Sprites/fullShip.png"), SKTexture(imageNamed:"Sprites/laser.jpg"),7],
"rapid":[SKTexture(imageNamed:"Sprites/fullShip.png"),7],
"bazooka":[SKTexture(imageNamed:"Sprites/fullShip.png"),7]
]
var moveSpeed:CGFloat
var lives:Int
var lasers = [SKSpriteNode]()
var canShoot = false
var aGun: genericGun? = nil
var theGameScene:GameScene
static var shipImage = SKTexture(imageNamed:"Sprites/fullShip.png")//: Int = Int(shipTypes[shipState]![0])
init(gameScene:GameScene, startPosition startPos:CGPoint, controllerVector:CGVector){
self.lives = 3
self.moveSpeed = 200
theGameScene = gameScene
//Call super initilizer
super.init(texture: Ship.shipImage, color: UIColor.clearColor(), size: Ship.shipImage.size())
self.aGun = genericGun(gameScene: theGameScene, shipInstance: self)
self.setScale(0.2)
//Position is an property of SKSpriteNode so super must be called first
self.position = startPos
//Physics of the ship
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2)
self.physicsBody?.dynamic = true
self.physicsBody?.collisionBitMask = 0//PhysicsCategory.Ship
self.physicsBody?.contactTestBitMask = PhysicsCategory.Ship
self.physicsBody?.allowsRotation = false
self.physicsBody?.angularVelocity = CGFloat(0)
self.physicsBody?.affectedByGravity = false //TBD
self.physicsBody?.velocity.dx = controllerVector.dx * moveSpeed
self.physicsBody?.velocity.dy = controllerVector.dy * moveSpeed
}
func updateVelocity(v:CGVector){
if(v == CGVector(dx:0,dy:0)){
self.physicsBody?.velocity = CGVector(dx: 0,dy: 0)
}
self.physicsBody?.velocity.dx = v.dx * moveSpeed
self.physicsBody?.velocity.dy = v.dy * moveSpeed
}
func updateLaserPos(){
// laser.position = self.position
}
func updateShipProperties(shipVelocity v:CGVector,laserStartPos laserStart:CGPoint){
updateVelocity(v)
updateLaserPos()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这就是飞船实例化的地方:
class GameScene: SKScene, SKPhysicsContactDelegate {
var aShip = Ship(gameScene:GameScene(), startPosition: CGPoint(x:50,y:200),controllerVector: controlVector)
如果您以 Ship
需要创建 Gun
并且 Gun
在初始化之前需要 Ship
的方式设计游戏,那么您是遇到很多麻烦。
SpriteKit already solved this kind of problem with the scene
property available in SKNode
. It returns the scene
the current node belongs to.
你可以做类似的事情,让你的生活更轻松
飞船
class Ship: SKSpriteNode {
lazy var gun: Gun? = { return self.children.flatMap { [=10=] as? Gun }.first }()
}
如您所见,我创建了一个惰性 属性,当您调用 Ship
的 gun
属性 时,它会自动填充第一个 [=15] =] 在其 children.
中找到
枪
您可以对 Gun
object 执行相同的操作,因为您可以看到它有一个惰性变量 ship
,它的 parent 有条件地转换为Ship
.
class Gun: SKSpriteNode {
lazy var ship: Ship? = { return self.parent as? Ship }()
}
测试
let gun = Gun()
let ship = Ship()
ship.name = "Enteprise"
ship.addChild(gun)
print(gun.ship?.name) // Optional("Enterprise")
注意事项
根据 SpriteKit 对 scene
属性 所做的操作,我将属性 gun
和 ship
设为可选。这意味着如果 Gun
不是 Ship
的直接 child 那么它的 ship
属性 将 return nil
.
同样,如果Ship
的children中没有Gun
,那么它的gun
属性就会return nil
.
性能
由于 ship
和 gun
属性 是 lazy
它们将需要非常短的时间(第一次读取)来填充。您不会注意到延迟,但请记住它。
使用计算属性
您可以将它们定义为计算属性,而不是创建 ship
和 gun
惰性属性。在这种情况下,如果您将枪支从一艘船移到另一艘船,您将获得一致的结果。
另一方面,在这种情况下,每次阅读 ship
和 gun
时,您都需要花费非常(非常非常)少的时间。
class Ship: SKSpriteNode {
var gun: Gun? { return self.children.flatMap { [=13=] as? Gun }.first }
}
class Gun: SKSpriteNode {
var ship: Ship? { return self.parent as? Ship }
}
我在我的第一个 Swift 游戏中构建了 Ship
和 GenericGun
classes,我只是 运行 遇到了实例化问题Ships
属性之一。有问题的 属性,aGun
调用 self
作为它的值,但出现错误是因为虽然 属性 必须在调用 super.init()
之前设置,但属性 依赖于 self
,它只能在 之后 super.init()
被调用。我玩了一堆,发现在变量上使用可选会使错误消失,但我不确定为什么也不知道它是否会长期有效。这是我的枪 class:
class genericGun{
var theShip:Ship
var theGameScene:GameScene
init(gameScene:GameScene, shipInstance:Ship){
theShip = shipInstance
theGameScene = gameScene
}
func addLaser(){
let aLaser = Laser(laserPosition: theShip.position)
theShip.lasers.append(aLaser)
}
//If statement on user touch to call this
func shoot(){
//Pull out the laser from the ship
let availableLaser = theShip.lasers.removeLast()
let constY:CGFloat = theShip.position.y
availableLaser.position = CGPoint(x: theShip.position.x, y:constY)
//Set its speed
availableLaser.physicsBody?.velocity = CGVector(dx: 400.0,dy: 0)
//Add it to the scene
theGameScene.addChild(availableLaser)
theShip.canShoot = false
func printHey(){print("Hey!!!!!!!!!!!!!!!!!!!!!!")}
let sayHey = SKAction.runBlock{printHey()}
let reloadTime = SKAction.waitForDuration(1)
let loadGun = SKAction.sequence([reloadTime, sayHey])
theShip.runAction(SKAction.repeatActionForever(loadGun))
}
}
激光Class:
class Laser:SKSpriteNode{
init(laserPosition:CGPoint){
let laser = SKTexture(imageNamed: "Sprites/laser.jpg")
super.init(texture: laser, color: UIColor.clearColor(), size: laser.size())
//Laser physics
self.physicsBody = SKPhysicsBody(circleOfRadius: laser.size().width/2)
self.physicsBody?.dynamic = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Laser
self.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
self.physicsBody?.collisionBitMask = PhysicsCategory.None
self.physicsBody?.collisionBitMask = 0;
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.linearDamping = 0.0;
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
飞船Class:
class Ship:SKSpriteNode{
static var shipState = "norm"
//A dictionary with String keys and AnyType array values
static var shipTypes: [String: [Any]] = [
"norm":[SKTexture(imageNamed:"Sprites/fullShip.png"), SKTexture(imageNamed:"Sprites/laser.jpg"),7],
"rapid":[SKTexture(imageNamed:"Sprites/fullShip.png"),7],
"bazooka":[SKTexture(imageNamed:"Sprites/fullShip.png"),7]
]
var moveSpeed:CGFloat
var lives:Int
var lasers = [SKSpriteNode]()
var canShoot = false
var aGun: genericGun? = nil
var theGameScene:GameScene
static var shipImage = SKTexture(imageNamed:"Sprites/fullShip.png")//: Int = Int(shipTypes[shipState]![0])
init(gameScene:GameScene, startPosition startPos:CGPoint, controllerVector:CGVector){
self.lives = 3
self.moveSpeed = 200
theGameScene = gameScene
//Call super initilizer
super.init(texture: Ship.shipImage, color: UIColor.clearColor(), size: Ship.shipImage.size())
self.aGun = genericGun(gameScene: theGameScene, shipInstance: self)
self.setScale(0.2)
//Position is an property of SKSpriteNode so super must be called first
self.position = startPos
//Physics of the ship
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2)
self.physicsBody?.dynamic = true
self.physicsBody?.collisionBitMask = 0//PhysicsCategory.Ship
self.physicsBody?.contactTestBitMask = PhysicsCategory.Ship
self.physicsBody?.allowsRotation = false
self.physicsBody?.angularVelocity = CGFloat(0)
self.physicsBody?.affectedByGravity = false //TBD
self.physicsBody?.velocity.dx = controllerVector.dx * moveSpeed
self.physicsBody?.velocity.dy = controllerVector.dy * moveSpeed
}
func updateVelocity(v:CGVector){
if(v == CGVector(dx:0,dy:0)){
self.physicsBody?.velocity = CGVector(dx: 0,dy: 0)
}
self.physicsBody?.velocity.dx = v.dx * moveSpeed
self.physicsBody?.velocity.dy = v.dy * moveSpeed
}
func updateLaserPos(){
// laser.position = self.position
}
func updateShipProperties(shipVelocity v:CGVector,laserStartPos laserStart:CGPoint){
updateVelocity(v)
updateLaserPos()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这就是飞船实例化的地方:
class GameScene: SKScene, SKPhysicsContactDelegate {
var aShip = Ship(gameScene:GameScene(), startPosition: CGPoint(x:50,y:200),controllerVector: controlVector)
如果您以 Ship
需要创建 Gun
并且 Gun
在初始化之前需要 Ship
的方式设计游戏,那么您是遇到很多麻烦。
SpriteKit already solved this kind of problem with the
scene
property available inSKNode
. It returns thescene
the current node belongs to.
你可以做类似的事情,让你的生活更轻松
飞船
class Ship: SKSpriteNode {
lazy var gun: Gun? = { return self.children.flatMap { [=10=] as? Gun }.first }()
}
如您所见,我创建了一个惰性 属性,当您调用 Ship
的 gun
属性 时,它会自动填充第一个 [=15] =] 在其 children.
枪
您可以对 Gun
object 执行相同的操作,因为您可以看到它有一个惰性变量 ship
,它的 parent 有条件地转换为Ship
.
class Gun: SKSpriteNode {
lazy var ship: Ship? = { return self.parent as? Ship }()
}
测试
let gun = Gun()
let ship = Ship()
ship.name = "Enteprise"
ship.addChild(gun)
print(gun.ship?.name) // Optional("Enterprise")
注意事项
根据 SpriteKit 对 scene
属性 所做的操作,我将属性 gun
和 ship
设为可选。这意味着如果 Gun
不是 Ship
的直接 child 那么它的 ship
属性 将 return nil
.
同样,如果Ship
的children中没有Gun
,那么它的gun
属性就会return nil
.
性能
由于 ship
和 gun
属性 是 lazy
它们将需要非常短的时间(第一次读取)来填充。您不会注意到延迟,但请记住它。
使用计算属性
您可以将它们定义为计算属性,而不是创建 ship
和 gun
惰性属性。在这种情况下,如果您将枪支从一艘船移到另一艘船,您将获得一致的结果。
另一方面,在这种情况下,每次阅读 ship
和 gun
时,您都需要花费非常(非常非常)少的时间。
class Ship: SKSpriteNode {
var gun: Gun? { return self.children.flatMap { [=13=] as? Gun }.first }
}
class Gun: SKSpriteNode {
var ship: Ship? { return self.parent as? Ship }
}