在调用super.init()之前实例化调用self的属性

时间:2016-07-28 17:38:56

标签: swift oop sprite-kit instantiation self

我在第一个Swift游戏中构建了ShipGenericGun类,我遇到了一个问题,实例化了一个Ships属性。相关媒体资源aGun会调用self作为其值,但会显示错误,因为必须在调用super.init()之前设置该属性,该属性依赖于self只能在 super.init()被调用后才能访问。我玩了很多,发现在变量上使用可选项会使错误消失,但我不确定为什么,也不知道它是否能长期运行。这是我的枪类:

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

1 个答案:

答案 0 :(得分:1)

如果您设计游戏的方式是Ship需要创建GunGun在初始化之前需要Ship,那么您将进入很麻烦。

  

SpriteKit已经使用scene中提供的SKNode属性解决了这类问题。它返回当前节点所属的scene

你可以做类似的事情,让你的生活更轻松

class Ship: SKSpriteNode {
    lazy var gun: Gun? = { return self.children.flatMap { $0 as? Gun }.first }()
}

正如您所看到的,我创建了一个惰性属性,当您调用gun的{​​{1}}属性时,它会自动填充其子级中找到的第一个Ship

您可以对Gun对象执行相同的操作,因为您可以看到它有一个惰性变量Gun,其父级有条件地投放到ship

Ship

测试

class Gun: SKSpriteNode {
    lazy var ship: Ship? = { return self.parent as? Ship }()
}

考虑

按照SpriteKit对let gun = Gun() let ship = Ship() ship.name = "Enteprise" ship.addChild(gun) print(gun.ship?.name) // Optional("Enterprise") 属性所做的操作,我创建了属性scenegun选项。这意味着,如果ship不是Gun的直接子项,则其Ship属性将返回ship

同样,如果nil在其子级中没有Ship,则其Gun属性将返回gun

效果

由于nilship属性为gun,因此需要花费很少的时间(第一次读取它们)。你不会注意到延迟,但请记住它。

使用计算属性

您可以将它们定义为计算属性,而不是使lazyship延迟属性。在这种情况下,如果您将枪从船舶移动到另一艘船,您将获得一致的结果。

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

gun