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

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

贪食蛇游戏

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

开发

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

snake.py

  1. #!/usr/bin/python
  2. # ZetCode PyGTK tutorial
  3. #
  4. # This is a simple snake game
  5. # clone
  6. #
  7. # author: jan bodnar
  8. # website: zetcode.com
  9. # last edited: February 2009
  10. import sys
  11. import gtk
  12. import cairo
  13. import random
  14. import glib
  15. WIDTH = 300
  16. HEIGHT = 270
  17. DOT_SIZE = 10
  18. ALL_DOTS = WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE)
  19. RAND_POS = 26
  20. x = [0] * ALL_DOTS
  21. y = [0] * ALL_DOTS
  22. class Board(gtk.DrawingArea):
  23. def __init__(self):
  24. super(Board, self).__init__()
  25. self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0, 0))
  26. self.set_size_request(WIDTH, HEIGHT)
  27. self.connect("expose-event", self.expose)
  28. self.init_game()
  29. def on_timer(self):
  30. if self.inGame:
  31. self.check_apple()
  32. self.check_collision()
  33. self.move()
  34. self.queue_draw()
  35. return True
  36. else:
  37. return False
  38. def init_game(self):
  39. self.left = False
  40. self.right = True
  41. self.up = False
  42. self.down = False
  43. self.inGame = True
  44. self.dots = 3
  45. for i in range(self.dots):
  46. x[i] = 50 - i * 10
  47. y[i] = 50
  48. try:
  49. self.dot = cairo.ImageSurface.create_from_png("dot.png")
  50. self.head = cairo.ImageSurface.create_from_png("head.png")
  51. self.apple = cairo.ImageSurface.create_from_png("apple.png")
  52. except Exception, e:
  53. print e.message
  54. sys.exit(1)
  55. self.locate_apple()
  56. glib.timeout_add(100, self.on_timer)
  57. def expose(self, widget, event):
  58. cr = widget.window.cairo_create()
  59. if self.inGame:
  60. cr.set_source_rgb(0, 0, 0)
  61. cr.paint()
  62. cr.set_source_surface(self.apple, self.apple_x, self.apple_y)
  63. cr.paint()
  64. for z in range(self.dots):
  65. if (z == 0):
  66. cr.set_source_surface(self.head, x[z], y[z])
  67. cr.paint()
  68. else:
  69. cr.set_source_surface(self.dot, x[z], y[z])
  70. cr.paint()
  71. else:
  72. self.game_over(cr)
  73. def game_over(self, cr):
  74. w = self.allocation.width / 2
  75. h = self.allocation.height / 2
  76. (x, y, width, height, dx, dy) = cr.text_extents("Game Over")
  77. cr.set_source_rgb(65535, 65535, 65535)
  78. cr.move_to(w - width/2, h)
  79. cr.show_text("Game Over")
  80. self.inGame = False
  81. def check_apple(self):
  82. if x[0] == self.apple_x and y[0] == self.apple_y:
  83. self.dots = self.dots + 1
  84. self.locate_apple()
  85. def move(self):
  86. z = self.dots
  87. while z > 0:
  88. x[z] = x[(z - 1)]
  89. y[z] = y[(z - 1)]
  90. z = z - 1
  91. if self.left:
  92. x[0] -= DOT_SIZE
  93. if self.right:
  94. x[0] += DOT_SIZE
  95. if self.up:
  96. y[0] -= DOT_SIZE
  97. if self.down:
  98. y[0] += DOT_SIZE
  99. def check_collision(self):
  100. z = self.dots
  101. while z > 0:
  102. if z > 4 and x[0] == x[z] and y[0] == y[z]:
  103. self.inGame = False
  104. z = z - 1
  105. if y[0] > HEIGHT - DOT_SIZE:
  106. self.inGame = False
  107. if y[0] < 0:
  108. self.inGame = False
  109. if x[0] > WIDTH - DOT_SIZE:
  110. self.inGame = False
  111. if x[0] < 0:
  112. self.inGame = False
  113. def locate_apple(self):
  114. r = random.randint(0, RAND_POS)
  115. self.apple_x = r * DOT_SIZE
  116. r = random.randint(0, RAND_POS)
  117. self.apple_y = r * DOT_SIZE
  118. def on_key_down(self, event):
  119. key = event.keyval
  120. if key == gtk.keysyms.Left and not self.right:
  121. self.left = True
  122. self.up = False
  123. self.down = False
  124. if key == gtk.keysyms.Right and not self.left:
  125. self.right = True
  126. self.up = False
  127. self.down = False
  128. if key == gtk.keysyms.Up and not self.down:
  129. self.up = True
  130. self.right = False
  131. self.left = False
  132. if key == gtk.keysyms.Down and not self.up:
  133. self.down = True
  134. self.right = False
  135. self.left = False
  136. class Snake(gtk.Window):
  137. def __init__(self):
  138. super(Snake, self).__init__()
  139. self.set_title('Snake')
  140. self.set_size_request(WIDTH, HEIGHT)
  141. self.set_resizable(False)
  142. self.set_position(gtk.WIN_POS_CENTER)
  143. self.board = Board()
  144. self.connect("key-press-event", self.on_key_down)
  145. self.add(self.board)
  146. self.connect("destroy", gtk.main_quit)
  147. self.show_all()
  148. def on_key_down(self, widget, event):
  149. key = event.keyval
  150. self.board.on_key_down(event)
  151. Snake()
  152. gtk.main()

首先,我们将定义一些在游戏中使用的全局变量。

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

  1. x = [0] * ALL_DOTS
  2. y = [0] * ALL_DOTS

这两个列表存储蛇的所有可能关节的 x,y 坐标。

init_game()方法初始化变量,加载图像并启动超时功能。

  1. self.left = False
  2. self.right = True
  3. self.up = False
  4. self.down = False
  5. self.inGame = True
  6. self.dots = 3

游戏开始时,蛇有三个关节。 而且它正在向右行驶。

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

  1. while z > 0:
  2. x[z] = x[(z - 1)]
  3. y[z] = y[(z - 1)]
  4. z = z - 1

该代码将关节向上移动。

  1. if self.left:
  2. x[0] -= DOT_SIZE

将头向左移动。

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

  1. while z > 0:
  2. if z > 4 and x[0] == x[z] and y[0] == y[z]:
  3. self.inGame = False
  4. z = z - 1

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

  1. if y[0] > HEIGHT - DOT_SIZE:
  2. self.inGame = False

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

locate_apple()方法在表格上随机定位一个苹果。

  1. r = random.randint(0, RAND_POS)

我们得到一个从 0 到RAND_POS-1的随机数。

  1. self.apple_x = r * DOT_SIZE
  2. ...
  3. self.apple_y = r * DOT_SIZE

这些行设置了apple对象的 x,y 坐标。

  1. self.connect("key-press-event", self.on_key_down)
  2. ...
  3. def on_key_down(self, widget, event):
  4. key = event.keyval
  5. self.board.on_key_down(event)

我们在Snake类中捕获按键事件,并将处理委托给board对象。

Board类的on_key_dow()方法中,我们确定玩家按下了哪些键。

  1. if key == gtk.keysyms.Left and not self.right:
  2. self.left = True
  3. self.up = False
  4. self.down = False

如果我们按左光标键,则将self.left变量设置为True。 在move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

PyGTK 中的贪食蛇游戏 - 图1

图:贪食蛇

这是使用 PyGTK 编程库编程的贪食蛇电脑游戏。