繁体   English   中英

Sprite-kit游戏生命周期,属性和全局变量?

[英]Sprite-kit game lifecycle, properties and global variable?

在我的sprite-Kit游戏中,我的游戏经历了各种场景,我想要某些变量可用。 请参见下图:

在此输入图像描述

因此标题屏幕加载主游戏屏幕。 当'wave'结束(所有客观完成或所有生命丢失)时,最终场景会显示一些统计数据。 然后,如果玩家还有生命,则再次加载主游戏,否则再次显示标题画面。

我希望玩家的得分和生命数量可以在主要游戏场景和最终场景中使用; 所有场景都应保持高分。

我正在使用全局变量,而我的大多数变量都是在类下定义的属性。

第一次我不得不这样做(在我加入高分或多次生命之前为我的得分属性)我在每个场景上使用得分属性并在场景之间传递它,当我为下一个场景做现在的场景时,但这看起来很笨拙,特别是当我不得不添加更多变量时。

是否有一个最佳实践,即应该使用/不使用全局变量,如果没有,是否有正确的方法来处理属性,可能由初始化它们的位置控制,例如在initdidMoveToView相比,它确定它们是否重新-initiialised再次显示场景?

编辑:

当我从End Wave Scene回到主游戏画面时,我做了什么“错误”,导致主画面再次重新初始化? 有没有办法保存它(包含它的所有属性)所以它没有完全重新初始化但是它的didMoveToView再次被调用?

我知道许多人使用广义规则,如“不要使用全局”或“单身人士是邪恶的”。 这些智慧的话语有时可能适用,但另一种看待它们的方式是作为让你更有效地完成游戏的工具。

有时候全局/单身人士很有用。 游戏和嵌入式系统遵循与传统应用程序不同的规则。 这些通常是资源受限的“应用程序”,与性能要求相结合。 全局比你想象的更常见。 单身人士也常用。 我曾经在嵌入式系统和游戏上工作过,可以说在我参与过的每个项目中,他们都使用全局,单例或两者兼而有之。

在实践中,我使用单身而不是全局。 主要是因为我不必担心在哪里推送全局及其所属的头文件。注意,原因的基础将与你所有的Swifties不同。 对我来说,这些东西主要是在C ++中用一些Obj-C完成的,并且在Swift的这里和那里都很少。

管理游戏生命周期的需要与SpriteKit 我在SO上看到的许多SpriteKit帖子都有开发人员将游戏状态嵌入到场景或VC中。 他们通常面临着如何将状态移动到下一个场景的两难困境。 虽然这种方法可能对我称之为“本地状态”(即当前屏幕,阶段等的状态)有益,但它对于全局游戏状态并不好。 例如,如果您从主屏幕转换到选项屏幕然后转回主屏幕然后再转换到游戏中,您如何跟踪这些选项的更改(例如,更改游戏难度)? 是的,你当然可以传递字典,结构或你身边的东西。 但是在某些时候,你会发现拥有一个共同的,便利的倾倒场更方便。 这个“倾销场”将是全球/单身人士。

在你开始对我大喊大叫之前,所有关于全局和单身的疯狂谈话都有一个问题。 我不是说创造很多全局/单身。 相反,当你使用全局/单身时,可以控制它。

我不是在谈论它们。 我说话就像一个(虽然我通常使用一些)。 输入我们的朋友, GameManager

让我们走一个太空射击游戏的简单场景。 假设我有多个屏幕,例如标题屏幕,主屏幕,游戏屏幕和结束波屏幕。 有些信息可以传递给不同的屏幕。 例如高分甚至当前分数。 这些可能是您想要在各种屏幕/场景上显示的值。 那你在哪里存储分数? 我如何通过它们之间的分数? 例如,如果所有屏幕上都显示最后一个高分,那么我在哪里保留该值?

class GameManager : NSObject {
    // Overall game state, GameState is probably some enum
    var state:GameState
    var lastState:GameState

    // Sometimes tracking scenes is useful
    var currentScene:SKScene?
    var nextScene:SKScene? // Sometimes helpful if you need to construct the next scene and it takes a non-trivial amount of time

    // These are the globals we need to share to different "parts" of the game
    var score: UInt
    var highscore: UInt
    var lives: Uint
    var stage: Uint

    var highscores:[Uint]   // A list of all the high score for the user. You would probably want HS based on other users too

    // Egads a singleton!
    static let sharedInstance = GameManager()
}

如果这是一个全局的,它将在任何范围之外对实例进行一些全局定义。 喜欢:

var theGameManager : GameManager

所以,如果你想改变分数,那你就做了

theGameManager.score += 100

如果是单身人士更新,分数看起来像是:

GameManager.sharedInstance.score += 100

好吧,单身人士的语法要长一点,但也许比全球的语言更加神秘,不知道这是什么。

但现在,你可以在这里获得更多的力量。 让我们假设当你添加一个分数时,100000的每个倍数都会给你额外的生命。 我现在可以轻松利用设定者并使用该设定器来奖励额外的生命。

例如:

GameManager.sharedInstance.score += 100 // the magic of the 100000 for extra life happens automagically in the setter

与可能是这样的东西:

myScore += 100

// Do logic to find out if we get an extra life. 
// Is this done in a method and if so where does this method live?
// Or is it straight up code which will be duplicated?

像这样有助于管理全局游戏状态的代码全部包含在GameManager 换句话说, GameManager不仅仅维护值,还提供了一种围绕这些值封装功能的方法。

嗯,看来当前的currentScene也在这里? 那为什么会这样? GameManager也是管理场景变化的绝佳方式。 更有可能通过游戏状态完成。 也许是这样的:

GameManager.sharedInstance.gameState = GameOverState

在幕后, gameState setter可以做出交换场景的魔力。

这些只是这种方法的实际性质的几个例子。

关于加载/存储的讨论,如果需要,也可以通过GameManager完成。 虽然对我自己来说,我通常会根据GameManager一些数据独立对待它。 这带来了另一个区别。 我通常会将想法封装在GameManager 例如,我可能有一个Player ,这将使GameManager看起来像这样:

class GameManager : NSObject {
    // Overall game state, GameState is probably some enum
    var state:GameState
    var lastState:GameState

    // Sometimes tracking scenes is useful
    var currentScene:SKScene?
    var nextScene:SKScene? // Sometimes helpful if you need to construct the next scene and it takes a non-trivial amount of time

    // These are the globals we need to share to different "parts" of the game
    // Note player state is done through this object. This way I can pass around only player when needed.
    var player: Player

    var stage: Uint

    var highscores:[Uint]   // A list of all the high score for the user. You would probably want HS based on other users too

    // Egads a singleton!
    static let sharedInstance = GameManager()
}

等你说; “但我不喜欢这种全球/单身爵士乐。为什么我不把这个对象传递给需要它的每个人。” 答案是“你可以”。 但在实践中,你会发现这变得乏味。 您也可能发现您发现需要GameManager ,但调用者从未将GameManager传递给它。 这意味着现在您正在重新设计一些方法来传递此对象。

Soooo是全球/单身人士的邪恶? 你可以做自己的判断。 每个人都会有选择的解决方案。 如果它适合您,请使用它。 对我来说,这不是一件容易的事。 使用它在您的实现中访问/管理全局游戏状态时的一致易用性的好处使其成为显而易见的选择。

所以我在这里添加了一些更多的信息,用于解答一些已经很长的答案,以便澄清一些事情。

我对单身或全球的选择将取决于使用,平台和语言。 它也取决于游戏代码库。 它并不意味着只是任何数据的倾销场。 如果使用,应仔细考虑内容,并在可能的情况下,使用其他容器作为内容(即,将它们包装在课堂中)。 因此,虽然我的第一个例子让玩家信息悬空(更容易传达这个想法),但实际上我会让PlayerState包含玩家数据。 请注意,这可能不是整个播放器中的播放器,但也许是一些需要超出播放器游戏内生活的共享信息。

class PlayerState {
    var score:Uint
    var highscore:Uint
    var lives:Uint
}

// Simplified GameManager
class GameManager {
    var state:GameState
    var lastState:GameState

    var player:PlayerState

    // Egads a singleton!
    static let sharedInstance = GameManager()
}

另外,我最终会传递PlayerState实例,而不是期望代码始终通过global / singleton获取它。

所以例如我会这样做:

func doSomething(playerState:PlayerState) {
    var score = playerState.score   

    // Blah blah blah
}

VERUS

func doSomething() {
    // Whee I have singleton
    var score = GameManager.player.score

    // Blah blah blah
}

实际上我正在做的是使用单例作为锚点来更轻松地访问数据对象 ,然后在需要时传递它。 换句话说,当他们被雇用时,我也不希望我的代码充斥着GameManager.sharedInstance

如前所述,这种方法肯定有很多缺点。 其中之一是并发。 但是,我将指出,即使数据不在全局/单例中,并发数据仍然可以发生。

我对此添加的观点是游戏开发(和一般编码)涉及一定程度的实用性。 您不仅要使用系统资源进行平衡操作来展示您的游戏,而且可能是您正在为人们制作游戏。 要做到这一点,你需要完成它。 每个人都希望制作出完美的雪花,但这种完美的时间成本是多少? 在答案开始时,我指出这种技术是一种工具。 全球/单身人士不适合所有人。 与此同时,人们也不应盲目追随设计咒语。 如果它可以成为您的有效工具并帮助您更快地完成游戏,那么它值得做。 总会有另一个游戏要写,然后你可以根据经验决定哪些有效,哪些无效。

一般经验法则:如果可以避免全局变量,请执行此操作。

现在每个节点都有一个名为userData的可变字典。 这旨在用于将数据保存到节点。

创建新场景后,从旧场景传输所需的任何数据。

let scene = SKScene(fileNamed:....)
scene.userData["highscore"] = self.highscore
view.presentScene(scene)

现在在新的场景didMove(view:)方法中,你从所说的userData读取

正如0x141E指出的那样,记住你需要初始化userData ,因为你可能有自定义类,你可以在init方法中执行此操作,只需记住覆盖所有指定的inits,以便您可以访问方便方法。

另外,正如Whirlwind和我之间的评论中所提到的, SKNode符合NSCoding标准,因此当您保存信息时,您可以将SKNode整体保存, userData将随之保存。

这意味着你所要做的就是为SKNode编写一个扩展来保存/加载节点,这样就不用担心管理另一个类了。

在游戏执行期间,可能发生许多事情,游戏发生崩溃,玩家关闭应用程序,互联网停止,节点可能从父级暂停(因此当场景处理动作时将跳过它)。 出于这个原因,我同意Simone Pistecchia的意见:您可以准备一个“存储管理器”类来加载和保存以存档您的玩家统计数据,使用NSCoding来声明和初始化它们,使用共享实例类在整个项目中处理它们。 通过这种方式,您可以在需要时加载并保存您的变量并将其显示在游戏之外(在主菜单中显示“高分”场景,例如您想要的任何地方)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM