You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C#实现更快的游戏区域截图?

优化节奏游戏小区域截图性能的方案

一、修复BitBlt重复截图的问题

你遇到的BitBlt始终返回首次截图的情况,核心原因是游戏开启了硬件加速,导致窗口DC(设备上下文)无法实时同步最新画面。改用PrintWindow可以绕过这个缓存问题,同时保留BitBlt的高速特性:

[DllImport("user32.dll")]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

public Bitmap getBitmapAt(Point location, int width, int height)
{
    // 先捕获整个窗口客户区
    Bitmap fullWindowImg = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    using (Graphics gdest = Graphics.FromImage(fullWindowImg))
    {
        IntPtr hDC = gdest.GetHdc();
        // PW_CLIENTONLY 只捕获窗口客户区,排除边框
        PrintWindow(windowHandle, hDC, 0x00000001);
        gdest.ReleaseHdc(hDC);
    }

    // 裁剪到目标50x150区域
    Rectangle cropRect = new Rectangle(location.X, location.Y, width, height);
    Bitmap croppedImg = fullWindowImg.Clone(cropRect, fullWindowImg.PixelFormat);
    fullWindowImg.Dispose();
    return croppedImg;
}

二、高性能方案:Windows Graphics Capture(Win10+)

如果你的程序运行在Win10 1903及以上版本,推荐使用Windows Graphics Capture API——这是微软专为低延迟屏幕捕获设计的硬件加速接口,捕获延迟通常在1-5ms,完全满足你8ms以内的要求:

using Windows.Graphics.Capture;
using Windows.Graphics.DirectX;
using Windows.Graphics.DirectX.Direct3D11;
using System.Runtime.InteropServices;

// 全局捕获资源(仅初始化一次)
private GraphicsCaptureItem _captureItem;
private Direct3D11CaptureFramePool _framePool;
private GraphicsCaptureSession _session;
private Bitmap _latestCroppedFrame;

public void InitializeCapture(IntPtr windowHandle)
{
    // 从游戏窗口句柄创建捕获项
    _captureItem = GraphicsCaptureItem.CreateFromWindowId(new WindowId((ulong)windowHandle));
    
    // 创建Direct3D设备和帧池
    var d3dDevice = new Direct3D11Device();
    _framePool = Direct3D11CaptureFramePool.Create(
        d3dDevice,
        DirectXPixelFormat.B8G8R8A8UIntNormalized,
        2, // 帧池大小,避免丢帧
        _captureItem.Size);
    
    // 初始化捕获会话
    _session = _framePool.CreateCaptureSession(_captureItem);
    _session.IsCursorCaptureEnabled = false;
    
    // 注册帧到达事件,实时获取最新画面
    _framePool.FrameArrived += (sender, args) =>
    {
        using (var frame = _framePool.TryGetNextFrame())
        {
            var surface = frame.Surface;
            // 将Direct3D表面转换为Bitmap
            var fullBitmap = new Bitmap(surface.Description.Width, surface.Description.Height, PixelFormat.Format32bppArgb);
            var lockRect = new Rectangle(0, 0, fullBitmap.Width, fullBitmap.Height);
            var bits = fullBitmap.LockBits(lockRect, ImageLockMode.WriteOnly, fullBitmap.PixelFormat);
            
            surface.CopyToMemory(bits.Scan0, bits.Stride * fullBitmap.Height);
            fullBitmap.UnlockBits(bits);
            
            // 裁剪到目标50x150区域(替换成你的实际坐标)
            _latestCroppedFrame = fullBitmap.Clone(new Rectangle(100, 200, 50, 150), fullBitmap.PixelFormat);
            fullBitmap.Dispose();
        }
    };
    
    _session.StartCapture();
}

// 获取最新截图
public Bitmap GetLatestCapture()
{
    return _latestCroppedFrame?.Clone() as Bitmap;
}

三、优化现有Graphics.CopyFromScreen的性能

如果暂时不想更换API,通过以下优化可将耗时压缩到10ms以内:

  • 复用Bitmap对象,避免频繁创建/销毁的开销
  • 固定捕获区域坐标,跳过重复调用GetWindowRect
  • 使用CaptureBlt替代SourceCopy,针对屏幕捕获优化
// 全局复用的Bitmap,仅在区域变化时重新创建
private static Bitmap _reusableBitmap;

public static Bitmap CaptureImage(IntPtr handle, Rectangle targetCoords)
{
    // 初始化或更新复用的Bitmap
    if (_reusableBitmap == null || 
        _reusableBitmap.Width != targetCoords.Width || 
        _reusableBitmap.Height != targetCoords.Height)
    {
        _reusableBitmap?.Dispose();
        _reusableBitmap = new Bitmap(targetCoords.Width, targetCoords.Height, PixelFormat.Format32bppArgb);
    }
    
    using (Graphics g = Graphics.FromImage(_reusableBitmap))
    {
        g.CopyFromScreen(
            new Point(targetCoords.Left, targetCoords.Top), 
            Point.Empty, 
            targetCoords.Size, 
            CopyPixelOperation.CaptureBlt);
    }
    
    // 返回克隆对象,避免外部修改复用资源
    return _reusableBitmap.Clone(new Rectangle(0,0,targetCoords.Width,targetCoords.Height), _reusableBitmap.PixelFormat);
}

额外优化建议

  • 直接读取Bitmap内存:如果检测逻辑只需要像素颜色值,可通过LockBits直接访问内存,跳过格式转换
  • 异步捕获:将截图操作放到后台线程,避免阻塞检测逻辑
  • 关闭游戏VSync:若游戏开启垂直同步,画面更新频率会被限制在60Hz,可能影响截图实时性

内容的提问来源于stack exchange,提问作者Toll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:35:18