原文: http://zetcode.com/gui/csharpwinforms/snake/

在 Mono Winforms 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。

贪食蛇游戏

贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。 该游戏有时称为 Nibbles 。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 通过按下光标键之一开始游戏。 如果游戏结束,我们将在棋盘中间显示"Game Over"消息。

Board.cs

  1. using System;
  2. using System.Collections;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Windows.Forms;
  7. public class Board : UserControl {
  8. private const int WIDTH = 300;
  9. private const int HEIGHT = 300;
  10. private const int DOT_SIZE = 10;
  11. private const int ALL_DOTS = 900;
  12. private const int RAND_POS = 27;
  13. private int[] x = new int[ALL_DOTS];
  14. private int[] y = new int[ALL_DOTS];
  15. private int dots;
  16. private int apple_x;
  17. private int apple_y;
  18. private bool left = false;
  19. private bool right = true;
  20. private bool up = false;
  21. private bool down = false;
  22. private bool inGame = true;
  23. private Timer timer;
  24. private Bitmap dot;
  25. private Bitmap apple;
  26. private Bitmap head;
  27. private IContainer components;
  28. public int BORDER_WIDTH;
  29. public int TITLEBAR_HEIGHT;
  30. public Board() {
  31. components = new Container();
  32. BackColor = Color.Black;
  33. DoubleBuffered = true;
  34. this.ClientSize = new Size(WIDTH, HEIGHT);
  35. try {
  36. dot = new Bitmap("dot.png");
  37. apple = new Bitmap("apple.png");
  38. head = new Bitmap("head.png");
  39. } catch (Exception e) {
  40. Console.WriteLine(e.Message);
  41. Environment.Exit(1);
  42. }
  43. initGame();
  44. }
  45. private void OnTick(object sender, EventArgs e) {
  46. if (inGame) {
  47. checkApple();
  48. checkCollision();
  49. move();
  50. }
  51. this.Refresh();
  52. }
  53. private void initGame() {
  54. dots = 3;
  55. for (int z = 0; z < dots; z++) {
  56. x[z] = 50 - z * 10;
  57. y[z] = 50;
  58. }
  59. locateApple();
  60. KeyUp += new KeyEventHandler(OnKeyUp);
  61. timer = new Timer(this.components);
  62. timer.Enabled = true;
  63. timer.Interval = 100;
  64. timer.Tick += new System.EventHandler(this.OnTick);
  65. Paint += new PaintEventHandler(this.OnPaint);
  66. }
  67. private void OnPaint(object sender, PaintEventArgs e) {
  68. Graphics g = e.Graphics;
  69. if (inGame) {
  70. g.DrawImage(apple, apple_x, apple_y);
  71. for (int z = 0; z < dots; z++) {
  72. if (z == 0) {
  73. g.DrawImage(head, x[z], y[z]);
  74. } else {
  75. g.DrawImage(dot, x[z], y[z]);
  76. }
  77. }
  78. } else {
  79. gameOver(g);
  80. }
  81. }
  82. private void gameOver(Graphics g) {
  83. String msg = "Game Over";
  84. StringFormat format = new StringFormat();
  85. format.Alignment = StringAlignment.Center;
  86. format.LineAlignment = StringAlignment.Center;
  87. g.DrawString(msg, Font, Brushes.White, ClientRectangle, format);
  88. timer.Stop();
  89. }
  90. private void checkApple() {
  91. if ((x[0] == apple_x) && (y[0] == apple_y)) {
  92. dots++;
  93. locateApple();
  94. }
  95. }
  96. private void move() {
  97. for (int z = dots; z > 0; z--) {
  98. x[z] = x[(z - 1)];
  99. y[z] = y[(z - 1)];
  100. }
  101. if (left) {
  102. x[0] -= DOT_SIZE;
  103. }
  104. if (right) {
  105. x[0] += DOT_SIZE;
  106. }
  107. if (up) {
  108. y[0] -= DOT_SIZE;
  109. }
  110. if (down) {
  111. y[0] += DOT_SIZE;
  112. }
  113. }
  114. private void checkCollision() {
  115. for (int z = dots; z > 0; z--) {
  116. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  117. inGame = false;
  118. }
  119. }
  120. if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
  121. inGame = false;
  122. }
  123. if (y[0] < 0) {
  124. inGame = false;
  125. }
  126. if (x[0] > WIDTH - DOT_SIZE - 2 * BORDER_WIDTH) {
  127. inGame = false;
  128. }
  129. if (x[0] < 0) {
  130. inGame = false;
  131. }
  132. }
  133. private void locateApple() {
  134. Random rand = new Random();
  135. int r = (int)(rand.Next(RAND_POS));
  136. apple_x = ((r * DOT_SIZE));
  137. r = (int)(rand.Next(RAND_POS));
  138. apple_y = ((r * DOT_SIZE));
  139. }
  140. private void OnKeyUp(object sender, KeyEventArgs e) {
  141. int key = (int) e.KeyCode;
  142. if ((key == (int) Keys.Left) && (!right)) {
  143. left = true;
  144. up = false;
  145. down = false;
  146. }
  147. if ((key == (int) Keys.Right) && (!left)) {
  148. right = true;
  149. up = false;
  150. down = false;
  151. }
  152. if ((key == (int) Keys.Up) && (!down)) {
  153. up = true;
  154. right = false;
  155. left = false;
  156. }
  157. if ((key == (int) Keys.Down) && (!up)) {
  158. down = true;
  159. right = false;
  160. left = false;
  161. }
  162. }
  163. }

首先,我们将定义游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。

  1. private int[] x = new int[ALL_DOTS];
  2. private int[] y = new int[ALL_DOTS];

这两个数组存储蛇的所有关节的 x,y 坐标。

move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。

  1. for (int z = dots; z > 0; z--) {
  2. x[z] = x[(z - 1)];
  3. y[z] = y[(z - 1)];
  4. }

该代码将关节向上移动。

  1. if (left) {
  2. x[0] -= DOT_SIZE;
  3. }

将头向左移动。

checkCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。

  1. for (int z = dots; z > 0; z--) {
  2. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  3. inGame = false;
  4. }
  5. }

如果蛇用头撞到关节之一,我们就结束游戏。

  1. if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
  2. inGame = false;
  3. }

如果蛇击中了棋盘的底部,我们就结束了游戏。

Snake.cs

  1. using System;
  2. using System.Drawing;
  3. using System.Windows.Forms;
  4. class Snake : Form {
  5. public Snake() {
  6. Text = "Snake";
  7. DoubleBuffered = true;
  8. FormBorderStyle = FormBorderStyle.FixedSingle;
  9. int borderWidth = (this.Width - this.ClientSize.Width) / 2;
  10. int titleBarHeight = this.Height - this.ClientSize.Height - borderWidth;
  11. Board board = new Board();
  12. board.BORDER_WIDTH = borderWidth;
  13. board.TITLEBAR_HEIGHT = titleBarHeight;
  14. Controls.Add(board);
  15. CenterToScreen();
  16. }
  17. }
  18. class MApplication {
  19. public static void Main() {
  20. Application.Run(new Snake());
  21. }
  22. }

这是主要的类。

Mono Winforms 中的贪食蛇 - 图1

图:贪食蛇

这是使用 Mono Winforms 库编程的贪食蛇游戏。