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

标签 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 的子类中并且不知道 View 大小是多少,我不希望他们为此担心:

我正在这样做,但是在 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: ...)

还有这个

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

现在来看一些 GameManager 的实际示例,我相信您已经了解其中的一些。

示例 1

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

我个人为此使用协议(protocol)扩展。 创建一个新的 .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个场景都符合协议(protocol)

class StartScene: SKScene, SceneManager { ... }

并像这样调用加载方法,使用 3 个枚举案例中的 1 个作为场景标识符。

 loadScene(withIdentifier: .game)

示例 2

让我们使用单例方法为游戏数据创建一个游戏管理器类。

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)

这是一个庞大的答案,但我希望这对您有所帮助。

关于swift - 以编程方式创建 SKScene 子类,没有大小信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40515711/

相关文章:

ios - 在 Swift 中更改多种货币的分组分隔符、货币符号和货币符号的位置

arrays - 在通用和惯用的 Swift 中递归计算多维数组中的项目

ios - 适用于 iPhone XR 和 iPhone XS 的 Peek 和 Pop

objective-c - iOS7 Sprite Kit如何将交互式Sprite Nodes和交互式UIViews结合起来?

ios - SKSpriteNode 子类化 swift

python - psycopg2 跨 python 文件

ios - swift 从另一个类访问类类型变量

ios - 基于 Swift SpriteKit 分数更改背景

c - 用 0 个元素初始化多维 C 数组

c# - 为什么我可以在 C# 中像数组一样初始化一个列表?