如何在Unity中为第一人称角色相机实现15fps帧率截图
解决Unity第一人称相机每1/15秒截图的问题
首先先排查最容易忽略的细节:检查Unity Inspector面板中你的SS脚本的fps参数是否设置为15。如果这个值被误设为1,就会导致每秒只截一张图,这很可能是你当前遇到的核心问题。
如果fps参数已经正确设置,那问题出在Update中用Time.deltaTime累加的方式容易受帧率波动影响,导致时间间隔不准确。下面提供两种更可靠的解决方案:
方案一:修复Update逻辑,优化时间累加
调整当前的代码逻辑,避免冗余判断,同时优化时间累加方式减少误差:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SS : MonoBehaviour { public int resWidth = 640; public int resHeight = 480; public int fps = 15; private float timeSinceLastShot = 0f; private float captureInterval => 1f / fps; // 计算每帧间隔 public static string ScreenShotName(int width, int height) { return string.Format("{0}/screenshots/screen_{1}x{2}_{3}.png", Application.dataPath, width, height, System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-fff")); // 增加毫秒,避免文件名重复覆盖 } void Update(){ timeSinceLastShot += Time.deltaTime; // 用while循环确保帧率过低时不会漏帧 while (timeSinceLastShot >= captureInterval) { TakeHiResShot(); timeSinceLastShot -= captureInterval; // 减去间隔而非重置为0,避免长期误差累积 } } private void TakeHiResShot() { RenderTexture rt = new RenderTexture(resWidth, resHeight, 24); Camera cam = GetComponent<Camera>(); cam.targetTexture = rt; Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false); cam.Render(); RenderTexture.active = rt; screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0); // 清理资源,避免内存泄漏 cam.targetTexture = null; RenderTexture.active = null; Destroy(rt); // 保存截图 byte[] bytes = screenShot.EncodeToPNG(); string filename = ScreenShotName(resWidth, resHeight); System.IO.File.WriteAllBytes(filename, bytes); Debug.Log($"Took screenshot to: {filename}"); Destroy(screenShot); // 释放Texture2D资源 } }
优化点说明:
- 用
while循环代替if,确保帧率过低导致累计时间超过多个间隔时,能一次性补全截图,避免漏帧 - 采用
timeSinceLastShot -= captureInterval代替重置为0,减少长期运行的时间误差 - 截图文件名增加毫秒标识(
fff),避免短时间内截图导致文件名重复被覆盖 - 增加
Destroy(screenShot)释放Texture2D资源,防止内存泄漏
方案二:使用协程实现更稳定的定时截图
协程可以更精准地控制时间间隔,不受帧率波动影响,是更推荐的方式:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SS : MonoBehaviour { public int resWidth = 640; public int resHeight = 480; public int fps = 15; private float captureInterval => 1f / fps; private void Start() { // 启动截图协程 StartCoroutine(CaptureScreenshotsCoroutine()); } public static string ScreenShotName(int width, int height) { return string.Format("{0}/screenshots/screen_{1}x{2}_{3}.png", Application.dataPath, width, height, System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-fff")); } private IEnumerator CaptureScreenshotsCoroutine() { while (true) { TakeHiResShot(); // 使用WaitForSecondsRealtime,不受游戏时间缩放(暂停、慢动作)影响 yield return new WaitForSecondsRealtime(captureInterval); } } private void TakeHiResShot() { RenderTexture rt = new RenderTexture(resWidth, resHeight, 24); Camera cam = GetComponent<Camera>(); cam.targetTexture = rt; Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false); cam.Render(); RenderTexture.active = rt; screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0); cam.targetTexture = null; RenderTexture.active = null; Destroy(rt); byte[] bytes = screenShot.EncodeToPNG(); string filename = ScreenShotName(resWidth, resHeight); System.IO.File.WriteAllBytes(filename, bytes); Debug.Log($"Took screenshot to: {filename}"); Destroy(screenShot); } }
协程方案的优势:
WaitForSecondsRealtime不受游戏时间缩放影响,间隔更精准- 逻辑更清晰,不需要在Update中累加时间,降低代码复杂度
- 稳定的定时执行,适合需要固定帧率截图的场景
额外注意事项:
- 确保
Application.dataPath下的screenshots文件夹存在,否则会报错。可以在脚本启动时自动创建:private void Awake() { string screenshotPath = $"{Application.dataPath}/screenshots"; if (!System.IO.Directory.Exists(screenshotPath)) { System.IO.Directory.CreateDirectory(screenshotPath); } } - 如果截图分辨率很高,频繁截图会占用大量磁盘空间和内存,建议根据需求调整分辨率或限制截图时长
内容的提问来源于stack exchange,提问作者Aditya Vartak
相关产品推荐
相关产品推荐

