以编程方式创建SKScene子类,不需要大小信息



我正在努力学习如何制作一个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)
    }
}

正如我提到的你的问题有点模糊,但让我们做一些GameManager类可以是什么的例子。

在我开始之前,让我们区分一下调用

let scene = StartScene(size: ...)

和this

let scene = SKScene(fileNamed: "StartScene")

第一个方法,与大小,是当你创建你的场景全部在代码中,你不使用xCode可视化关卡编辑器。

第二种方法是当你使用Xcode关卡编辑器时,你需要创建一个StartScene。sks文件。它在filename .

中查找。sks文件。

现在对于一些游戏管理器的例子,让我们首先想象我们有3个skscene。

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

现在来看看一些gamemanager的实际例子,我相信你已经知道其中的一些了。

示例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 { ... }

并像这样调用load方法,使用3个枚举情况中的一个作为场景标识符。

 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。单例类的一个很好的使用例子是Game Center, inapppurces, GameData等的helper类

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

相关内容

最新更新