Timer未触发问题:重复调用loadPatternEnemies无法生成敌人
嘿,我来帮你拆解这个问题!从你描述的现象——第二次调用loadPatternEnemies(number: 0)能打印in here?却没生成敌人——来看,问题大概率出在你使用的Timer.scheduledTimer相关逻辑上,下面是几个最常见的诱因和对应的解决思路:
1. 旧Timer未清理导致逻辑冲突
如果第一次调用时创建的parentTimer没有被失效(invalidate),第二次调用又新建了一个Timer,很可能会导致两个Timer的逻辑互相干扰,或者旧Timer占用了资源,让新Timer的敌人生成逻辑无法正常执行。而且如果parentTimer是函数内的局部变量,第一次调用结束后它会被销毁,但Timer.scheduledTimer会被RunLoop持有,重复创建多个Timer只会让情况更混乱。
修复方案:
把parentTimer改成类的属性(别放在函数里当局部变量),这样每次调用loadPatternEnemies前,先检查并清理旧Timer,再创建新的:
class YourGameClass { // 把Timer定义为类属性,方便管理 var parentTimer: Timer? func loadPatternEnemies(number: Int) { switch number { case 0: print("in here?") // 先干掉旧Timer,避免冲突 parentTimer?.invalidate() // 创建新Timer,记得用弱引用捕获self parentTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in // 这里写你的敌人生成逻辑 // 等这波敌人全生成/击败后,记得调用timer.invalidate()销毁Timer } // 处理其他case... } } }
2. Timer的RunLoop模式不匹配
如果你的游戏是基于SpriteKit,或者在UIKit的特殊场景下运行(比如用户正在滑动屏幕),直接用Timer.scheduledTimer可能会让Timer“罢工”——因为默认的scheduledTimer会被加到RunLoop的default模式下,而当RunLoop处于UITrackingRunLoopMode(比如处理触摸滑动)时,default模式的Timer会暂停。
修复方案:
手动把Timer加到RunLoop的common模式下,确保它在各种场景都能正常触发:
parentTimer = Timer(timeInterval: 1.0, repeats: true) { [weak self] timer in // 敌人生成逻辑 } // 添加到common模式,保证不会被UI操作打断 RunLoop.current.add(parentTimer!, forMode: .common)
另外提一句:如果是SpriteKit游戏,更推荐用SKAction.wait(forDuration:)或者SKScene的update(_:)方法来处理定时逻辑,因为SpriteKit有自己的游戏循环,Timer很容易和它不同步。
3. 敌人生成的容器节点状态异常
虽然你能打印日志,但敌人生成的父节点可能已经被移除、隐藏,或者游戏状态已经切换到非战斗模式了,导致生成的敌人根本没法显示出来。
排查&修复:
在敌人生成的代码里加几行日志,确认父节点的状态:
// 假设你把敌人加到gameScene这个节点上 print("父节点是否隐藏:\(gameScene.isHidden)") print("父节点是否在场景树中:\(gameScene.parent != nil)")
同时检查敌人生成的坐标、zPosition等属性,别把敌人生成到屏幕外面或者被其他节点挡住了。
4. 循环引用导致Timer失效
如果你的Timer闭包没有用[weak self]捕获self,会形成循环引用:self持有Timer,Timer的闭包又持有self,导致第一次的Timer和self都没法被释放,第二次调用的Timer自然也没法正常工作。
修复方案:
闭包里一定要用弱引用捕获self,还可以加个guard判断self是否存在,避免野指针:
parentTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in // 先确认self还存在,否则销毁Timer guard let self = self else { timer.invalidate() return } // 这里写敌人生成逻辑 }
内容的提问来源于stack exchange,提问作者R Bradshaw

