我在第一个Swift游戏中构建了Ship
和GenericGun
类,我遇到了一个问题,实例化了一个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)
答案 0 :(得分:1)
如果您设计游戏的方式是Ship
需要创建Gun
而Gun
在初始化之前需要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")
属性所做的操作,我创建了属性scene
和gun
选项。这意味着,如果ship
不是Gun
的直接子项,则其Ship
属性将返回ship
。
同样,如果nil
在其子级中没有Ship
,则其Gun
属性将返回gun
。
由于nil
和ship
属性为gun
,因此需要花费很少的时间(第一次读取它们)。你不会注意到延迟,但请记住它。
您可以将它们定义为计算属性,而不是使lazy
和ship
延迟属性。在这种情况下,如果您将枪从船舶移动到另一艘船,您将获得一致的结果。
另一方面,在这种情况下,每次阅读gun
和ship
时,您都需要非常(非常非常)非常少的时间。
gun