在 iOS 游戏中记录帧率(FPS)是一个常见的需求,用于优化性能、调试帧率波动、确保游戏流畅运行等。以下是如何在 iOS 中记录游戏帧率的几种常见方法,适用于 Objective-C、Swift 和 Xcode 开发环境。
✅ 一、使用 CADisplayLink 记录帧率
CADisplayLink 是一个用于在屏幕刷新率的节奏下定时执行回调的类,非常适合用来记录帧率。
✅ 示例代码(Swift):
import UIKit
class GameViewController: UIViewController {
var frameRate: Int = 0
var lastTime: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
// 设置 CADisplayLink
let displayLink = CADisplayLink(target: self, selector: #selector(updateFrameRate))
displayLink.delegate = self
displayLink.paused = true
displayLink.isPaused = true
// 开始显示链接
displayLink.schedule(in: .now(), for: .ever)
}
@objc func updateFrameRate() {
// 计算帧率
let currentTime = CACurrentTime()
let deltaTime = currentTime - lastTime
// 假设屏幕刷新率是 60 FPS
frameRate = Int(1.0 / (deltaTime / 60.0))
lastTime = currentTime
}
// 自定义 CADisplayLink 的 delegate 方法
func displayLink(timer: CADisplayLink) {
updateFrameRate()
}
}
✅ 说明:
CADisplayLink会以屏幕刷新率(通常为 60 FPS)自动调用updateFrameRate。- 每次调用
updateFrameRate时,计算当前时间与上一次的时间差。 - 通过
1.0 / (deltaTime / 60.0)计算出每秒的帧数,单位为 FPS。
✅ 二、使用 DispatchQueue 和 Timer 记录帧率
如果你不想使用 CADisplayLink,也可以使用 DispatchQueue 和 Timer 来记录帧率。
✅ 示例代码(Swift):
import UIKit
class GameViewController: UIViewController {
var frameRate: Int = 0
var lastTime: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
// 使用 Timer 计算帧率
let timer = Timer.init(deadline: .now(), interval: 1.0 / 60.0, repeats: true)
timer.fire()
// 保存当前时间
lastTime = CACurrentTime()
}
func updateFrameRate() {
let currentTime = CACurrentTime()
let deltaTime = currentTime - lastTime
frameRate = Int(1.0 / (deltaTime / 60.0))
lastTime = currentTime
}
}
✅ 说明:
- 使用
Timer每秒触发一次,计算帧率。 - 通过
1.0 / (deltaTime / 60.0)计算出每秒的帧数。
✅ 三、使用 CVPixelBuffer 和 CFAbsoluteTime 记录帧率(更精确)
如果你需要更精确的帧率记录(例如在游戏循环中),可以使用 CVPixelBuffer 和 CFAbsoluteTime。
✅ 示例代码(Swift):
import UIKit
import CoreVideo
class GameViewController: UIViewController {
var frameRate: Int = 0
var lastTime: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
// 使用 CFAbsoluteTime 计算帧率
let startTime = CFAbsoluteTime()
// 在游戏循环中执行
while true {
let endTime = CFAbsoluteTime()
let deltaTime = endTime - startTime
frameRate = Int(1.0 / (deltaTime / 60.0))
// 重置时间
startTime = endTime
}
}
}
✅ 说明:
CFAbsoluteTime是 macOS 和 iOS 中的高精度时间函数,可以用于更精确的帧率计算。
✅ 四、使用 NSRunLoop 或 NSOperationQueue 记录帧率
如果你在游戏主循环中运行,可以使用 NSRunLoop 来确保帧率的稳定。
import UIKit
class GameViewController: UIViewController {
var frameRate: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// 设置 NSRunLoop
let runLoop = NSRunLoop.currentRunLoop()
runLoop.add(self, forMode: .commonModes)
// 启动运行循环
runLoop.run()
}
@objc func updateFrameRate() {
let currentTime = CACurrentTime()
let deltaTime = currentTime - lastTime
frameRate = Int(1.0 / (deltaTime / 60.0))
lastTime = currentTime
}
}
✅ 五、使用 GCD 记录帧率(适用于游戏主循环)
如果你在游戏主循环中运行,可以使用 DispatchQueue 来记录帧率。
import UIKit
class GameViewController: UIViewController {
var frameRate: Int = 0
var lastTime: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
// 使用 GCD 计算帧率
DispatchQueue.global(qos: .userInitiated).async {
self.updateFrameRate()
}
}
@objc func updateFrameRate() {
let currentTime = CACurrentTime()
let deltaTime = currentTime - lastTime
frameRate = Int(1.0 / (deltaTime / 60.0))
lastTime = currentTime
}
}
✅ 总结
| 方法 | 优点 | 缺点 |
|---|---|---|
CADisplayLink |
适合游戏主循环,自动与屏幕刷新率同步 | 需要处理 displayLink 的生命周期 |
Timer |
简单易用 | 需要手动管理时间 |
CFAbsoluteTime |
精度高 | 需要处理时间计算 |
NSRunLoop |
适合游戏主循环 | 需要处理运行循环 |
GCD |
简单 | 依赖于 GCD 的调度机制 |
✅ 推荐方案
- 推荐使用
CADisplayLink,因为它直接与屏幕刷新率同步,适合游戏主循环。 - 如果你使用的是游戏引擎(如 SpriteKit、SceneKit),可以使用引擎提供的帧率记录方法。
✅ 附:如何在 Xcode 中查看帧率
在 Xcode 中,你可以通过以下方式查看帧率:
- 打开你的项目,进入 Debug Navigator。
- 找到
GameViewController或你的游戏类。 - 在
GameViewController中添加一个@property (nonatomic) NSInteger frameRate;。 - 在
updateFrameRate方法中,将frameRate的值打印出来。
如需更详细的代码示例或针对特定游戏引擎的帧率记录方法,欢迎继续提问!