简单快速的实时图形C#电脑游戏(WinForms)

本文关键字:电脑游戏 WinForms 图形 实时 简单 | 更新日期: 2023-09-27 18:06:14

作为C#winforms项目的一部分,我正在尝试使整数数组和位图协调工作,以实现快速PictureBox编辑(避免使用缓慢的SetPixel命令(。

我在表单上添加了一个按钮和一个图片框,一个点击事件和一个关闭事件

表单的代码现在是这样的:

public partial class Form1 : Form
{
    uint[] _Pixels { get; set; }
    Bitmap _Bitmap { get; set; }
    GCHandle _Handle { get; set; }
    IntPtr _Addr { get; set; }

    public Form1()
    {
        InitializeComponent();
        int imageWidth = 100; //1920;
        int imageHeight = 100; // 1080;
        PixelFormat fmt = PixelFormat.Format32bppRgb;
        int pixelFormatSize = Image.GetPixelFormatSize(fmt);
        int stride = imageWidth * pixelFormatSize;
        int padding = 32 - (stride % 32);
        if (padding < 32)
        {
            stride += padding;
        }
        _Pixels = new uint[(stride / 32) * imageHeight + 1];
         _Handle = GCHandle.Alloc(_Pixels, GCHandleType.Pinned);
        _Addr = Marshal.UnsafeAddrOfPinnedArrayElement(_Pixels, 0);
        _Bitmap = new Bitmap(imageWidth, imageHeight, stride / 8, fmt, _Addr);
        pictureBox1.Image = _Bitmap;
    }
    private void button1_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < _Pixels.Length; i++)
        {
            _Pixels[i] = ((uint)(255 | (255 << 8) | (255 << 16) | 0xff000000));
        }
    }
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _Addr = IntPtr.Zero;
        if (_Handle.IsAllocated)
        {
            _Handle.Free();
        }
        _Bitmap.Dispose();
        _Bitmap = null;
        _Pixels = null;
    }
}

运行时,开头会出现预期的黑色图像。

当点击按钮时,图像应该会变成白色,但我遗漏了一些东西。

我忘了做什么了?

简单快速的实时图形C#电脑游戏(WinForms)

WinForms控件不会"仅仅因为"重新绘制自己,它们必须有这样做的理由。

一些数据源能够告诉其容器它们已经更新。Bitmap类没有。它无法告诉包含的PictureBox(或用于显示它的任何控件(它已更新。当您调用SetPixel()来设置单个像素时,或者在操作BitmapData实例后调用UnlockBits()时,它可以,但当位图是使用固定数组构造的时,它不能,无论如何,您完全在位图类的控制之外操作该固定数组。

因此,Bitmap类没有事件或其他方式来通知其容器更新。

这意味着您需要告诉包含控件其数据源已更新,以便控件可以重新绘制自己。

您可以按照"如何刷新PictureBox"中的说明进行操作,即使用pictureBox.Refresh()。这会导致PictureBox控件自身失效,并在下一次(立即(重新绘制时重新读取现在更改的位图数据。

另请参阅MSDN博客:Control.Invalidate、Control.Update和Control.Refresh之间有什么区别?。

解决方案

更新_Pixels数组后,添加pictureBox1.Refresh()

这种更新速度很快,能够以高分辨率渲染平滑的视频。

添加对System.Runtime.InteropServices(可通过nuget获得(的引用

表单的代码现在是这样的:

public partial class Form1 : Form
{
    uint[] _Pixels { get; set; }
    Bitmap _Bitmap { get; set; }
    GCHandle _Handle { get; set; }
    IntPtr _Addr { get; set; }

    public Form1()
    {
        InitializeComponent();
        int imageWidth = 100; //1920;
        int imageHeight = 100; // 1080;
        PixelFormat fmt = PixelFormat.Format32bppRgb;
        int pixelFormatSize = Image.GetPixelFormatSize(fmt);
        int stride = imageWidth * pixelFormatSize;
        int padding = 32 - (stride % 32);
        if (padding < 32)
        {
            stride += padding;
        }
        _Pixels = new uint[(stride / 32) * imageHeight + 1];
         _Handle = GCHandle.Alloc(_Pixels, GCHandleType.Pinned);
        _Addr = Marshal.UnsafeAddrOfPinnedArrayElement(_Pixels, 0);
        _Bitmap = new Bitmap(imageWidth, imageHeight, stride / 8, fmt, _Addr);
        pictureBox1.Image = _Bitmap;
    }
    private void button1_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < _Pixels.Length; i++)
        {
            _Pixels[i] = ((uint)(255 | (255 << 8) | (255 << 16) | 0xff000000));
        }
        pictureBox1.Refresh();
    }
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _Addr = IntPtr.Zero;
        if (_Handle.IsAllocated)
        {
            _Handle.Free();
        }
        _Bitmap.Dispose();
        _Bitmap = null;
        _Pixels = null;
    }
}

测试

当然,我想测试这个方法的性能,看看它是否足够快,可以渲染平滑的视频。

将按钮点击替换为计时器勾选

到目前为止,我的测试一开始很简单。我用每毫秒触发一次的timer tick event替换了button click event,并用随机颜色值填充_Pixels数组。

为了确保PictureBox在当前刷新完成之前不会尝试刷新,我使用了一个bool IsRefreshing变量。

每秒帧数计算

我还通过增加单个int HzCount变量,并在每次PictureBox刷新开始时将该变量重置为零,来测量每次刷新之间的时间。就在我重置HzCount*之前,我在TextBox中显示该值。

英特尔(R(酷睿(TM(i7-5600U CPU@2.60GHz 2.60GHz,16.0 GB RAM结果:

72Hz@2560x1440

*赫兹或赫兹是指每秒经过的帧数(或某事物的次数(。从广义上讲,这意味着兆赫转换为每秒数百万次,千兆赫兹转换为每秒千万次(美国数十亿次(。