当前位置: 首页 > 面试题库 >

在Swift中“暂停”游戏

笪煌
2023-03-14
问题内容

我在Swift中创建了一个游戏,其中涉及怪物的出现。怪物根据计时器的出现和消失如下:

func RunAfterDelay(_ delay: TimeInterval, block: @escaping ()->()) 
{
    let time = DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)

    DispatchQueue.main.asyncAfter(deadline: time, execute: block)
}

然后我就这样称呼它(例如在2秒后生成):

///Spawn Monster
RunAfterDelay(2) { 
                [unowned self] in
                self.spawnMonster()
 }

然后,我进行了类似的隐藏操作(在x秒后,我将怪物消灭了)。

因此,我在屏幕顶部创建了一个设置图标,当您点击它时,会出现一个巨大的矩形窗口以更改游戏设置,但是自然的问题是怪物仍然在背景中生成。如果我将玩家带到另一个屏幕,我相信我会失去我所有的游戏状态,并且必须重新开始才能回到它的状态(玩家可能处于游戏的中间)。

有没有办法告诉我以上创建的所有游戏计时器,即

DispatchQueue.main.asyncAfter(deadline: time, execute: block)

要暂停并继续说话吗?我想用所有计时器都可以(如果没有一种方法可以标记和暂停某些计时器)。

谢谢!


问题答案:

我已经解决了这个问题,并希望在下面的结论中分享我的工作时间,以研究/编码。为了更简单地说明问题,我实际上想实现这一目标(不仅仅是简单地使用SpriteKit场景暂停,这很容易):

  1. 在Swift中启动一个或多个计时器
  2. 停止所有计时器(当用户按下暂停时)
  3. 当用户取消暂停时,所有计时器都将重新启动, 此处停止

有人向我提到,因为我正在使用DispatchQueue.main.asyncAfter,所以无法以我想要的方式暂停/停止(可以取消,但我离题了)。毕竟,我正在做asyncAfter。但是要真正使用计时器,您需要使用NSTimer(在Swift3中现在称为Timer)。

经过研究,我发现 实际上不可能 暂停/取消暂停,因此当您想重新启动暂停的计时器时,可以通过创建一个新计时器(每个计时器)来“作弊”。我的结论如下:

  1. 每个计时器启动时,请记录您需要的延迟(我们将访问后者),并记录该计时器 “触发”的时间。因此,例如,如果它在3秒内启动并执行代码,则将时间记录为Date()+ 3秒。我使用以下代码实现了这一点:
//Take the delay you need (delay variable) and add this to the current

time

let calendar = Calendar.current  
let YOUR_INITIAL_TIME_CAPTURED = calendar.date(byAdding: .nanosecond,

value: Int(Int64(delay * Double(NSEC_PER_SEC))), to: Date())!

  1. 既然您已经记录了计时器将触发的时间,您就可以等待用户按下Stop键了。完成后,您将使用.invalidate()使每个计时器无效,并立即记录停止的时间。实际上,此时,您还可以完全计算出用户启动时所需的剩余延迟:
//Calculate the remaining delay when you start your timer back
let elapsedTime = YOUR_INITIAL_TIME_CAPTURED.timeIntervalSince(Date)
let remainingDelay = YOUR_INITIAL_TIMER_DELAY - elapsedTime
  1. 当用户点击启动时,您可以通过简单地创建新的计时器来再次启动所有计时器,利用上述的余数(remainingDelay)和viola`,您将拥有新的计时器。

现在,因为我有多个计时器,所以我决定需要在AppDelegate(通过服务类访问)中创建一个词典,以保留所有活动计时器。每当计时器结束时,我都会将其从字典中删除。我最终制作了一个特殊的类,该类具有计时器,初始延迟及其启动时间的属性。从技术上讲,我可以使用数组,也可以将timer键放在该类上,但是我离题了。

我创建了自己的addTimer方法,该方法将为每个计时器创建一个唯一的键,然后在计时器的代码完成后,将按以下方式自动删除:

  let timerKey = UUID().uuidString

let myTimer: Timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) {
            _ in
               block()
               self.timers.removeValue(forKey: timerKey)
            }

        }

注意:block()只是调用您包装在计时器中的任何块。例如,我做了一些很酷的事情:

addTimer(delay: 4, repeating: true)
        { [unowned self] in
            self.spawnMonster()
        }

因此,addTimer将运行self.spawnMonster代码(作为block()),然后在完成后将其从字典中自动删除。

后来我变得更复杂了,做了一些事情,例如保持重复计时器的运行而不是自动删除,但是出于我的目的,这只是很多非常具体的代码,可能会占用太多的回复:)

无论如何,我真的希望这对某人有帮助,并希望回答任何人遇到的任何问题。我花了很多时间!

谢谢!



 类似资料:
  • 抱歉我的问题,我被卡住了。我是lib gdx开发游戏的新手,不要严格评判我。我有我的游戏活动: } 我有两个屏幕: 还有我有暂停按钮的游戏屏幕: 还有我的主要问题--

  • 暂停脚本的当前线程。 #p::Pause ; 按一次 Win+P 会暂停脚本. 再按一次则取消暂停. Pause [, On|Off|Toggle, OperateOnUnderlyingThread?] 参数 On|Off|Toggle 如果为空或省略, 则它默认为 Toggle. 否则, 请指定下列单词的其中一个: Toggle:如果在当前线程下的潜在线程处于运行状态,则暂停当前线程,否则让潜

  • 问题内容: 我成功使用以下代码暂停了一个场景游戏: 但是我有一个问题。游戏中的所有随机间隔都会创建对象并将其显示在屏幕上。当我暂停游戏时,它将继续在后台创建对象,而当我继续游戏时,在暂停期间创建的所有对象将同时显示在屏幕上。 我该如何解决? 问题答案: 暂停SKView时,无法将SKLabelNode(或其他任何东西)添加到场景中。您将需要返回运行循环,以便在暂停游戏之前添加您的文本。这是一种方法

  • 问题内容: 我已经阅读了有关该主题的所有内容,但仍然无法弄清我的问题。我尝试在appdelegate的每个区域暂停游戏 在我的控制器中: 我知道pauseGame可以用,因为如果我在场景中用按钮切换它,它将停止游戏。即使我在将skview和场景加载到控制器中后立即暂停它们,游戏也不会在启动时暂停。在游戏中暂停游戏很容易。但是由于某种原因,每当我退出并恢复该应用程序时,游戏便会自行取消暂停。 我注意

  • 问题内容: 考虑下面的代码…阅读前5行后,我试图暂停流: 输出(接下来复制)表明它在暂停后继续读取行。也许readline在缓冲区中排队了几行,并且无论如何都将它们喂给我…如果它继续在后台异步读取,这是有道理的,但是根据文档,我不知道该怎么做。应该有适当的行为。关于如何达到预期效果的任何建议? 问题答案: 因此,事实证明,即使在pause()之后,readline流也趋向于“滴落”(即泄漏一些额外

  • 1.若要暂停训练,请按下该按钮。显示暂停。若要继续训练,请点击绿色箭头图标。 2.若要停止训练,在记录训练期间或处于暂停模式时长按该按钮三秒钟,直至计数器清零。或者您可以点击并按住显示屏上的红色停止按钮。 如果在暂停后停止训练,则暂停后经过的时间不包括在总训练时间内。