以编程方式创建 SKScene 子类,没有大小信息? [英] create SKScene subclasses programmatically, without size info?

查看:18
本文介绍了以编程方式创建 SKScene 子类,没有大小信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to learn how to make a GameManager type class, and making individual classes for each of my GameScenes... probably the wrong thing to do, but for the sake of this question, please accept this as the way to do things.

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

My GameManager looks like this, having a reference to each of the scenes, that's static:

import SpriteKit

class GM {

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

如何在没有大小信息的情况下以编程方式创建 SKScene,因为它们位于 SKScene 的子类中并且不知道视图大小是多少,我不希望他们担心这个:

How do I create a SKScene programmatically, without size info, since they're in a subclass of SKScene and don't have any idea what the view size is, and I don't want them to need worry about this:

我正在这样做,但在 convenience override init()

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 类可以是什么的例子.

As I mentioned your question is a bit vague but lets do some examples of what a GameManager class can be.

在我开始之前让我们区分调用这个

Before I start lets differentiate between calling this

let scene = StartScene(size: ...)

还有这个

let scene = SKScene(fileNamed: "StartScene")

第一种方法,即大小,是当您在代码中创建场景并且您不使用 xCode 可视级别编辑器时.

The 1st method, with size, is when you create your scenes all in code and you are not using the xCode visual level editor.

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

The 2nd method is when you are using the Xcode level editor, so you would need to create a StartScene.sks file. Its that .sks file that it looks for in fileNamed.

现在对于一些游戏管理器示例,我们首先假设我们有 3 个 SKScene.

Now for some game manager example, lets first imagine we have 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 的最简单方法.

Lets say you want to transition from StartScene to GameScene, you would add this code in your StartScene at the correct spot e.g when the play button is pressed. Thats the simplest way to move from one SKScene to the next, directly from the SKScene itself.

 // 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 示例,我相信你已经知道其中的一些.

Now for some actual examples of GameManagers, Im sure you know about some of them already.

示例 1

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

Lets say we want a scene loading manager. You approach with static methods will not work because a new instance of SKScene needs be created when you transition to one, otherwise stuff like enemies etc will not reset. Your approach with static methods means you would use the same instance every time and that is no good.

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

I personally use a protocol extension for this. Create a new .swift file and call it SceneLoaderManager or something and add this code

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个场景中符合协议

Now in the 3 scenes conform to the protocol

class StartScene: SKScene, SceneManager { ... }

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

and call the load method like so, using 1 of the 3 enum cases as the scene identifier.

 loadScene(withIdentifier: .game)

示例 2

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

Lets make a game manager class for game data using the Singleton approach.

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

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

Now anywhere in your project you can say

 GameData.shared.updateHighscore(forScore: SOMESCORE)

您倾向于将 Singleton 用于只需要 1 个类实例的事情.Singleton 类的一个很好的使用示例是诸如 Game Center、InAppPurchases、GameData 等的帮助类

You tend to use Singleton for things where you only need 1 instance of the class. A good usage example for Singleton classes would be things such as helper classes for Game Center, InAppPurchases, GameData etc

示例 3

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

Generic helper for storing some values you might need across all scenes. This uses static method approach similar to what you were trying to do. I like to use this for things such as game settings, to have them in a nice centralised spot.

class GameHelper {

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

在你的场景中像这样使用它们

Use them like so in your scenes

 ... = GameHelper.playerSpeed

示例 4

管理 SKSpriteNodes 的类,例如敌人

A class to manage SKSpriteNodes e.g enemies

 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 个敌人并单独管理他们的健康等.例如

Than in your scene you can create enemies using this helper class and call the methods and properties on it. This way you can add 10 enemies easily and individually manage their health etc. e.g

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

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

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

Its a massive answer but I hope this helps.

这篇关于以编程方式创建 SKScene 子类,没有大小信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆