I'm not quite sure why, but I decided to make a tutorial on a Snake game using .NET 4.0. This tutorial might seem a bit lengthy and probably went too in depth in some areas and not enough in others. Some of the code in here might not be "optimized" persay but it works. Anyways, each tutorial is separated into different posts with a quick navigation in each post. I hope this is a one time exception to creating 8 posts in a row. Anyways enjoy!

"Snake" is probably one of the most simplest games to understand (along with many other classic arcade games). Which is why it's a great example to game programming. It requires all of the necessary requirements for a game.

Game LogicUpdate-Check Input-Update Player-Check Collision

Render-Draw Player-Draw Fruit-Draw Score

ConceptSnake is an easy game to understand. You start off as a "head" or a really, really small snake, roam around the playing field to collect food and you grow. You try to eat yourself, you die, if you run out of bounds, you die. Not much to it. So let's dig a bit deeper and understand how it works.

Each part of the snake can be considered an instance of an object, we'll call it "SnakePart". Each part follows the part in front of it. The snake and it's body, usually follow along a grid-based path. Each part is assigned an X and a Y coordinate, then gets rendered to that coordinate on screen. The food can be an instance of "SnakePart" rather then creating a new class. Set it's X and Y randomly to fit into the screen and then render it.

That's basically it for the concept of the snake game, can't really go much deeper with it.

We're going to be creating this game in .NET 4.0 C# with Visual Studio 2010. The rendering method of choice will be GDI+. GDI isn't really meant to be used to render games, but it'll work perfeclty for a lightweight game like this. Before moving on, I'm going to assume that you have a basic knowledge of C# and understand how to work with Visual Studio.

Creating the ProjectCreating the project is easy.File > New > Project...Be sure to select "Windows Forms Application" and then choose your desired Project Name and Solution Name.

Creating the FormCreating the form is easy. All we need are two controls. A PictureBox, for rendering the game, and a timer, for updating the game every X miliseconds. Go ahead and drag a PictureBox and a Timer onto your form. We'll be modifying some of it's properties, we'll also be modifying some of the Form properties. (Feel free to change any of these properties, these are just for personal preference and you can design yours to however you choose.)

Before we actually can start creating the game, we need to create a couple of classes. Go ahead and add these classes to your project.

SnakePart.csInput.cs

These are the only two main classes we'll need for the game itself. The food will be an instance of "SnakePart" as well as the snake itself. The Input class will be a static class that keeps track of key presses and key releases. We're just going to focus on SnakPart right now, we'll deal with Input a little later.

What we're going to do is create two getters/setters in our SnakePart class. This class only needs two variables, an X and Y coordinate.

Now we move onto the Input class. This won't be detecting Input right off the bat, we're just going to set it up so it can properly keep track of keys. WinForms basically runs off of events, so we'll be using the "Key Down" and "Key Up" events with our form. Once any of these events are fired, the key that was pressed/released will get sent to our Input manager which keeps track of every key.

Go ahead and create methods for the events "KeyUp" and "KeyDown" for your form. Here is all of the code we'll need to place in them.

Simple enough, right? Now Let's open up our Input class and create some code. First off, we'll need to create a variable to keep track of the keys. My personal preference would be to use a hashtable. Oh and since this is a static class, set the variable to a static so we don't have to initialize the class to use it.

private static Hashtable keys = new Hashtable();

We're going to need the following namespaces imported into this class.

using System.Collections; using System.Windows.Forms;

Now we create our ChangeState method, this also has to be static. This won't be returning any values. All this method will do is set the "key" in our hashtable to a boolean value. In this case, our "key" will be an actual keyboard key passed through our method.

Simple as that. Now we need one more method to return these values when as ask for a key state. Again, this will be a static method, the return value a boolean. First we have to check to see if the "key" exists in the hashtable, if it doesn't just return a false value. If it does exist, we'll return the value of "key". Remember, a hashtable is just a list of objects, so we have to cast the value to a boolean.

Now the fun part, we're going to finish creating our gameTimer, by adding proper events and setting the tick interval. Using the timer class, it will fire an event after every X miliseconds, this event will be our Update method.

FrameRateMost games run at 60 frames per second, that means the game will have updated and been rendered roughly 60 times every second. We're going to set the timer's interval to run at 60FPS. Since the timer's interval is in miliseconds, we'll take 1000 (miliseconds) / 60 (desired frame rate) and that'll be that. Go ahead and set this in our Form constructor.

Tick EventNow we need to create a "Tick" method. Go ahead and create the following method.

Now once we start up the application, the timer will call the update method. That's pretty much it for our game loop, other than the fact that we still need to create render methods. Throw this line in our "Update" method.

pbCanvas.Invalidate();

Every update, this will force our picture box to invalidate, which when that happens, the picture box will need to be re-rendered onto the form. Now onto rendering!

The Paint EventWe'll need to take advantage of the picture box's "Paint" event. This event will get fired when the control needs to be painted or re-painted. Go ahead and create a method for this event. Inside the method, we'll be grabbing the Graphics value from our event arguments. With an instance of the Graphics class, we'll be able to draw on the picture box itself.

And now we have a complete game loop. Let's go ahead and test it out shall we?

ExperimentingCreate two new variables in our Form class, "square_x" and "square_y". These will just be integers. Set them to 0 or whatever preferred number I suppose. In our Paint method, add this line.

Now let's go up to our Update method and test out our Input manager we created. We're going to check Input from the Arrow Keys and then change the values of square_x and square_y. Here is where we use our Input manager "Pressed" method.

This is the lengthy part of the tutorial. Here we will create all the necessary game logic for our Snake game. This includes :

4-way grid movement

Food generation/Snake growth

Collision and Gameover

Score

Setting up the SnakeRemember that SnakePart class? Well we're going to start using it now. What we're going to do is create a List of SnakeParts. This will be the easiest way to create a dynamic snake. Go ahead and create the follow variable.

List<SnakePart> snake = new List<SnakePart>();

This will be our list of parts. Now before we move on, I think it'd be best to explain the level size. The size of my picture box is 320x240, I'm groing to break this up into tiles since we're making this grid-based. Each tile with be 16x16 pixels, which leaves 20 tiles across, and 15 tiles down. In case you choose to change any of this later, we're going to create some constants as well.

const int tile_width = 16; const int tile_height = 16;

This way, in case you decided to go with a bigger picture box or you want larger tiles, you can change any of these values. We have a few more variables we need to create for the game.

Now we need to create a "StartGame" method. This method will reset all values and start the game. In this method, we'll also be creating the head of our snake. The following clears the list of snake parts, then creates the head. The head will appear in the center near the top of the screen. Starting in the down direction.

Notice the GenerateFood() line. We'll be creating that in a minute. Let's go ahead and add in the rendering for our snake. For our rendering, we'll be using a "for i in 0...size" iteration. Using this method, we can have our snake's head a different color from the rest of the body. Hop down to our picture box's Paint method.

This loops through every part, checks to see what "i" is and sets the color based on it. Then it draws a fill rectangle according to the current part's x and y with accordance to the tile size.

Generating FoodTime to generate some food! This method is extremely easy to create. What we're going to do here is make an instance of Random and have a number randomly generate to the canvas's bounds. Using our tile size constants and grabbing the size of the picture box, we'll be able to figure out our tile limit.

With some simple math, we figured out how many tiles there are vertically and horizontally. With this info, we create a new instance of SnakePart and assign it a random x and y in relevance to our bounds. Hop back into our Paint method. We have to make it draw our food now. Add this to the bottom of the method.

Feel free to change the Brush. Now all that's left is to Check Input, Update the Snake, Check for Collision, and Gameover. We're almost done.

Check InputThis part is pretty easy. Remember earlier when we tested out the Input Manager? Well we're basically going to be doing that. Except, we're going to add a couple of more "if" branches. These extra branches will prevent the snake from moving right when he's going left, and moving down when he's already going up, and vica versa.

Things to keep in mind: Point A) We're doing this so the player can't kill himself by accidentally pressing the opposite direction. Point B) We can't have this limitation when the size of our snake is only 1. How are we going to tackle this? Easy.

When moving right, we'll check to see if the size is less than 2 or if the head and the 2nd part are on the same X. If either of these conditions meet, we change directions.

We apply these conditions (and slightly modify them) to all other directions. In our Update method, we need to check for "gameover" before we start updating. Then we add our Input logic into the else branch.

Left and Right will check to make sure the head and second part's X match.Up and Down will check to make sure the head and second part's Y match.The above logic will be ignored it the snake's size is less than 2.

That's all we need for input (for now). Notice I added the line "UpdateSnake()", just to keep organized, we're going to update the snake in another method.

Moving the SnakeSince each part of the snake is it's own part, we have to loop through the list of snake parts and update each one. How are we going to update the snake exactly? We're going to be using this logic.

Loop through the snake starting from the back.Set the current part's X and Y to the part in front of it.Move the head in accordance to it's current direction.

Basically, we'll start from the back of the snake. We'll grab the part that's in front of the current part we're on and copy it's coordinates. In our for loop, we'll need to check to see if "i" is equal to 0 or not. In order to start from the back of our snake, we need to set "i" to the size of our snake, minus one, check if it's greater than or equal to zero, then minus one each loop.

Let's go ahead and do the body logic right now, this part is sweet and short.

snake[i].X = snake[i - 1].X; snake[i].Y = snake[i - 1].Y;

Now let's go and do the head logic. In the head logic, we'll need to do a bit more work.

Update head's position.Check for collision with the body.Check for collision outside of the level.Check for collision with the food object.[/li][/list]

For now, we're just going to update the snake's head. We'll worry about collision a bit later. We'll be using a switch statement in relation to our "direction" variable. This code goes in place of our head logic.

Revisiting the Frame RateNow we're almost ready to test this out, however we have to change the frames-per-second. Earlier we set it up so it'd run 60 frames per second. With a game like snake, we don't need it that high and we certainly don't want it. THe snake would move roughly 60 times every second. Let's try something along the lines of like 4 times every second. In our constructor, just change the gameTimer.Interval.

gameTimer.Interval = 1000 / 4;

One more thing, add "StartGame();" right under gameTimer.Start(), now run your project. You should have perfectly working snake movement.

CollisionCollision is easy. All collision checking will be done inside the head logic. First we'll check to see if the head is outside the screen. Then we'll do another for loop with the body and check to see if the head collided with it's body, then we'll check collision with the food.

Let's start with checking the bounds. We'll use the same simple math we used to generate food to grab the number of vertical and horizontal tiles. Then we'll check to see if the head's X and Y is less than zero or exceeds the number of tiles. Then set the gameover flag to true.

Alright, time to update with the body. Collision with the body is simple. We create another for loop that starts from "1" and climbs up through the snake's body. For each index, we check to see if the head's coordinates match any of the others.

Now food collision and snake growth. All we do here is check the head's X and Y with the food's X and Y. If collision occurs, we create another instance of "SnakePart". It's coordinates will be set to the last part in the snake's coordinates. Along with adding a new part, we also have to generate a new piece of food and add 1 to our score.

And that's it for collision. So what's left? Our DoGameover() method. Then starting a new game when the gameover occurs. Drawing the score in the top corner would be nice as well. Before we move on, feel free to try out what we have so far. You should have a completely working snake that grows as he eats food. Dying works properly as well. You'll notice the game freeze whenever you die.

GameoverAll we're doing here is trying the player's final score, text saying "Gameover" then some text saying "Press Enter to Start Over". Then of course we actually have to detect input. We have to add an "if else" branch in our Paint method as we forgot to do so earlier.

Now to center it in the screen horizontally, we need to measure the size of the string. The Graphics class comes with a nice MeasureString method. We'll also need to know the center of the screen so we take the width of our picture box and divide it by 2. We'll start off by measuring our gameover_msg and then drawing that.

That's it for drawing our gameover screen. Feel free to test it out. While we're here in the paint method, let's go ahead and draw the player's score while the game is playing. In the "else" branch of our Paint method, add this to the bottom.

I was expecting it to be in XNA but I guess that can be easily converted. Nevertheless it was a good tutorial.just one little change I made was to change direction in the KeyDown event instead so you don't need to hold down the key. Just thought it made it harder having to time every press to the game update tick.

Still though, a great tutorial that every new programmer should go through if they want to get into game programming.

Quote from: stripe103 on August 05, 2012, 04:24:42 pmI was expecting it to be in XNA but I guess that can be easily converted. Nevertheless it was a good tutorial.just one little change I made was to change direction in the KeyDown event instead so you don't need to hold down the key. Just thought it made it harder having to time every press to the game update tick.

Still though, a great tutorial that every new programmer should go through if they want to get into game programming.

Yeah this was an issue I ran into, but figured if anyone had experience at all they could fix it. The reason it does this is because it's only being updated 4 times every second. If you go with a traditional 60 FPS, you'd have to apply another timer to the snake so it wouldn't move 60 times a second. But yeah, I'm with you on that.

Very nice tut, G_G. ++I like that it is not in XNA, which is overkill for such a thing and simply adds dependencies. Not to mention that it teaches the same logic without the extra confusion of teaching basic XNA on top of it.

I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Quote from: ForeverZer0 on August 05, 2012, 06:22:43 pmVery nice tut, G_G. ++I like that it is not in XNA, which is overkill for such a thing and simply adds dependencies. Not to mention that it teaches the same logic without the extra confusion of teaching basic XNA on top of it.

Thanks. And agreed. With XNA we would have had to gone through texture loading and font loading and so much more just to get this result. GDI+ can work great for a lot of 2D games depending on how much you need/have going on.

Unfortunately, I never finished the series. I got extremely bored with Game Maker. It's a very powerful tool and it's pretty limitless, but there are so many restrictions with it as well. They could have chosen a better scripting language, one that supported object orientation. If I do decide to do some tutorials, I'm not sure what I'll do.

Game Maker back then, back before they started overcharging the fuck out of it, was good. It used to only be 25 dollars. You look on their site now, it's so fucking expensive, and it's the same damn engine as it was before. Just with improvements. Game Maker 8 and 7 users got screwed over when they released Studio. They pretty much dropped support for it and started overcharging for their software. I don't really like the path YoYoGanes has taken.

Ruby is object oriented. It would have been a pretty suitable language for Game Maker. And just because you can be careless with your code, doesn't mean it's not a great language. As for XNA, I could probably do some XNA tutorials. I might even do some Android SDK tutorials. I'd have to get back into Java though. Anyways, thanks for the ideas Apid, maybe I'll do some this weekend. :3

The Android SDK is only a set of supporting tools for developing and building apps and games for Android. It's not a game engine, keep that in mind. Just like installing Windows SDK (now called Windows Kit since Windows 8, it comes as integral part of Visual Studio 2012) is for development on Windows platforms. XNA is a bit more specific, it's a game development SDK (even though you can technically do any kind of 3D stuff in there), but still not a game engine.

If you want a good game engine, I can recommend Unity. I haven't tried it myself, but a colleague at work is using it on a regular basis and from this "second hand" experience it seems not only pretty good, it also incorporates the latest industry standards and technologies related to 3D, physics and other segments. Not only that, it is multi-platform, you can even run your games in a web browser through their Web Player.