Home  Contents

Snake game in PyGTK

فى هذه الجزئية سننشئ نسخة من لعبة الثعبان

Snake game

لعبة الثعبان لعبة فيديو كلاسيكية من اواخر السبعينيات ثم تم استقدامها للحاسب الشخصى، فى هذه اللعبة يتحكم اللاعب فى ثعبان وهدفها هو اكل اكثر عدد ممكن من التفاح، وكلما يأكل الثعبان يكبر حجم جسمه فيجب محاولة تفادى الإصطدام بالحائظ او بجسمه

Development

جزء كل قطع الثعبان 10px ويتم التحكم فيه عن طريق الأسهم، ويبدأ الثعبان ب3 قطع وتبدأ اللعبة بالضغط على اى من الأسهم وعند انتهاء اللعبة نعرض Game Over فى منتصف اللوحة



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

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(100, 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 .. تنشئ المتغيرات وتحمل الصور وتبدأ دالة ال timeout



These two lists store x, y coordinates of all possible joints of a snake.

The init_game() method initializes variables, loads images and starts a timeout function.

 self.left = False
 self.right = True
 self.up = False
 self.down = False
 self.inGame = True
 self.dots = 3

فى بداية اللعبة، الثعبان لديه 3 قطع ورأسه لليمين

When the game starts, the snake has three joints. And it is heading to the right.

الطريقة 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

ننهى اللعبة اذا اصطدم الثعبان بأسفل اللوحة

The locate_apple() method locates an apple randomly on the form.

 r = random.randint(0, RAND_POS)

نحصل على رقم عشوائى من 0 الى RAND_POS - 1



 self.apple_x = r * DOT_SIZE
 ...
 self.apple_y = r * DOT_SIZE

تحدد احداثيات ال س ، ص للتفاحة

     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)

نصطاد الضغة على اى من المفاتيح ونعالجها فى on_key_down التابعة للصف Board لنحدد اى مفتاح ضغطه اللاعب

We catch the key press event in the Snake class, and delegate the processing to the board object.

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

اذا ضغطنا على سهم يسار ، نقوم بإسناد قيمة True للمتغير self.left المستخدم فى الطريقة move لنغير احداثات الثعبان، لاحظ ايضا عندما يكون الثعبان متجه لليمين لانستطيع تحويله مباشرة لليسار




Snake

Figure: Snake

هذه كانت لعبة الثعبان، تم كتابتها ب PyGTK


Home ‡ Contents ‡ Top of Page