【问题标题】:Multiple NSTimers in spritekit not working as expectedspritekit 中的多个 NSTimer 未按预期工作
【发布时间】:2016-03-28 15:30:00
【问题描述】:

目标:

我有粒子从屏幕顶部落下,我希望它们在分数达到一定值时提高速度。

上一次失败的尝试:

我首先制作了那些受重力影响的下落粒子,并尝试在分数达到一定值时增加重力值。但是它似乎不起作用,所以我认为在游戏过程中不能改变重力。

现状:

经过一番研究,我想到了使用两个全局 NSTimer,slowTimerfastTimerslowTimerdidMoveToView 中调用。在touchesBegan,每次点击,我都会调用一个函数,changeSpeed,就是这样:

    func changeSpeed()
        {

            if score >= 2

            {
                slowTimer.invalidate()
                makeWallsDividerT = 300

                fastTimer =  NSTimer.scheduledTimerWithTimeInterval(1.4, target: self, selector: Selector("makeWalls"), userInfo: nil, repeats: true)
            }

        }

makeWallsDividerTfunction makeWalls中的一个变量,被fastTimer调用。

当前情况的问题:

当游戏结束并且一切都被重置时,我执行fastTimer.invalidate() 并且似乎仍然存在问题,因为它没有重置为慢定时器。此外,似乎两个计时器都在重置后打开,游戏比 fastTimer 快得多。

请指点一下?或者任何其他方式来实现我想要做的事情? 谢谢!!

【问题讨论】:

  • 如何创建粒子?这有点令人困惑,因为粒子通常是指SKEmitterNode 发射的粒子。顺便说一句,使用SKActions 而不是NSTimer...SKActions 与游戏循环配对。
  • 我很好奇 changeSpeed 被调用了多少次,您可能有多个计时器被触发,但 fastTimer 只保留您最后分配的计时器,其他计时器无法失效。跨度>
  • @Knight0fDragon:游戏开始时,我希望 slowTimer 只被调用一次,直到得分达到 2。然后它将在游戏的其余部分使用 fastTimer,直到游戏结束。然后当游戏重新开始时,它会再次是slowTimer,直到得分为2,然后是fastTimer,直到游戏结束。随着游戏的进行,相同的循环将发生多次。谢谢!!
  • 您是说每次调用 touchBegans 时,您都在改变速度,这就是我问我的问题的原因。如果您每次点击都没有使 fastTimer 无效,那么您将触发许多计时器
  • @Knight0fDragon 啊 icc!感谢那。请问您有什么建议吗?我想做的是:stackoverflow.com/questions/36265482/…

标签: ios swift sprite-kit swift2


【解决方案1】:

要重新编写代码以便不再使用 NSTimers,您需要执行以下操作:

override func didMoveToView(view:SKView)
{
    let spawnWall = SKAction.runBlock({[unowned self] in self.makeWalls();});
    let sequence = SKAction.sequence([SKAction.waitForDuration(3),spawnWall])
    let repeatForever = SKAction.repeatActionForever(sequence);
    self.runAction(repeatForver, withKey:"makeWalls"); 
}

func changeSpeed()
{
    if score >= 2
    {
        self.removeActionForKey("makeWalls");  
        makeWallsDividerT = 300;
        let spawnWall = SKAction.runBlock({[unowned self] in self.makeWalls();});
        let sequence = SKAction.sequence([SKAction.waitForDuration(1.4),spawnWall])
        let repeatForever = SKAction.repeatForever(sequence);
        self.runAction(repeatForver, withKey:"makeWalls"); 

    }
}

这将防止触发多个操作。 (在您的情况下,您有多个计时器被触发,并且您只停止了最后一个计时器。)

【讨论】:

  • 你可能不想让 makeWallsDividerT 成为全局变量,你可以将它传递给你的 makeWalls 函数
  • 非常感谢您的耐心等待先生。第一个代码块运行得非常好,就像 NSTimer 一样。然而,第二个根本没有被执行。也许这是因为我们没有从 didMoveToView 调用 changeSpeed()。所以我认为当类中的函数按时间顺序执行时,当它到达 changeSpeed 时,它会检查分数。由于分数不 >=2,因此不执行该代码块 (changeSpeed)。但是我希望它在分数为该值时执行。你能帮忙吗?谢谢!
  • 等等我又糊涂了
  • 这东西太棒了,伙计!在 touchesBegan 中调用它可以解决问题并且效果很好!现在唯一的问题是,当游戏重置时,它默认每 1.4 秒调用一次 makeWalls 函数。它意味着每 3 秒调用一次,直到分数达到 >=2,然后它应该每 1.4 秒调用一次。感谢您的耐心等待。
  • 我不需要理解你的逻辑在做什么,我们已经远远超出了这个答案的范围,didMoveToView 只有在场景移动到视图时才会被调用(通常这是一次),如果您需要再次调用该函数,您需要手动声明您希望再次调用它。如果您需要在 didBeginContact 中再次调用它,那很好。但是你只需要确保你的逻辑是正确的。我发现用你的母语写出来并在你的脑海中逐步完成它更容易设计游戏。一旦你掌握了语言,然后尝试将其翻译成 swift
【解决方案2】:

问题是你根本不应该在SpriteKit 中使用NSTimers

使用SKAction:

    runAction(
        SKAction.sequence([
            SKAction.waitForDuration(1.4),
            SKAction.runBlock({
                self.makeWalls()
            })
        ])
    )

【讨论】:

  • 感谢您的 hamobi。您的意思是创建一个名为 runAction 的新函数吗?从那时起我会在哪里称呼它?对不起,我是 SpriteKit 的新手。一旦分数 >=2,我希望更频繁地调用函数 makeWalls。谢谢。
  • 您可以将此类操作分配给您的场景。在 GameScene.swift 中,当您准备好运行动作(不要在更新中运行)时,只需调用 self.runAction(....)。请注意,如果您最终使用 repeatforever 操作,那么当您完成操作时,您将需要实际删除操作,hamobi 在他的回答中没有讨论,
  • 对 SKAction 做一点研究,它是 spritekit 的一个非常基础的部分。您可以从场景或任何 SKNode 对象调用 runAction
  • 嗨,所以逻辑是从 didMoveToView 函数调用 makeWalls 函数。这(makeWalls)创建了一次且仅一次移动到屏幕末尾的墙。为了重复创建这些墙,我在 didMoveToView (slowTimer) 中创建了一个 NSTimer,它每 3 秒调用一次 makeWalls。我想要的是在分数大于 2 时修改这个 NSTimer(slowTimer),以便现在每 1.4 秒调用一次 makeWalls。根据我的研究,这不能直接完成。因此不确定。谢谢!
【解决方案3】:

NSTimer 的替代方法是使用SKActions。

SKAction *wait = [SKAction waitForDuration:1.0f]; 

【讨论】:

  • 感谢您的回复。我会在哪里使用这个?本质上,我希望函数 makeWalls 一旦 score>=2 被更频繁地调用
  • 我们正处于 swifts 编程生活的一个阶段,进入它的人不了解目标 c,所以为了让你的答案更好,我会提到如果 OP 没有,你正在用目标 c 编写它们使用目标 c 标签或在问题中说目标 c 可以作为答案
【解决方案4】:

因为生成动作很简单,例如。等待 + 添加墙,你可以修改生成动作的速度,如下所示:

import SpriteKit

class GameScene: SKScene {

    var score = 0 { didSet{changeSpawningSpeed() } }

    override func didMoveToView(view: SKView) {
        /* Setup your scene here */

        let wait = SKAction.waitForDuration(2.0)
        let block = SKAction.runBlock({ [unowned self] in self.spawnWalls()})
        let sequence = SKAction.sequence([wait, block])

        runAction(SKAction.repeatActionForever(sequence), withKey: "spawning")
    }

    func spawnWalls(){print("Walls spawned")}

    func changeSpawningSpeed(){

        if score == 2 {
            if let action = actionForKey("spawning"){action.speed += 1.0}
        }
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        score += 1
    }
}

【讨论】:

  • 所以,如果你想提高每次动作的速度,你可以将条件更改为&gt;= 2 ...这取决于你如何以及何时提高产卵动作的速度.
  • 我会在我的版本中完成速度,但我不确定这个人的数学技能是否可以计算出适用于速度的数学,以便在他们需要间隔触发时进行任何时间更改。
  • @Knight0fDragon 是的,这可以通过几种方式完成,修改速度只是其中之一......最初我投票赞成你的答案,因为总的来说,这将是一个可行的解决方案。运行一个动作,并在适当的时候重置它(你也可以稍微缩短它并避免代码重复)。但我将此添加为另一种可能的解决方案。谢谢你的支持(如果是你的话):)
  • 是的,我投了赞成票,因为您的答案是一个可行的答案,我同意代码可以短得多,但是从代码中您可以看出 OP 是新手,我们都处于那种状态更容易理解。 += 1 的触感很好,使其符合 swift 3 标准
  • @Knight0fDragon 哈哈,你已经注意到 += 了。我无法想象没有 ++ 东西的编程语言......但正如你所见,我正在慢慢适应 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2020-09-24
相关资源
最近更新 更多