如何在 Windows 窗体中循环 paint 事件以创建简单的游戏循环?

How do I loop the paint event in Windows Forms to create a simple game loop?

提问人:duffin01 提问时间:10/11/2022 更新时间:10/13/2022 访问量:292

问:

我正在尝试在 C# Visual Studio 中创建一个简单的贪吃蛇游戏作为练习。我对 Windows 窗体应用程序知之甚少,但它似乎是使用箭头访问绘图和用户输入的最简单方法。

这是否是一个很好的框架,可以用于 C# 中这样的简单游戏?

但这是我真正的问题。我正在尝试创建游戏循环,它大约每秒循环一次,并改变接下来要画蛇头的位置。但是我无法弄清楚如何多次调用 Paint 事件。它似乎调用了一次,然后退出。如果我使用 Thread.Sleep() 在 paint 方法中放置一个 while() 循环,它会每秒绘制一个矩形,但我无法访问 KeyEventArgs 或其他任何东西,因为正在运行的代码被困在 Paint 调用中。

我希望最初的绘制调用绘制蛇的起点,然后循环绘制调用,我猜测另一种绘制方法(?),它询问控制器最后按下了哪个按钮,然后在该方向上绘制下一个矩形。执行此操作并创建该游戏循环的预期方法是什么?

我将不胜感激任何信息和知识来帮助我学习这个过程,并欢迎任何其他建议或意见!

这是我当前的代码: '''

public partial class FormMain : Form
    {
        private readonly int pixelSize;
        private readonly int gridSize;
        private readonly int center;
        private string currDirection;

        

    public FormMain()
    {
        pixelSize = 30;
        currDirection = "right";

        // Calculating size of the grid based on the size of the 'pixel'
        gridSize = 640 / pixelSize;

        // Calculating the starting position of the snake in the center of the screen
        center = (gridSize / 2) * pixelSize;

        InitializeComponent();
    }

    private void FormMain_Paint(object sender, PaintEventArgs e)
    {
        // referencing the Graphics object and setting the color of the snake
        Graphics g = e.Graphics;
        Pen seaPen = new Pen(Color.MediumSeaGreen);


        // This draws the initial center rectangle
        Point currentHeadLocation = new Point(center, center);

        Rectangle r;
        // Here's the loop that should happen every second or so, probably in another method.
        while (true)
        {
            r = new Rectangle(currentHeadLocation, new Size(pixelSize, pixelSize));
            g.DrawRectangle(seaPen, r);

            if (currDirection.Equals("right"))
            {
                currentHeadLocation = new Point(currentHeadLocation.X + pixelSize, currentHeadLocation.Y);
            }

            else if (currDirection.Equals("left"))
            {
                currentHeadLocation = new Point(currentHeadLocation.X - pixelSize, currentHeadLocation.Y);
            }

            else if (currDirection.Equals("up"))
            {
                currentHeadLocation = new Point(currentHeadLocation.X, currentHeadLocation.Y - pixelSize);
            }

            else if (currDirection.Equals("down"))
            {
                currentHeadLocation = new Point(currentHeadLocation.X, currentHeadLocation.Y + pixelSize);
            }

            Thread.Sleep(1000);
        }
            
    }

    private void FormMain_KeyDown(object sender, KeyEventArgs e)
    {
        switch(e.KeyCode)
        {
            case Keys.Left:
                currDirection = "left";
                break;
            case Keys.Right:
                currDirection = "right";
                break;
            case Keys.Up:
                currDirection = "up";
                break;
            case Keys.Down:
                currDirection = "down";
                break;
        }

    }
}

'''

C# WinForms 事件 游戏循环

评论

0赞 Rivo R. 10/11/2022
观看此视频以了解它是如何完成的:youtube.com/watch?v=i6W-aGhlq7M
0赞 Rivo R. 10/11/2022
而这篇文章也针对 Github 上的代码 mooict.com/...

答:

0赞 TheJP_ 10/13/2022 #1

在这种情况下使用 Thread.Sleep() 几乎总是一个坏主意,因为它会冻结你唯一的线程,这也是你的 UI 线程。

您可能想使用计时器

简单示例:


public FormMain()
{
    //Any other init stuff here

    System.Windows.Forms.Timer t = new System.Windows.Forms.Timer(); //Create a timer
    t.interval = 1000; //interval time in ms
    t.Tick += (s, e) => LoopFunctionName(); //Bind a function to the event whenever the timer reaches its interval
}
public void LoopFunctionName()
{
    //Game loop 
}

如果要强制重新绘制控件,只需调用 .Invalidate()