• entries
3
0
• views
341

See how easy it is to change from console to GDI when the code is reasonably well designed.

I've takent the console snake game I blogged previously, and changed it to use GDI on WinForms. 95% of the code is identical.

## Bit of a cleanup

using System; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace GdiSnake { public sealed partial class Form1 : Form { // our unchanging values: // playfield height & width const int PlayfieldWidth = 80; const int PlayfieldHeight = 40; const int CellSize = 9; const int PlayfieldYOffset = 50; const int PlayfieldXOffset = 20; Color[] CellColors = { Color.White, Color.Blue, Color.CornflowerBlue, Color.Coral, Color.Black }; // timeout to adjust speed of snake const int MillisecondsTimeout = 40; // our playfield; stores FieldVals instead of ints so we don't have to remember them readonly FieldVals[,] PlayField = new FieldVals[PlayfieldWidth, PlayfieldHeight]; // not yet used until we increase length of snake int _snakeBodyLen; // not including head // which direction (SnakdDirs enum) the snake is currently moving SnakeDirs _snakeDir; // position of the one-and-only piece of food; use our own coordinate class, Pos readonly Pos FoodPos = new Pos(0, 0); // defines the snake; each element tells us which coordinates each snake piece is at static int _maxSnakeLen = 31; readonly Pos[] _snakeCells = new Pos[_maxSnakeLen]; // guess int _score = 0; // for randomizing things like food placement Random _rnd; // how many body pieces the snake will increase by when it eats food int SnakeSizeIncrease = 2; // could've used something existing, but made a simple screen coordinate class public class Pos { public int X { get; set; } public int Y { get; set; } public Pos(int x, int y) { X = x; Y = y; } } // these make it easy (for the human) to know what each cell contains public enum FieldVals { Empty, SnakeHead, SnakeBody, SnakeFood, Border } // these make it easy (for the human) to read snake the direction public enum SnakeDirs { Up, Right, Down, Left } public Form1() { DoubleBuffered = true; InitializeComponent(); RunGame(); } public void Timer1_Tick(object sender, EventArgs e) { CheckForKeyboardCommand(); CheckForSnakeOutOfBounds(); CheckForSnakeCollisionWithSelf(); UpdateSnakeBodyPosition(); CheckSnakeHasEatenFood(); Invalidate(); } public void CheckForSnakeCollisionWithSelf() { if (_snakeCells.Skip(1).Any(pos => pos.X == _snakeCells.First().X && pos.Y == _snakeCells.First().Y)) { EndGame(false); } } /// <summary> /// Work out the initial coordinates of the snake's body parts /// </summary> public void SetUpSnake() { _snakeBodyLen = 4; // create the empty snake array cells for (var i = 0; i < _snakeCells.Length; i++) { _snakeCells[i] = new Pos(0, 0); } // randomly choose snake's initial direction _snakeDir = (SnakeDirs)_rnd.Next((int)SnakeDirs.Up, (int)SnakeDirs.Left + 1); // First set the position of the snake's head. // We'll work out the rest of the snake body coords based on which // direction it's initially facing. _snakeCells.First().X = PlayfieldWidth / 2; _snakeCells.First().Y = PlayfieldHeight / 2; switch (_snakeDir) { case SnakeDirs.Up: // make the snake's body go below the head, as it's moving up for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X; _snakeCells[i].Y = _snakeCells[i - 1].Y + 1; } break; case SnakeDirs.Right: // make the snake's body go left of the head, as it's moving right for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X - 1; _snakeCells[i].Y = _snakeCells.First().Y; } break; case SnakeDirs.Down: // make the snake's body go above of the head, as it's moving down for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X; _snakeCells[i].Y = _snakeCells[i - 1].Y - 1; } break; case SnakeDirs.Left: // make the snake's body go right of the head, as it's moving left for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X + 1; _snakeCells[i].Y = _snakeCells.First().Y; } break; } } /// <summary> /// Check the keyboard for arrow keys /// I got the code off the net (see bottom of code); no point re-creating this /// </summary> public void CheckForKeyboardCommand() { if (NativeKeyboard.IsKeyDown(KeyCode.Down)) // player hit Down arrow { // can't hit down while going up; game over if (_snakeDir == SnakeDirs.Up) EndGame(false); // change snake direction to down _snakeDir = SnakeDirs.Down; } else if (NativeKeyboard.IsKeyDown(KeyCode.Up)) { // can't hit up while going down; game over if (_snakeDir == SnakeDirs.Down) EndGame(false); // change snake direction to up _snakeDir = SnakeDirs.Up; } else if (NativeKeyboard.IsKeyDown(KeyCode.Left)) { // can't hit left while going right; game over if (_snakeDir == SnakeDirs.Right) EndGame(false); // change snake direction to left _snakeDir = SnakeDirs.Left; } else if (NativeKeyboard.IsKeyDown(KeyCode.Right)) { // can't hit right while going left; game over if (_snakeDir == SnakeDirs.Left) EndGame(false); // change snake direction to right _snakeDir = SnakeDirs.Right; } } /// <summary> /// See if snake has eaten the food /// </summary> public void CheckSnakeHasEatenFood() { // if snake head is in the same x,y position as the food // NB: First() is a Linq function; it gives me the first element in the array if (_snakeCells.First().X == FoodPos.X && _snakeCells.First().Y == FoodPos.Y) { IncrementScore(); MakeNewFood(); IncreaseSnakeSize(); } } public void IncreaseSnakeSize() { if (_snakeBodyLen + SnakeSizeIncrease < _maxSnakeLen) { _snakeBodyLen += SnakeSizeIncrease; } } public void DrawScore(Graphics g) { WriteAt(g, $"Score: {_score} Snake Size: {_snakeBodyLen}", 0, 0); } public void IncrementScore() { ++_score; } /// <summary> /// Put food item at random location /// </summary> public void MakeNewFood() { int x, y; do { // this ensures we're not putting the food on top of the snake, or the border x = _rnd.Next(1, PlayfieldWidth - 1); y = _rnd.Next(1, PlayfieldHeight - 1); } while (_snakeCells.Any(pos => pos.X == x || pos.Y == y)); // set the food coords FoodPos.X = x; FoodPos.Y = y; // update the playfield position with the food value PlayField[FoodPos.X, FoodPos.Y] = FieldVals.SnakeFood; } void CheckForSnakeOutOfBounds() { // snake mustn't be on any border cell, or game over if (_snakeCells.First().Y < 1 || _snakeCells.First().X > PlayfieldWidth - 2 || _snakeCells.First().Y > PlayfieldHeight - 2 || _snakeCells.First().X < 1) { EndGame(false); } } /// <summary> /// Move the snake pieces appropriately. I just did the simplest thing that I thought of. /// </summary> void UpdateSnakeBodyPosition() { // Last piece of snake's tail will always become empty as the snake moves // NB: Last() is a Linq function; it gives me the last element in the array (end of snake tail) PlayField[_snakeCells[_snakeBodyLen].X, _snakeCells[_snakeBodyLen].Y] = FieldVals.Empty; // move the 'middle' section of the snake one cell along for (int i = _snakeCells.Length - 1; i > 0; i--) { _snakeCells[i].X = _snakeCells[i - 1].X; _snakeCells[i].Y = _snakeCells[i - 1].Y; } // move the snake's head, depending on direction moving // the body was already moved above switch (_snakeDir) { case SnakeDirs.Up: // moved the snake head up 1 (-ve Y direction) --_snakeCells.First().Y; break; case SnakeDirs.Right: // moved the snake head right 1 (+ve X direction) ++_snakeCells.First().X; break; case SnakeDirs.Down: // moved the snake head up 1 (+ve Y direction) ++_snakeCells.First().Y; break; case SnakeDirs.Left: // moved the snake head left 1 (-ve X direction) --_snakeCells.First().X; break; } // Set the playfield position at the head of the snake, to be... the snake head! PlayField[_snakeCells.First().X, _snakeCells.First().Y] = FieldVals.SnakeHead; // Set the positions on the playfield for the snake body cells // so we know to draw them // NB: Skip(1).Take(4) is Linq; it gives me the array left after // skipping the first item, then grabbing the next 4 (so in this // case misses the first and last). foreach (var cell in _snakeCells.Skip(1).Take(4)) { PlayField[cell.X, cell.Y] = FieldVals.SnakeBody; } } /// <summary> /// Just show a message and exit (can only lose right now) /// </summary> /// <param name="win"></param> void EndGame(bool win) { //var g = CreateGraphics(); //WriteAt(g,$"YOU DIED. Score: {_score} Snake Length: {_snakeBodyLen}", 0, 20); RunGame(); } public void RunGame() { _rnd = new Random(); _score = 0; for (var i = 0; i < PlayfieldWidth; i++) { for (var j = 0; j < PlayfieldHeight; j++) { PlayField[i, j] = FieldVals.Empty; } } // create the initial snake cell coords (place it on playfield) SetUpSnake(); // start with an initial piece of food MakeNewFood(); timer1.Interval = MillisecondsTimeout; timer1.Start(); } /// <summary> /// Set the console size appropriately & draw the border, leaving room for the score /// </summary> void DrawBorder( Graphics g) { for (var i = 0; i < PlayfieldWidth; i++) { DrawCell(g, FieldVals.Border, i, 0); DrawCell(g, FieldVals.Border, i, PlayfieldHeight); } for (var i = 0; i < PlayfieldHeight; i++) { DrawCell(g, FieldVals.Border, 0, i); DrawCell(g, FieldVals.Border, PlayfieldWidth - 1, i); } } /// <summary> /// Go through every element of the 2d array, only drawing a cell /// if it has a value (other than 0). This way we only draw the /// cells that need to be updated. A bit like Invalidate() in GDO. /// Pretty self-explanatory; if a cell has a value, draw the character /// appropriate for it. The space is only used to overwrite the last /// piece of the snake's tail. /// </summary> void UpdatePlayfield( Graphics g) { DrawBorder( g); for (var i = 1; i < PlayfieldWidth - 1; i++) { for (var j = 1; j < PlayfieldHeight - 1; j++) { switch (PlayField[i, j]) { case FieldVals.SnakeHead: DrawCell( g, FieldVals.SnakeHead, i, j + 1); break; case FieldVals.SnakeBody: DrawCell(g, FieldVals.SnakeBody, i, j + 1); break; case FieldVals.SnakeFood: DrawCell(g, FieldVals.SnakeFood, i, j + 1); break; } } } } public void DrawCell( Graphics g ,FieldVals cellType, int x, int y) { if (cellType != FieldVals.Empty) { Color color = CellColors[(int)cellType]; g.FillRectangle(new SolidBrush(color), x * CellSize + PlayfieldXOffset, y * CellSize + PlayfieldYOffset, CellSize, CellSize); } } private void WriteAt( Graphics g, string s, int x, int y) { using (var drawFont = new Font("Arial", 16)) using (var drawBrush = new SolidBrush(System.Drawing.Color.Black)) { g.DrawString(s, drawFont, drawBrush, x, y); } } private void Form1_Paint(object sender, PaintEventArgs e) { UpdatePlayfield( e.Graphics); DrawScore( e.Graphics); } } /// <summary> /// Codes representing keyboard keys. /// </summary> /// <remarks> /// Key code documentation: /// http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx /// </remarks> internal enum KeyCode { Left = 0x25, Up, Right, Down } /// <summary> /// Provides keyboard access. /// </summary> internal static class NativeKeyboard { /// <summary> /// A positional bit flag indicating the part of a key state denoting /// key pressed. /// </summary> const int KeyPressed = 0x8000; /// <summary> /// Returns a value indicating if a given key is pressed. /// </summary> /// <param name="key">The key to check.</param> /// <returns> /// <c>true</c> if the key is pressed, otherwise <c>false</c>. /// </returns> public static bool IsKeyDown(KeyCode key) { return (GetKeyState((int)key) & KeyPressed) != 0; } /// <summary> /// Gets the key state of a key. /// </summary> /// <param name="key">Virtual-key code for key.</param> /// <returns>The state of the key.</returns> [System.Runtime.InteropServices.DllImport("user32.dll")] static extern short GetKeyState(int key); } }

## Changes I made...

So essentially the changes I made are: Create a WinForms app instead of Console (obviously) Move my game loop code from a for loop to a timer Tick Move UpdatePlayfield() into the form Paint() Change WriteAt (which wrote on the console) to DrawCell (to draw on the form) That's pretty much it! Apart from some minor stuff which I'll go into next post. Some of the code can still be removed, e.g. there's no need to 'erase' the last snake tail piece as in the console version, and we could use different keyboard technique, but it works BTW I'm not saying this is a great design, it's quick and dirty to be honest, which was the original intention (to get it done fast, but not throwing good design right out the window..)

## All done

using System; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace GdiSnake { public sealed partial class Form1 : Form { // our unchanging values: // playfield height & width const int PlayfieldWidth = 80; const int PlayfieldHeight = 40; const int CellSize = 9; const int PlayfieldYOffset = 50; const int PlayfieldXOffset = 20; // game pieces // no longer required //const string EmptyCell = " "; //const string SnakeHeadCell = "@"; //const string SnakeBodyCell = "o"; //const string FoodCell = "."; // timeout to adjust speed of snake const int MillisecondsTimeout = 30; // our playfield; stores FieldVals instead of ints so we don't have to remember them readonly FieldVals[,] PlayField = new FieldVals[PlayfieldWidth, PlayfieldHeight]; // not yet used until we increase length of snake int _snakeBodyLen; // not including head // which direction (SnakdDirs enum) the snake is currently moving SnakeDirs _snakeDir; // position of the one-and-only piece of food; use our own coordinate class, Pos readonly Pos FoodPos = new Pos(0, 0); readonly Pos EraserPos = new Pos(0, 0); // defines the snake; each element tells us which coordinates each snake piece is at static int _maxSnakeLen = 31; Pos[] _snakeCells = new Pos[_maxSnakeLen]; // guess int _score = 0; // for randomizing things like food placement Random _rnd; // how many body pieces the snake will increase by when it eats food int SnakeSizeIncrease = 2; // could've used something existing, but made a simple screen coordinate class public class Pos { public int X { get; set; } public int Y { get; set; } public Pos(int x, int y) { X = x; Y = y; } } // these make it easy (for the human) to know what each cell contains public enum FieldVals { DontDraw, Empty, SnakeHead, SnakeBody, SnakeFood, Border } // these make it easy (for the human) to read snake the direction public enum SnakeDirs { Up, Right, Down, Left } public Form1() { DoubleBuffered = true; InitializeComponent(); RunGame(); } public void Timer1_Tick(object sender, EventArgs e) { CheckForKeyboardCommand(); // using a timer now //AdjustGameSpeed(); // done in paint() now //UpdatePlayfield(); CheckForSnakeOutOfBounds(); CheckForSnakeCollisionWithSelf(); UpdateSnakeBodyPosition(); CheckSnakeHasEatenFood(); Invalidate(); } public void CheckForSnakeCollisionWithSelf() { if (_snakeCells.Skip(1).Any(pos => pos.X == _snakeCells.First().X && pos.Y == _snakeCells.First().Y)) { EndGame(false); } } /// <summary> /// Work out the initial coordinates of the snake's body parts /// </summary> public void SetUpSnake() { _snakeBodyLen = 4; // create the empty snake array cells for (var i = 0; i < _snakeCells.Length; i++) { _snakeCells[i] = new Pos(0, 0); } // randomly choose snake's initial direction _snakeDir = (SnakeDirs)_rnd.Next((int)SnakeDirs.Up, (int)SnakeDirs.Left + 1); // have simplified the snake placement //int[] xOffsets = { 0, _snakeBodyLen * -1, 0, _snakeBodyLen }; //int[] yOffsets = { _snakeBodyLen, 0, _snakeBodyLen * -1, 0 }; //int xOffset = xOffsets[(int)_snakeDir]; //int yOffset = yOffsets[(int)_snakeDir]; // First set the position of the snake's head. // We'll work out the rest of the snake body coords based on which // direction it's initially facing. _snakeCells.First().X = PlayfieldWidth / 2; _snakeCells.First().Y = PlayfieldHeight / 2; switch (_snakeDir) { case SnakeDirs.Up: // make the snake's body go below the head, as it's moving up for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X; _snakeCells[i].Y = _snakeCells[i - 1].Y + 1; } break; case SnakeDirs.Right: // make the snake's body go left of the head, as it's moving right for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X - 1; _snakeCells[i].Y = _snakeCells.First().Y; } break; case SnakeDirs.Down: // make the snake's body go above of the head, as it's moving down for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X; _snakeCells[i].Y = _snakeCells[i - 1].Y - 1; } break; case SnakeDirs.Left: // make the snake's body go right of the head, as it's moving left for (int i = 1; i < _snakeBodyLen; i++) { _snakeCells[i].X = _snakeCells.First().X + 1; _snakeCells[i].Y = _snakeCells.First().Y; } break; } } // not required for GDI version //public void AdjustGameSpeed() //{ // // delay so the game isn't too fast. Halve the delay (to go faster) when going left or right // // as it appears that going up/down is faster // Task.Delay(_snakeDir == SnakeDirs.Up || _snakeDir == SnakeDirs.Right ? MillisecondsTimeout / 2 : MillisecondsTimeout).Wait(); //} /// <summary> /// Check the keyboard for arrow keys /// I got the code off the net (see bottom of code); no point re-creating this /// </summary> public void CheckForKeyboardCommand() { if (NativeKeyboard.IsKeyDown(KeyCode.Down)) // player hit Down arrow { // can't hit down while going up; game over if (_snakeDir == SnakeDirs.Up) EndGame(false); // change snake direction to down _snakeDir = SnakeDirs.Down; } else if (NativeKeyboard.IsKeyDown(KeyCode.Up)) { // can't hit up while going down; game over if (_snakeDir == SnakeDirs.Down) EndGame(false); // change snake direction to up _snakeDir = SnakeDirs.Up; } else if (NativeKeyboard.IsKeyDown(KeyCode.Left)) { // can't hit left while going right; game over if (_snakeDir == SnakeDirs.Right) EndGame(false); // change snake direction to left _snakeDir = SnakeDirs.Left; } else if (NativeKeyboard.IsKeyDown(KeyCode.Right)) { // can't hit right while going left; game over if (_snakeDir == SnakeDirs.Left) EndGame(false); // change snake direction to right _snakeDir = SnakeDirs.Right; } } /// <summary> /// See if snake has eaten the food /// </summary> public void CheckSnakeHasEatenFood() { // if snake head is in the same x,y position as the food // NB: First() is a Linq function; it gives me the first element in the array if (_snakeCells.First().X == FoodPos.X && _snakeCells.First().Y == FoodPos.Y) { IncrementScore(); MakeNewFood(); IncreaseSnakeSize(); } } public void IncreaseSnakeSize() { if (_snakeBodyLen + SnakeSizeIncrease < _maxSnakeLen) { _snakeBodyLen += SnakeSizeIncrease; } } public void DrawScore(Graphics g) { WriteAt(g, $"Score: {_score} Snake Size: {_snakeBodyLen}", 0, 0); } public void IncrementScore() { ++_score; } /// <summary> /// Put food item at random location /// </summary> public void MakeNewFood() { int x, y; do { // this ensures we're not putting the food on top of the snake, or the border x = _rnd.Next(1, PlayfieldWidth - 1); y = _rnd.Next(1, PlayfieldHeight - 1); } while (_snakeCells.Any(pos => pos.X == x || pos.Y == y)); // set the food coords FoodPos.X = x; FoodPos.Y = y; // update the playfield position with the food value PlayField[FoodPos.X, FoodPos.Y] = FieldVals.SnakeFood; } void CheckForSnakeOutOfBounds() { // snake mustn't be on any border cell, or game over if (_snakeCells.First().Y < 1 || _snakeCells.First().X > PlayfieldWidth - 2 || _snakeCells.First().Y > PlayfieldHeight - 2 || _snakeCells.First().X < 1) { EndGame(false); } } /// <summary> /// Move the snake pieces appropriately. I just did the simplest thing that I thought of. /// </summary> void UpdateSnakeBodyPosition() { // remember the position of the snake's last piece so that later, // after drawing the snake, we can set it to the 'don't draw' value EraserPos.X = _snakeCells[_snakeBodyLen].X; EraserPos.Y = _snakeCells[_snakeBodyLen].Y; // Last piece of snake's tail will always become empty as the snake moves // NB: Last() is a Linq function; it gives me the last element in the array (end of snake tail) PlayField[_snakeCells[_snakeBodyLen].X, _snakeCells[_snakeBodyLen].Y] = FieldVals.Empty; // move the 'middle' section of the snake one cell along for (int i = _snakeCells.Length - 1; i > 0; i--) { _snakeCells[i].X = _snakeCells[i - 1].X; _snakeCells[i].Y = _snakeCells[i - 1].Y; } // move the snake's head, depending on direction moving // the body was already moved above switch (_snakeDir) { case SnakeDirs.Up: // moved the snake head up 1 (-ve Y direction) --_snakeCells.First().Y; break; case SnakeDirs.Right: // moved the snake head right 1 (+ve X direction) ++_snakeCells.First().X; break; case SnakeDirs.Down: // moved the snake head up 1 (+ve Y direction) ++_snakeCells.First().Y; break; case SnakeDirs.Left: // moved the snake head left 1 (-ve X direction) --_snakeCells.First().X; break; } // Set the playfield position at the head of the snake, to be... the snake head! PlayField[_snakeCells.First().X, _snakeCells.First().Y] = FieldVals.SnakeHead; // Set the positions on the playfield for the snake body cells // so we know to draw them // NB: Skip(1).Take(4) is Linq; it gives me the array left after // skipping the first item, then grabbing the next 4 (so in this // case misses the first and last). foreach (var cell in _snakeCells.Skip(1).Take(4)) { PlayField[cell.X, cell.Y] = FieldVals.SnakeBody; } } /// <summary> /// Just show a message and exit (can only lose right now) /// </summary> /// <param name="win"></param> void EndGame(bool win) { // Console.Clear(); // Console.WriteLine($"YOU DIED. Score: {_score} Snake Length: {_snakeBodyLen}"); // Console.ReadKey(); // Console.WriteLine("P to play again, Q to quit."); // var consoleKeyInfo = Console.ReadKey(); // if (consoleKeyInfo.Key == ConsoleKey.Q) // { // Environment.Exit(0); // } RunGame(); } public void RunGame() { _rnd = new Random(); //Console.Clear(); _score = 0; for (var i = 0; i < PlayfieldWidth; i++) { for (var j = 0; j < PlayfieldHeight; j++) { PlayField[i, j] = FieldVals.DontDraw; } } // create the initial snake cell coords (place it on playfield) SetUpSnake(); // start with an initial piece of food MakeNewFood(); timer1.Start(); // game loop; this was the easiest but might switch to Timer, etc. // function names should explain purpose // for (;/* ever */; ) // { // CheckForKeyboardCommand(); // AdjustGameSpeed(); // UpdatePlayfield(); // CheckForSnakeOutOfBounds(); // CheckForSnakeCollisionWithSelf(); // UpdateSnakeBodyPosition(); // CheckSnakeHasEatenFood(); // } } /// <summary> /// Set the console size appropriately & draw the border, leaving room for the score /// </summary> void DrawBorder( Graphics g) { for (var i = -1; i <= PlayfieldWidth; i++) { DrawCell(g, FieldVals.Border, i, -1); DrawCell(g, FieldVals.Border, i, PlayfieldHeight); } for (var i = -1; i <= PlayfieldHeight; i++) { DrawCell(g, FieldVals.Border, -1, i); DrawCell(g, FieldVals.Border, PlayfieldWidth, i); } } /// <summary> /// Go through every element of the 2d array, only drawing a cell /// if it has a value (other than 0). This way we only draw the /// cells that need to be updated. A bit like Invalidate() in GDO. /// Pretty self-explanatory; if a cell has a value, draw the character /// appropriate for it. The space is only used to overwrite the last /// piece of the snake's tail. /// </summary> void UpdatePlayfield( Graphics g) { DrawBorder( g); for (var i = 1; i < PlayfieldWidth - 1; i++) { for (var j = 1; j < PlayfieldHeight - 1; j++) { switch (PlayField[i, j]) { case FieldVals.Empty: DrawCell(g, FieldVals.Empty, i, j + 1); break; case FieldVals.SnakeHead: DrawCell( g, FieldVals.SnakeHead, i, j + 1); break; case FieldVals.SnakeBody: DrawCell(g, FieldVals.SnakeBody, i, j + 1); break; case FieldVals.SnakeFood: DrawCell(g, FieldVals.SnakeFood, i, j + 1); break; } } } PlayField[EraserPos.X, EraserPos.Y] = FieldVals.DontDraw; } public void DrawCell( Graphics g ,FieldVals cellType, int x, int y) { Color color = Color.Blue; switch (cellType) { case FieldVals.DontDraw: break; case FieldVals.Empty: break; case FieldVals.SnakeHead: color = Color.Blue; break; case FieldVals.SnakeBody: color = Color.DarkCyan; break; case FieldVals.SnakeFood: color = Color.Coral; break; case FieldVals.Border: color = Color.Black; break; } g.FillRectangle( new SolidBrush(color), x * CellSize + PlayfieldXOffset, y * CellSize + PlayfieldYOffset, CellSize, CellSize); } // From Microsoft sample private void WriteAt( Graphics g, string s, int x, int y) { using (var drawFont = new Font("Arial", 16)) using (var drawBrush = new SolidBrush(System.Drawing.Color.Black)) { g.DrawString(s, drawFont, drawBrush, x, y); } } private void Form1_Paint(object sender, PaintEventArgs e) { UpdatePlayfield( e.Graphics); DrawScore( e.Graphics); } } /// <summary> /// Codes representing keyboard keys. /// </summary> /// <remarks> /// Key code documentation: /// http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx /// </remarks> internal enum KeyCode { Left = 0x25, Up, Right, Down } /// <summary> /// Provides keyboard access. /// </summary> internal static class NativeKeyboard { /// <summary> /// A positional bit flag indicating the part of a key state denoting /// key pressed. /// </summary> const int KeyPressed = 0x8000; /// <summary> /// Returns a value indicating if a given key is pressed. /// </summary> /// <param name="key">The key to check.</param> /// <returns> /// <c>true</c> if the key is pressed, otherwise <c>false</c>. /// </returns> public static bool IsKeyDown(KeyCode key) { return (GetKeyState((int)key) & KeyPressed) != 0; } /// <summary> /// Gets the key state of a key. /// </summary> /// <param name="key">Virtual-key code for key.</param> /// <returns>The state of the key.</returns> [System.Runtime.InteropServices.DllImport("user32.dll")] static extern short GetKeyState(int key); } }

×

## Important Information

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!