14 PyGTK 中的 Snake 游戏
在教程的这个部分,我们将创建一个Snake游戏的克隆版。
Snake game
Snake(贪吃蛇)是一个古老的经典的视频游戏。它是在20世纪70年代后期第一次被创建。后来它被引入到PC机上。在这个游戏中,玩家控制一条蛇,目标即是尽可能地吃足够多的苹果。每次这个蛇吃下一个苹果,它的身体就会长长。这条蛇必须避免撞上墙和它自己的身体。这个游戏有时候被称为Nibbles。
Development
蛇的每个节点的大小被设计为10像素。蛇被方向键(cursor keys)控制。最初蛇有3个节点。游戏会立刻开始。如果游戏完成了,我们将在面板的中间显示“Game Over”的信息。
Code:snake.py
#!/usr/bin/python
# ZetCode PyGTK tutorial
#
# This is a simple snake game
# clone
#
# author: jan bodnar
# website: zetcode.com
# last edited: February 2009
import sys
import gtk
import cairo
import random
import glib
WIDTH = 300
HEIGHT = 270
DOT_SIZE = 10
ALL_DOTS = WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE)
RAND_POS = 26
DELAY = 100
x = [0] * ALL_DOTS
y = [0] * ALL_DOTS
class Board(gtk.DrawingArea):
def __init__(self):
super(Board, self).__init__()
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0, 0))
self.set_size_request(WIDTH, HEIGHT)
self.connect("expose-event", self.expose)
self.init_game()
def on_timer(self):
if self.inGame:
self.check_apple()
self.check_collision()
self.move()
self.queue_draw()
return True
else:
return False
def init_game(self):
self.left = False
self.right = True
self.up = False
self.down = False
self.inGame = True
self.dots = 3
for i in range(self.dots):
x[i] = 50 - i * 10
y[i] = 50
try:
self.dot = cairo.ImageSurface.create_from_png("dot.png")
self.head = cairo.ImageSurface.create_from_png("head.png")
self.apple = cairo.ImageSurface.create_from_png("apple.png")
except Exception, e:
print e.message
sys.exit(1)
self.locate_apple()
glib.timeout_add(DELAY, self.on_timer)
def expose(self, widget, event):
cr = widget.window.cairo_create()
if self.inGame:
cr.set_source_rgb(0, 0, 0)
cr.paint()
cr.set_source_surface(self.apple, self.apple_x, self.apple_y)
cr.paint()
for z in range(self.dots):
if (z == 0):
cr.set_source_surface(self.head, x[z], y[z])
cr.paint()
else:
cr.set_source_surface(self.dot, x[z], y[z])
cr.paint()
else:
self.game_over(cr)
def game_over(self, cr):
w = self.allocation.width / 2
h = self.allocation.height / 2
(x, y, width, height, dx, dy) = cr.text_extents("Game Over")
cr.set_source_rgb(65535, 65535, 65535)
cr.move_to(w - width/2, h)
cr.show_text("Game Over")
self.inGame = False
def check_apple(self):
if x[0] == self.apple_x and y[0] == self.apple_y:
self.dots = self.dots + 1
self.locate_apple()
def move(self):
z = self.dots
while z > 0:
x[z] = x[(z - 1)]
y[z] = y[(z - 1)]
z = z - 1
if self.left:
x[0] -= DOT_SIZE
if self.right:
x[0] += DOT_SIZE
if self.up:
y[0] -= DOT_SIZE
if self.down:
y[0] += DOT_SIZE
def check_collision(self):
z = self.dots
while z > 0:
if z > 4 and x[0] == x[z] and y[0] == y[z]:
self.inGame = False
z = z - 1
if y[0] > HEIGHT - DOT_SIZE:
self.inGame = False
if y[0] < 0:
self.inGame = False
if x[0] > WIDTH - DOT_SIZE:
self.inGame = False
if x[0] < 0:
self.inGame = False
def locate_apple(self):
r = random.randint(0, RAND_POS)
self.apple_x = r * DOT_SIZE
r = random.randint(0, RAND_POS)
self.apple_y = r * DOT_SIZE
def on_key_down(self, event):
key = event.keyval
if key == gtk.keysyms.Left and not self.right:
self.left = True
self.up = False
self.down = False
if key == gtk.keysyms.Right and not self.left:
self.right = True
self.up = False
self.down = False
if key == gtk.keysyms.Up and not self.down:
self.up = True
self.right = False
self.left = False
if key == gtk.keysyms.Down and not self.up:
self.down = True
self.right = False
self.left = False
class Snake(gtk.Window):
def __init__(self):
super(Snake, self).__init__()
self.set_title('Snake')
self.set_size_request(WIDTH, HEIGHT)
self.set_resizable(False)
self.set_position(gtk.WIN_POS_CENTER)
self.board = Board()
self.connect("key-press-event", self.on_key_down)
self.add(self.board)
self.connect("destroy", gtk.main_quit)
self.show_all()
def on_key_down(self, widget, event):
key = event.keyval
self.board.on_key_down(event)
Snake()
gtk.main()
首先,我们定义了一些在游戏中要使用的全局常量和变量。
WIDTH和HEIGHT常量确定了游戏面板的大小。DOT_SIZE就是苹果和蛇的节点的大小。ALL_DOTS常量是定义了在面板上可能有的点的最大数字。RAND_POS常量被用来计算一个苹果的随机位置。DELAY常量是设定游戏的速度。
x = [0] * ALL_DOTS y = [0] * ALL_DOTS
这两个列表存储蛇的所有可能的节点的x, y坐标。
init_game()方法初始化变量,加载图像,开始一个超时函数。
self.left = False self.right = True self.up = False self.down = False self.inGame = True self.dots = 3
当我们的游戏开始后,蛇有三个节点,并且头朝向右边。
在move()方法中,我们有游戏的关键算法。为了理解它,请看看蛇是怎样在运动。你控制蛇的头部,通过方向键,你可以改变它的方向。剩下的节点在链上移动一个位置。第二个节点移动到第一个节点原来所在的位置,第三个节点移动到第二个节点原来所在的位置等等。
while z > 0: x[z] = x[(z - 1)] y[z] = y[(z - 1)] z = z - 1
这里的代码将节点移动到链上。
if self.left: x[0] -= DOT_SIZE
将头部移向左边。
在checkCollision()方法中,我们来判断蛇是否撞到它自己或者其中一面墙。
while z > 0: if z > 4 and x[0] == x[z] and y[0] == y[z]: self.inGame = False z = z - 1
如果蛇用头部撞上了它的一个节点,游戏结束。
if y[0] > HEIGHT - DOT_SIZE: self.inGame = False
如果蛇撞上了面板的底部,游戏结束。
locate_apple()方法是在面板中随机地给苹果定位。
r = random.randint(0, RAND_POS)
我们获得一个从0到RAND_POS-1的随机数。
self.apple_x = r * DOT_SIZE ... self.apple_y = r * DOT_SIZE
这几行设置苹果这个对象的x, y坐标。
self.connect("key-press-event", self.on_key_down) ... def on_key_down(self, widget, event): key = event.keyval self.board.on_key_down(event)
我们在Snake类中捕获摁键事件,并且对面板对象委派一个处理过程。
在Board类的on_key_down()方法中,我们判断玩家点击了哪个按键。
if key == gtk.keysyms.Left and not self.right: self.left = True self.up = False self.down = False
如果我们按下左方向键,我们设置self.left变量为真,这个变量被用在move()方法中来改变蛇对象的坐标。我们也应该注意到,当蛇想右边前行的时候,我们不能立刻改为向左。
Figure:Snake
这是用PyGTK编程库制作的贪吃蛇电脑游戏。