在调用 super.init() 之前实例化一个调用自身的 属性

Instantiating a property that calls on self before super.init() is called

我在我的第一个 Swift 游戏中构建了 ShipGenericGun 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 }()
}

如您所见,我创建了一个惰性 属性,当您调用 Shipgun 属性 时,它会自动填充第一个 [=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 属性 所做的操作,我将属性 gunship 设为可选。这意味着如果 Gun 不是 Ship 的直接 child 那么它的 ship 属性 将 return nil.

同样,如果Ship的children中没有Gun,那么它的gun 属性就会return nil.

性能

由于 shipgun 属性 是 lazy 它们将需要非常短的时间(第一次读取)来填充。您不会注意到延迟,但请记住它。

使用计算属性

您可以将它们定义为计算属性,而不是创建 shipgun 惰性属性。在这种情况下,如果您将枪支从一艘船移到另一艘船,您将获得一致的结果。

另一方面,在这种情况下,每次阅读 shipgun 时,您都需要花费非常(非常非常)少的时间。

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