放个美女养眼

注意事项

  1. 只测试Canvas是ScreenSpace-Camera模式的情况
  2. 需要Unitask插件
  3. 单例自己维护下,会报错
  4. 会自动保存成jpg到特定目录

代码速览

public async UniTask<CaptureData> BeginCaptureByRect(RectTransform rtUI)
public async UniTask<CaptureData> BeginCapture()

ScreenCapture.Instance.BeginCapture();
ScreenCapture.Instance.BeginCaptureByRect();

核心代码

using System;
using System.IO;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using MiaoLeGeMi.Tool;
using UnityEngine;
using UnityEngine.UI;
using static System.Net.Mime.MediaTypeNames;
using static Unity.VisualScripting.Member;

namespace MiaoLeGeMi
{
    public class ScreenCapture : SingleMono<ScreenCapture>
    {
        [SerializeField]
        private CaptureData Data;


        [ContextMenu("测试截屏")]
        public async void Test()
        {
            CaptureData d = await BeginCapture();
            Debug.Log(d.SavePath);
        }

        /// <summary>
        /// 返回保存目录
        /// </summary>
        /// <param name="autoSave"></param>
        /// <returns></returns>
        public async UniTask<CaptureData> BeginCapture()
        {
            Debug.Log("开始截屏");
            await UniTask.WaitForEndOfFrame(this);

            int width = Screen.width;
            int height = Screen.height;
            Texture2D t2d = new Texture2D(width, height, TextureFormat.RGB24, false);
            Rect rect = new Rect(0, 0, width, height);
            t2d.ReadPixels(rect, 0, 0);
            t2d.Apply();
            Debug.Log("结束截屏");
            string dir = GetDownloadDirectory();
            string fileName = DateTime.Now.Ticks.ToString() + ".jpg";
            string fullpath = Path.Combine(dir, fileName);
            byte[] data = t2d.EncodeToJPG(80);
            File.WriteAllBytes(fullpath, data);
            Debug.Log($"写入文件:{fullpath}");
            CaptureData d = new CaptureData();
            d.SavePath = fullpath;
            d.t2d = t2d;
            await UniTask.CompletedTask;
            return d;
        }
 
        public async UniTask<CaptureData> BeginCaptureByRect(RectTransform rtUI)
        {

            // 等待帧结束,确保布局已更新
            await UniTask.WaitForEndOfFrame(this);

            Canvas canvas = rtUI.GetComponentInParent<Canvas>();
            if (canvas == null)
            {
                Debug.LogError("RectTransform 不在 Canvas 下!");
                return null;
            }

            // 获取 Canvas 的逻辑尺寸(即参考分辨率)
            RectTransform canvasRect = canvas.GetComponent<RectTransform>();
            Vector2 canvasSize = canvasRect.rect.size;
            Debug.Log($"Canvas 尺寸: {canvasSize.x} x {canvasSize.y}");

            // ---- 使用 anchoredPosition + sizeDelta 计算截取区域 ----
            Vector2 centerOffset = rtUI.anchoredPosition;   // 中心相对于父级(Canvas)中心的偏移
            Vector2 size = rtUI.sizeDelta;                  // UI 元素的宽高(锚点居中的情况下等于实际尺寸)

            // 左下角相对于 Canvas 中心的偏移
            float leftOffset = centerOffset.x - size.x * 0.5f;
            float bottomOffset = centerOffset.y - size.y * 0.5f;

            // 转换为以 Canvas 左下角为原点的像素坐标(与 RenderTexture 坐标一致)
            float x = leftOffset + canvasSize.x * 0.5f;
            float y = bottomOffset + canvasSize.y * 0.5f;
            float width = size.x;
            float height = size.y;

            // 边界裁剪(防止溢出)
            x = Mathf.Clamp(x, 0, canvasSize.x);
            y = Mathf.Clamp(y, 0, canvasSize.y);
            width = Mathf.Min(width, canvasSize.x - x);
            height = Mathf.Min(height, canvasSize.y - y);

            Debug.Log($"截取区域: x={x}, y={y}, w={width}, h={height}");

            // ---- 创建 RenderTexture 并渲染 Canvas ----
            int rtWidth = Mathf.RoundToInt(canvasSize.x);
            int rtHeight = Mathf.RoundToInt(canvasSize.y);
            RenderTexture rt = new RenderTexture(rtWidth, rtHeight, 16);
            Camera cam = canvas.worldCamera;
            if (cam == null)
            {
                Debug.LogError("Canvas 的 worldCamera 为空!");
                return null;
            }

            RenderTexture prevRT = cam.targetTexture;
            cam.targetTexture = rt;
            cam.Render();
            cam.targetTexture = prevRT;

            // ---- 从 RT 中截取指定区域 ----
            RenderTexture.active = rt;
            Texture2D t2d = new Texture2D(Mathf.RoundToInt(width), Mathf.RoundToInt(height), TextureFormat.RGB24, false);
            t2d.ReadPixels(new Rect(x, y, width, height), 0, 0);
            t2d.Apply();
            RenderTexture.active = null;

            // 释放 RenderTexture
            rt.Release();
            DestroyImmediate(rt);

            // ---- 保存截图 ----
            string dir = GetDownloadDirectory();
            string fileName = DateTime.Now.Ticks.ToString() + ".jpg";
            string fullpath = Path.Combine(dir, fileName);
            byte[] data = t2d.EncodeToJPG(80);
            File.WriteAllBytes(fullpath, data);
            Debug.Log($"写入文件:{fullpath}");

            CaptureData d = new CaptureData { SavePath = fullpath, t2d = t2d };
            await UniTask.CompletedTask;
            return d;
        }
 
        private static string GetDownloadDirectory()
        {
            // Application.dataPath 在编辑器下指向 Assets,打包后指向 Data 文件夹
            string rootDir = Directory.GetParent(UnityEngine.Application.dataPath).FullName; // 项目根目录 或 exe 所在目录
            string dirName = "UserCaches";
            string targetDir = Path.Combine(rootDir, dirName);
            if (!Directory.Exists(targetDir))
                Directory.CreateDirectory(targetDir);
            return targetDir;
        }
    }

    [Serializable]
    public class CaptureData
    {
        public string SavePath;
        public Texture2D t2d;
    }
}

 一键三联

点赞·留言·收藏

如果可以帮到你,好用的话,记得一键三连。

毕竟博主写文章是靠爱发电,所以爱会消失对不对。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐