sprite-kit - SpriteKit : create node only once for all scenes

标签 sprite-kit singleton

通常在 Sprite 套件游戏中,当出现新场景时,旧场景中的所有节点及其内容会自动删除。现在是什么,如果不应该删除像“HUD”这样的节点? Sprite 套件中有没有办法只创建一次节点并在所有场景中使用它,而无需在每个新场景中每次都删除和一次又一次地创建它?必须有一种技术使之成为可能。这是一个严重的 Sprite 套件设计问题,如果不可能的话。但我不这么认为。单例技术与音频播放器配合得很好,它只创建一次并在所有场景中使用。有一种方法可以只创建一次节点并在所有场景中使用它。感谢您的任何想法。

最佳答案

您无法创建在场景之间持续存在的节点。呈现新场景后,您需要将节点添加到这个新场景中。

出于这个原因,由于这个问题,我没有像 Apple 在文档中描述的那样使用 SKScene。不仅每次都必须将节点添加到新场景中很麻烦,而且对于像背景节点这样应该始终存在的节点来说,效率也极低。

所以我所做的是创建 2 个场景,一个用于游戏场景,一个用于菜单(GUI)。

对于菜单场景,我将 SKNodes 子类化为我的界面,然后在这些节点上使用 SKActions 在屏幕上呈现和关闭它们,因此感觉就像用户在场景之间转换。这为您提供了完全自定义,因为您可以呈现多个节点,您可以将节点永久保留在屏幕上等。

通过对 SKNode 进行子类化,您可以像处理场景一样组织代码。每个节点将代表您应用程序中的一个“场景”。然后你只需要编写一个方法来呈现和关闭这些节点。

我在下面添加了一些示例代码来展示使用 SKNodes 的一种实现。作为“场景”。示例代码有一个名为 SceneNode 的基类。我们将其子类化(就像您将 SKScene 子类化一样)。在这个实现中,我使用了 GameScene处理场景节点之间的所有转换*。我还跟踪当前场景节点,以便在场景大小发生变化(例如 OS X** 上的旋转或窗口大小调整)时更新其布局。您的游戏可能不需要这个,但它是动态布局节点的好方法。您想添加到背景中或保留的任何内容,只需将其添加到 GameScene .您想添加到场景中的任何内容,只需将 SceneNode 子类化即可。 ,过渡到它和你的好去。

*您可以轻松地直接从其他场景节点呈现场景节点,而不是通过 GameScene。但是我发现使用 GameScene 处理节点之间的转换效果非常好,尤其是当您有许多具有复杂转换的场景时。
**在 OS X 上有一个错误,调整窗口大小不会调用场景的 didChangeSize。您需要手动调用它。

class GameViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let scene = GameScene(size:self.view.bounds.size)
        scene.scaleMode = .ResizeFill
        (self.view as! SKView).presentScene(scene)
    }
}

class GameScene: SKScene {
    var currentSceneNode: SceneNode!

    override func didMoveToView(view: SKView) {
        self.backgroundColor = SKColor.whiteColor()
        transitionToScene(.Menu)
    }
    override func didChangeSize(oldSize: CGSize) {
        currentSceneNode?.layout()
    }
    func transitionToScene(sceneType: SceneTransition) {
        switch sceneType {
        case .Menu:
            currentSceneNode?.dismissWithAnimation(.Right)
            currentSceneNode = MenuSceneNode(gameScene: self)
            currentSceneNode.presentWithAnimation(.Right)

        case .Scores:
            currentSceneNode?.dismissWithAnimation(.Left)
            currentSceneNode = ScoresSceneNode(gameScene: self)
            currentSceneNode.presentWithAnimation(.Left)

        default: fatalError("Unknown scene transition.")
        }
    }
}

class SceneNode: SKNode {
    weak var gameScene: GameScene!

    init(gameScene: GameScene) {
        self.gameScene = gameScene
        super.init()
    }
    func layout() {}
    func presentWithAnimation(animation:Animation) {
        layout()
        let invert: CGFloat = animation == .Left ? 1 : -1
        self.position = CGPoint(x: invert*gameScene.size.width, y: 0)
        gameScene.addChild(self)
        let action = SKAction.moveTo(CGPoint(x: 0, y: 0), duration: 0.3)
        action.timingMode = SKActionTimingMode.EaseInEaseOut
        self.runAction(action)
    }
    func dismissWithAnimation(animation:Animation) {
        let invert: CGFloat = animation == .Left ? 1 : -1
        self.position = CGPoint(x: 0, y: 0)
        let action = SKAction.moveTo(CGPoint(x: invert*(-gameScene.size.width), y: 0), duration: 0.3)
        action.timingMode = SKActionTimingMode.EaseInEaseOut
        self.runAction(action, completion: {self.removeFromParent()})
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


class MenuSceneNode: SceneNode {
    var label: SKLabelNode
    var container: SKSpriteNode

    override func layout() {
        container.position = CGPoint(x: gameScene.size.width/2.0, y: gameScene.size.height/2.0)
    }
    override init(gameScene: GameScene) {
        label = SKLabelNode(text: "Menu Scene")
        label.horizontalAlignmentMode = .Center
        label.verticalAlignmentMode = .Center
        container = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 200, height: 200))
        container.addChild(label)
        super.init(gameScene: gameScene)
        self.addChild(container)
        self.userInteractionEnabled = true
    }
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        self.gameScene.transitionToScene(.Scores)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ScoresSceneNode: SceneNode {
    var label: SKLabelNode
    var container: SKSpriteNode

    override func layout() {
        container.position = CGPoint(x: gameScene.size.width/2.0, y: gameScene.size.height/2.0)
    }
    override init(gameScene: GameScene) {
        label = SKLabelNode(text: "Scores Scene")
        label.horizontalAlignmentMode = .Center
        label.verticalAlignmentMode = .Center
        container = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 200, height: 200))
        container.addChild(label)
        super.init(gameScene: gameScene)
        self.addChild(container)
        self.userInteractionEnabled = true
    }
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        self.gameScene.transitionToScene(.Menu)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

enum SceneTransition{
    case Menu, Scores
}
enum Animation {
    case Left, Right, None
}

enter image description here

关于sprite-kit - SpriteKit : create node only once for all scenes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29364037/

相关文章:

ios - 知道所有 SKActions 何时完成或没有任何运行

c# - Singleton:两全其美(想要实例化类)

swift - 单例中的函数不起作用

c# - 状态很重要的对象的最佳设计模式 - 单例或静态

sprite-kit - 我的物理联系人在 SpriteKit 中没有联系

ios - 根据速度更改快速的SKSprite节点动画

ios - 需要帮助为 SKScene 制定自定义协议(protocol)

python - 在 python 中将参数传递给单例

c# - “密封类中的 protected 成员”警告(单例类)

swift - 调用私有(private) var 来方便 init (swift)