Ste*_*ves 3 ios sprite-kit swift
在我的sprite-Kit游戏中,我的游戏经历了各种场景,我想要某些变量可用.请参见下图:
因此标题屏幕加载主游戏屏幕.当'wave'结束(所有客观完成或所有生命丢失)时,最终场景会显示一些统计数据.然后,如果玩家还有生命,则再次加载主游戏,否则再次显示标题画面.
我希望玩家的得分和生命数量可以在主要游戏场景和结束场景中使用; 所有场景都应保持高分.
我正在使用全局变量,而我的大多数变量都是在类下定义的属性.
第一次我不得不这样做(在我加入高分或多次生命之前为我的得分属性)我在每个场景上使用得分属性并在场景之间传递它,当我为下一个场景做现在的场景时,但这看起来很笨拙,特别是当我不得不添加更多变量时.
是否有最佳实践,即应使用/不使用全局变量,如果没有,是否有正确的方法来处理属性,可能由您初始化它们的位置控制,例如与之init相比didMoveToView,这决定了它们是否已重新初始化什么时候再次显示场景?
编辑:
当我从End Wave Scene回到主游戏画面时,我做了什么"错误",导致主画面再次重新初始化?有没有办法保存它(包含它的所有属性)所以它没有完全重新初始化但是它的didMoveToView再次被调用?
一般经验法则:如果可以避免使用全局变量,请这样做。
现在,每个节点都有一个名为的可变字典userData。设计用于将数据保存到节点。
创建新场景后,从旧场景传输所需的任何数据。
let scene = SKScene(fileNamed:....)
scene.userData["highscore"] = self.highscore
view.presentScene(scene)
Run Code Online (Sandbox Code Playgroud)
现在在新场景didMove(view:)方法中,您从userData
正如0x141E指出的那样,请记住您需要初始化userData,因为您可能有一个自定义类,因此可以在init方法中执行此操作,只需记住重写所有指定的init即可访问便捷方法。
另外,正如Whirlwind和我本人之间的评论中所述,它 SKNode是NSCoding合规的,因此,当您保存信息时,可以将其SKNode作为一个整体保存,并userData会随之保留。
这意味着您所要做的就是编写一个扩展SKNode以保存/加载该节点,从而避免了担心管理另一个类的麻烦。
我知道许多人使用广义规则,如"不要使用全局"或"单身人士是邪恶的".这些智慧的话语有时可能适用,但另一种看待它们的方式是作为让你更有效地完成游戏的工具.
有时候全局/单身人士很有用.游戏和嵌入式系统遵循与传统应用程序不同的规则.这些通常是资源受限的"应用程序",与性能要求相结合.Globals比你想象的更常见.单身人士也常用.我曾经在嵌入式系统和游戏上工作过,可以说在我参与过的每个项目中,他们都使用全局,单例或两者兼而有之.
在实践中,我使用单身而不是全局.主要是因为我不必担心在哪里推送全局及其所属的头文件.注意,原因的基础将与你所有的Swifties不同.对我来说,这些东西主要是在C++中完成的,有一些Obj-C和一些Swift.
管理游戏生命周期的需求是独立的SpriteKit.SpriteKit我在SO上看到的很多帖子都有开发者将游戏状态嵌入到场景或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()
}
Run Code Online (Sandbox Code Playgroud)
如果这是一个全局的,它将在任何范围之外对实例进行一些全局定义.喜欢:
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?
Run Code Online (Sandbox Code Playgroud)
像这样有助于管理全球游戏状态的代码全部包含在内GameManager.换句话说,GameManager它不仅仅维护值,还提供了一种围绕这些值封装功能的方法.
嗯,似乎currentScene也在这里?那为什么会这样?这GameManager也是管理场景变化的绝佳方式.更有可能通过游戏状态完成.也许是这样的:
GameManager.sharedInstance.gameState = GameOverState
在幕后,gameState设置者可以做出交换场景的魔力.
这些只是这种方法的实际性质的几个例子.
关于加载/存储的讨论,这也可以通过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()
}
Run Code Online (Sandbox Code Playgroud)
等你说; "但我不喜欢这种全球/单身爵士乐.为什么我不把这个对象传递给需要它的每个人." 答案是"你可以".但在实践中,你会发现这变得乏味.您也可能会发现您发现自己需要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()
}
Run Code Online (Sandbox Code Playgroud)
另外,我最终会绕过PlayerState实例而不是期望代码总是通过全局/单例获取它.
所以例如我会这样做:
func doSomething(playerState:PlayerState) {
var score = playerState.score
// Blah blah blah
}
Run Code Online (Sandbox Code Playgroud)
VERUS
func doSomething() {
// Whee I have singleton
var score = GameManager.player.score
// Blah blah blah
}
Run Code Online (Sandbox Code Playgroud)
实际上我正在做的是使用单例作为锚点来更轻松地访问数据对象,然后在需要时传递它.换句话说,虽然他们正在受雇,但我也不希望我的代码充斥着GameManager.sharedInstance.
如前所述,这种方法肯定有很多缺点.其中之一是并发.但是,我将指出,即使数据不在全局/单例中,并发数据仍然可以发生.
我对此添加的观点是游戏开发(和一般编码)涉及一定程度的实用性.您不仅要使用系统资源进行平衡操作来展示您的游戏,而且可能是您正在为人们制作游戏.要做到这一点,你需要完成它.每个人都希望制作出完美的雪花,但这种完美的时间成本是多少?在答案开始时,我指出这种技术是一种工具.全球/单身人士不适合所有人.与此同时,人们也不应盲目追随设计咒语.如果它可以成为您的有效工具并帮助您更快地完成游戏,那么它值得做.总会有另一个游戏要写,然后你可以根据经验决定哪些有效,哪些无效.
| 归档时间: |
|
| 查看次数: |
942 次 |
| 最近记录: |