当前位置: 首页 > 文档资料 > PyGTK 教程 >

14 PyGTK 中的 Snake 游戏

优质
小牛编辑
121浏览
2023-12-01

在教程的这个部分,我们将创建一个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()

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

WIDTHHEIGHT常量确定了游戏面板的大小。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编程库制作的贪吃蛇电脑游戏。