以编程方式创建SKScene子类,没有大小信息?

时间:2016-11-09 20:44:26

标签: swift sprite-kit initialization subclass skscene

我试图学习如何制作GameManager类型类,并为我的每个GameScenes制作单独的类......可能是错误的做法,但为了这个问题,请接受这个做事的方式。

我的GameManager看起来像这样,引用了每个场景,这是静态的:

import SpriteKit

class GM {

    static let scene2 = SecondScene()
    static let scene3 = ThirdScene()
    static let home = SKScene(fileNamed: "GameScene")
}

如何以编程方式创建SKScene,没有大小信息,因为它们位于SKScene的子类中,并且不知道视图大小是什么,我不想要它们需要担心这个:

我这样做,但在convenience override init()获得EXC_BAD_Access

class SecondScene: SKScene {

    override init(size: CGSize){
        super.init(size: size)
    }

    convenience override init(){
        self.init()
        self.backgroundColor = SKColor.red
        self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    }
}

1 个答案:

答案 0 :(得分:7)

正如我所提到的,你的问题有点模糊,但让我们举一些关于GameManager类的例子。

在开始之前,让我们区分一下这个

let scene = StartScene(size: ...)

和这个

let scene = SKScene(fileNamed: "StartScene")

第一种方法,有大小,是在代码中创建所有场景而你没有使用xCode可视化级别编辑器。

第二种方法是在使用Xcode级别编辑器时,因此需要创建一个StartScene.sks文件。它是在fileNamed中查找的.sks文件。

现在举一些游戏经理的例子,让我们首先想象我们有3个SKScenes。

class StartScene: SKScene { 

      override func didMove(to view: SKView) { ... }
 }

class GameScene: SKScene { 

      override func didMove(to view: SKView) { ... }
 }

class GameOverScene: SKScene { 

      override func didMove(to view: SKView) { ... }
 }

假设您想要从StartScene过渡到GameScene,您可以在StartScene中将此代码添加到正确的位置,例如按下播放按钮时。这是从SKScene本身直接从SKScene转移到下一个SKScene的最简单方法。

 // Code only, no xCode level editor
 let gameScene = GameScene(size: CGSize(...))
 let transition = SKTransition...
 gameScene.scaleMode = .aspectFill
 view?.presentScene(gameScene, transition: transition)

 // With xCode level editor (returns an optional so needs if let
 // This will need the GameScene.sks file with the correct custom class set up in the inspector
 // Returns optional 
 if let gameScene = SKScene(fileNamed: "GameScene") {
      let transition = SKTransition...
      gameScene.scaleMode = .aspectFill
      view?.presentScene(gameScene, transition: transition)
 }

现在有一些GameManagers的实际例子,我相信你已经了解了其中的一些。

示例1

让我们说我们想要一个场景加载管理器。使用静态方法的方法不起作用,因为当你转换为一个时,需要创建一个新的SKScene实例,否则像敌人这样的东西不会重置。使用静态方法的方法意味着每次都使用相同的实例,这是不好的。

我个人使用协议扩展。 创建一个新的.swift文件并将其称为SceneLoaderManager或其他内容并添加此代码

enum SceneIdentifier: String {
   case start = "StartScene"
   case game = "GameScene"
   case gameOver = "GameOverScene"
}

private let sceneSize = CGSize(width: ..., height: ...)

protocol SceneManager { }
extension SceneManager where Self: SKScene {

     // No xCode level editor
     func loadScene(withIdentifier identifier: SceneIdentifier) {

           let scene: SKScene

           switch identifier {

           case .start:
              scene = StartScene(size: sceneSize)
           case .game:
              scene = GameScene(size: sceneSize)
           case .gameOver:
              scene = GameOverScene(size: sceneSize)
           }

           let transition = SKTransition...\
           scene.scaleMode = .aspectFill
           view?.presentScene(scene, transition: transition)
     }

      // With xCode level editor
     func loadScene(withIdentifier identifier: SceneIdentifier) {

           guard let scene = SKScene(fileNamed: identifier.rawValue) else { return }
           scene.scaleMode = .aspectFill
           let transition = SKTransition...
           view?.presentScene(scene, transition: transition)
     }
}

现在在3个场景中符合协议

class StartScene: SKScene, SceneManager { ... }

并使用3个枚举案例中的1个作为场景标识符来调用load方法。

 loadScene(withIdentifier: .game)

示例2

让我们使用Singleton方法为游戏数据制作游戏经理课程。

class GameData {

    static let shared = GameData()

    private init() { } // Private singleton init

    var highscore = 0

    func updateHighscore(forScore score: Int) {
       guard score > highscore else { return }
       highscore = score
       save()
    }

    func save() {
       // Some code to save the highscore property e.g UserDefaults or by archiving the whole GameData class
    }
}

现在你可以在项目的任何地方说

 GameData.shared.updateHighscore(forScore: SOMESCORE)

您倾向于将Singleton用于只需要1个类实例的事物。 Singleton类的一个很好的用例是诸如Game Center的助手类,InAppPurchases,GameData等等。

示例3

用于存储所有场景可能需要的某些值的通用助手。这使用类似于您尝试的静态方法方法。我喜欢将它用于诸如游戏设置之类的东西,以便将它们放在一个很好的集中位置。

class GameHelper {

     static let enemySpawnTime: TimeInterval = 5
     static let enemyBossHealth = 5
     static let playerSpeed = ...
}

在场景中使用它们

 ... = GameHelper.playerSpeed

示例4

管理SKSpriteNodes的类,例如敌人

 class Enemy: SKSpriteNode {

     var health = 5

     init(imageNamed: String) {
         let texture = SKTexture(imageNamed: imageNamed)
         super.init(texture: texture, color: SKColor.clear, size: texture.size())
     }

     func reduceHealth(by amount: Int) {
        health -= amount
     }
 }

在场景中,您可以使用此助手类创建敌人并调用其上的方法和属性。通过这种方式,您可以轻松添加10个敌人并单独管理他们的健康状况等,例如

 let enemy1 = Enemy(imageNamed: "Enemy1")
 let enemy2 = Enemy(imageNamed: "Enemy2")

 enemy1.reduceHealth(by: 3)
 enemy2.reduceHealth(by: 1)

这是一个很大的答案,但我希望这会有所帮助。