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

查看:55
本文介绍了以编程方式创建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()

I'm doing this, but getting a EXC_BAD_Access at 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个SKScenes.

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的最简单方法.

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

现在三个场景中都符合协议

Now in the 3 scenes conform to the protocol

class StartScene: SKScene, SceneManager { ... }

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

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天全站免登陆