I'm attempting to create a simple snake game in C# visual studio as an exercise. I have very little knowledge of the Windows Forms App, but it seemed like the easiest way to access drawing and user input with the arrows.
Is this a good framework to use for a simple game like this in C#?
But here is my real question. I'm trying to create the game loop which loops about every second, and changes where the head of the snake is going to be drawn next. But I cannot figure out how to call the Paint event multiple times. It seems like it calls once, and then exits. If I put a while() loop inside of the paint method with Thread.Sleep(), it draws a rectangle every second, but I don't have access to the KeyEventArgs or anything else because the running code is trapped inside the Paint call.
I want the initial paint call to draw the starting point for the snake, and then looping paint calls, I'm guessing to another paint method(?) which asks the controller which button was pressed last, and then draws the next rectangle in that direction. What is the intended way to do this and create that game loop?
I would appreciate any information and knowledge to help teach me this process, and any other advice or input is welcome!
Here is my current code: '''
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;
}
}
}
'''
Using Thread.Sleep() in this context is almost always a bad idea, as it will freeze your only thread, which is also your UI thread.
You probably want to make use of a timer.
Quick example:
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
}
If you want to force your control to be repainted, you can simply call Invalidate()
.