我有一款游戏,希望随着时间的推移更新“能量”栏。因此,每5分钟,玩家应该获得一个额外的能量点来使用。我会知道如何在单个场景中执行此操作,但是无论您处于哪个场景中以及在后台中执行此操作,都应该对此进行更新,但是我一直在努力以最佳方式实现这一目标。
对于背景知识,我想已经涵盖了。当应用程序发送到后台时,我会存储时间,当应用程序再次进入前台时,我会得到当前时间,并计算出它们应该获得多少能量。
我正在努力的最佳方法是跨场景管理此问题并相应地更新UI(我打算将其显示在所有屏幕的左上方)。我的第一个想法是要有一个单例对象,该对象读取存储的能量水平并具有一个计时器(在Sprite Kits更新循环之外)。这每5分钟运行一次,并更新能量水平。然后,它可以发布通知,以便任何非SpriteKit视图都可以显示当前能量,类似地,任何SpriteKit场景都可以使用update循环读取值并相应地显示它。但是我不确定这是否有效。
我应该将一个对象附加到appdelegate来执行类似于Singleton的操作。这将使我能够轻松比较应用程序再次激活时的时间(例如,刷新能量级别需要2.5分钟)
有人可以建议这是一个好方法还是有更好的方法来实现这一目标?
答案 0 :(得分:1)
因此,如果我理解正确,那么您在这里有两个问题;一种是在不考虑场景的情况下显示能量条,另一种用于计算在应用关闭时获得的能量。
关于第一个问题(在所有场景中显示能量条),您应该看一下SKReferenceNode
我在一款具有全局菜单的游戏中所做的是: 我为菜单创建了一个单独的SpriteKit场景(.sks文件)。然后,我在GameScene.sks文件中添加了SKReferenceNode。在GameScene.swift文件中,我将ReferenceNode视为普通的SKSpriteNode,如下所示:
class GameScene: SKScene {
// Reference node added in GameScene.sks
var playerHealthBar : SKSpriteNode!
override func didMove(to view: SKView) {
// Reference node
playerHealthBar = self.childNode(withName: “//playerHealthBar”) as? SKSpriteNode
}
}
使用SKReferenceNode,您可以在我们所有的场景中实现该节点。然后,您可以随时更新节点。我使用这种方法在游戏中创建全局菜单栏(例如,可以显示用户的黄金,健康点等)
对于有关playerHealthBar的数据(例如,健康点),您可以从UserDefaults中加载/保存。
对于第二个问题,一个想法是在UserDefaults中存储一个名为lastActive
的变量。当应用进入后台时更新此值。
当应用程序进入前台时,您需要将当前时间与lastActive
变量进行比较。
看看这个:
func timeSpentAwayInSeconds() -> Int {
// We then check when the player was last active
let lastActive = UserDefaults.standard.object(forKey: "lastActive")
// Then we compare that to the current date
let elapsedTime = Date().timeIntervalSince(lastActive as! Date)
// Covert that to seconds
let elapsedTimeInSeconds : Int = Int(elapsedTime)
return elapsedTimeInSeconds
}
我希望这能有所帮助。如果解决了您的问题,请标记为已回答:-)
祝您编程愉快!
答案 1 :(得分:1)
这是您要使用Timer
的罕见情况之一。
let date = Date().addingTimeInterval(5)
let timer = Timer(fireAt: date, interval: 0, target: self, selector: #selector(runCode), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: .common)
这是因为您依赖于现实世界的时间,而不是游戏时间。
现在请记住,由于您不打算做其他事情,因此需要跟踪计时器的失火情况。您的runCode
函数应链接到您要更新的场景的委托,并且不应递增。相反,它应该检查代码的上次触发时间,并将其与代码触发的当前时间进行比较,以确定要添加的点数。它的行为应与您离开并返回应用程序时使用的代码完全相同。
现在,如果您需要执行任何UI更新,我建议将其附加到您附加到场景的SKAction上,这样,如果场景处于暂停状态或未加载状态,它将在场景恢复时触发。这也保证了您的UI更改是在主线程上完成的,而不是您的计时器可能处于的任何线程。
现在,根据您设计游戏的方式,我建议使用另一种方法。如果您有多个场景都具有相同的覆盖层,那么我建议覆盖层甚至不属于SpriteKit。相反,创建一个位于SpriteKit场景之上的UIView层。该视图的作用是删除不需要在场景外的每一帧的节点,从而使场景执行速度更快,因为它必须在较少的节点之间进行渲染,排序和过滤。最重要的是,您只需要更新1个视图即可,而不是X个场景。