Skip to content

Sajed68/fast_pygame_tutorial_Snake_game

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

برای شروع کار: ابتدا ماژول را به برنامه اضافه می‌کنیم:

import pygame

سپس نیاز است که فراخوانی اولیه ماژول در ابتدای برنامه انجام شود:

pygame.init()

هدف ما در اینجا نوشتن یک برنامه ساده از شبیه سازی بازی [snake][1] است، با توجه به این که مکان طعمه در هر مرحله تصادفی انتخاب می‌شود، بنابراین به یک مولد اعداد تصادفی هم نیاز خواهیم داشت.

import pygame
import random
pygame.init()
(6, 0)

پنجره قابل نمایش در pygame ، screen یا صفحه نام دارد.این کلاس هر آنچه که قابل دیدن باشد را شرح می‌دهد. بنابراین ابتدا یک صفحه ایجاد می‌کنیم و اجزای دیگر را بر روی آن صفحه رسم می‌کنیم.

  • برای این که پنجره برنامه دارای اسم باشد از دستور زیر برای آن نام تعیین می‌کنیم:

pygame.display.set_caption('Snake v 1.0')

* راه انداز گرافیکی در پنجره مستطیلی Surface نام دارد، در واقع این سطح یک handle برای کارهای گرافیکی ما می‌باشد. سپس برای این که روی این صفحه رنگی اختصاص دهیم، یک پیش زمینه با استفاده از کلاس Surface تعیین می‌کنیم و آن را با رنگ خاکستری معادل (70, 70, 70) پر می‌کنیم. این کار را با استفاده از متد fill از کلاس Surface انجام می‌دهیم. اما اگر بخواهیم که این پیش زمینه بی‌رنگ ظاهر شود بجای متد fill از دستور زیر استفاده می‌کنیم:

background.set_colorkey((0,0,0))

در نهایت با استفاده از متد blit در صفحه screen پیش زمینه را در مختصات دلخواه قرار می‌دهیم. توجه شود که مبدا مختصات در بالاترین نقطه سمت چپ صفحه قرار دارد.

pygame.display.set_caption('Snake v 1')
screen = pygame.display.set_mode((640, 480))  # I set Screen size
background = pygame.Surface(screen.get_size())
background.fill((70, 70, 70))
background = background.convert()
screen.blit(background, (0,0))
<rect(0, 0, 640, 480)>

برای ایجاد زمان واقعی از شی pygame.time.clock() استفاده می‌کنیم. هر tick() از آن یک زمان بر حسب میلی‌ثانیه بر می‌گرداند. این زمان از روی نرخ تعداد فریم‌های نمایشی در هر ثانیه محاسبه می‌شود. بنابراین نیاز است که نرخ فریم از پیش تعیین شده باشد. در اینجا مقدار ۱۵ فریم در ثانیه انتخاب شده است. مقادیر بزرگ‌تر را خودتان امتحان نمایید.

clock = pygame.time.Clock()  # this is a clock
FPS = 15  # change frame rate for velocity of Snake 

مار قرار است که بر روی یک صفحه رسم شود، و این صفحه بر روی همان screen از پیش تعریف شده مدل می‌شود. نکته مهم این است که صفحه مار باید بی‌رنگ باشد، مگر در جاهایی که خود مار وجود دارد.

SNAKE_PLANE = pygame.Surface(screen.get_size())
SNAKE_PLANE.set_colorkey((0, 0, 0))

تمام برنامه در یک حلقه اصلی قرار دارد و در هر دور حلفه یک فریم از تصویر نمایش داده می‌شود، برای این که این حلقه عمر بی‌نهایت داشته باشد، از حلقه while استفاده می‌شود. اما برای این که بتوانیم حلقه را گاهی متوقف کنیم از یک عبارت منظقی استفاده می‌کنیم و به طور پیش‌فرض مقدار آن را برابر True قرار می‌دهیم.

main_loop = True

تا الان مقدمات را انجام دادیم و حالا آماده هستیم که یک کلاس برای نمایش و کنترل مار تعریف کنیم. مار دارای ویژگی‌های اولیه ساده‌ای می‌باشد. این ویژگی‌ها نیاز است که در همان ابتدای تعریف کلاس مقدار دهی شوند.:

self.points = [(x1,y1), (x2,y2), (x3,y3)] # initial position
self.__score__ = len(self.points) - 3  # score started from 0 and length started from 3
self.__draw__() # A function to draw "Snake" on the "SNAKE_PLANE"
self.direction = 'left' # the direction where snake wants to go
self.length = len(self.points)
self.crash = False  # not crash so you can go on
self.grid = [] # "Screen" is a grid 64 x 48, so each cell has 10 x 10 points area
self.__grow_num__ = 0 # after every collection of food, the length increases by __grow_num__ + 1
for i in range(64):
            for j in range(48):
                self.grid.append((i, j))
self.point_coor = None  # initiate location of food is UnKnown!
self.__create_point__() # A function to set location of food point

بنابراین کلاس تعریف شده نیاز به دو تابع یکی برای نمایش و دیگری برای مشخص کردن مکان طعمه خواهیم داشت: در این تابع هر نقطه موجود در ویژگی self.points به صورت یک مربع ۱۰ در ۱۰ سفید رنگ بر روی صفحه ظاهر می‌شود. تابع grow_up(): این تابع نقاط را برای یک حرکت تغییر می‌دهد.

def __draw__(self):
        global SNAKE_PLANE
        SNAKE_PLANE = pygame.Surface(screen.get_size())
        SNAKE_PLANE.set_colorkey((0, 0, 0))
        self.__grow_up__()
        for point in self.points:
            x, y = point
            pygame.draw.rect(SNAKE_PLANE, (255, 255, 255), (x*10, y*10, 10, 10))
        font = pygame.font.SysFont('ArcadeClassic', size=20)
        text = 'score ' + str(self.__score__)
        txt = font.render(text, True, (211, 211, 211))
        SNAKE_PLANE.blit(txt, (20, 20))
        SNAKE_PLANE = SNAKE_PLANE.convert()
        
        

def __grow_up__(self):
        if snake.__grow_num__ > 0:
            x1, y1 = self.points[-1]
            x0, y0 = self.points[-2]
            if x1 == x0 and y1 == y0 + 1:
                x = x1
                y = y1 - 1
            elif x1 == x0 and y1 == y0 - 1:
                x = x1
                y = y1  + 1
            elif y1 == y0 and x1 == x0 + 1:
                x = x1 - 1
                y = y1
            elif y1 == y0 and x1 == x0 - 1:
                x = x1 + 1
                y = y1
            self.points.append((x, y))
                        

برای تعیین مکان و نمایش طعمه از متد زیر استفاده می‌شود: مشخص است که مکان نقطه نباید توسط مار پر شده باشد:

def __create_point__(self):
        grid = self.grid[0:]
        for p in self.points:
            try:
                grid.remove(p)
            except:
                pass
        x, y = grid[random.randint(0,len(grid)-1)]
        self.point_putted = True
        self.point_coor = (x * 10, y * 10, 10, 10)
        pygame.draw.rect(SNAKE_PLANE, (0, 0, 0), self.point_coor)

همچنین در صورت برخورد سر مار با حاشیه‌های screen و یا بدن خودش، بازی متوقف می‌شود. بررسی این قضیه توسط متد crash انجام می‌شود.

def __crash_check__(self):
        start_point = self.points[0]
        if self.points.count(start_point) != 1:
            self.crash = True
            # print ('Crash')
        x, y = start_point
        if x < 0 or y < 0 or x > 63 or y > 47:
            self.crash = True
            # print ('Crash')

توابع برای حرکت در جهات مختلف، که به صورت move_left, up, right, down نوشته می‌شوند. فرم یکی از آن‌ها به صورت زیر است:

def __move_up__(self):
        start_x, start_y = self.points[0]
        next_x, next_y = self.points[1]
        if start_x == next_x and start_y == next_y + 1:
            go_x = start_x
            go_y = start_y
            self.__move_right__()
        else:
            go_x = start_x
            go_y = start_y - 1
            length = len(self.points)
            for i in range(length):
                if i != length - 1:
                    self.points[length - 1 - i] = self.points[length - 2 - i]
                else:
                    self.points[0] = (go_x, go_y)
       
        self.__draw__()

در صورت برخورد سر مار با محل طعمه باید امتیاز یکی اضافه شود:

def __get_bounce_check__(self):
        point = (self.point_coor[0]/10, self.point_coor[1]/10)
        if self.points[0] == point:
            # print ('got a bounce...')
            self.__create_point__()
            snake.__grow_num__ = snake.__grow_num__ + 1

در هر دور از اجرای حلفه اصلی نیاز است که شی **مار** بروز رسانی شود. در این بروز رسانی جهت حرکت دریافت می‌شود، حرکت انجام شده و بررسی می‌شود که آیا امتیاز دریافت شده است یا بازی شکست خورده است.

def update(self, direction):
        if self.crash is False:
            self.__score__ = len(self.points) - 3
            direction = self.direction if direction == None else direction
            if direction == 'down':
                self.__move_down__()
            elif direction == 'left':
                self.__move_left__()
            if direction == 'right':
                self.__move_right__()
            elif direction == 'up':
                self.__move_up__()
            elif direction == 'paused':
                pass
            self.direction = direction if direction != 'paused' else self.direction
            # self.__create_point()
            pygame.draw.rect(SNAKE_PLANE, (255, 255, 255), self.point_coor)
            self.__get_bounce_check__()
            self.__crash_check__()

در نهایت کلاس مار به صورت زیر نوشته می‌شود:

class snake(object):
    '''
    help : ?
    '''
    __grow_num__ = 0
    def __init__(self):
        self.points = [(32, 21), (33, 21), (33,21)]
        self.stack_pos = ['s', 'd', 'd', 'e']
        self.__score__ = len(self.points) - 3
        self.__draw__()
        self.direction = 'left'
        self.length = len(self.points)
        self.crash = False
        self.grid = []
        self.__grow_num__ = 0
        for i in range(64):
            for j in range(48):
                self.grid.append((i, j))
        self.point_coor = None
        self.__create_point__()
        
        
    def __grow_up__(self):
        if snake.__grow_num__ > 0:
            x1, y1 = self.points[-1]
            x0, y0 = self.points[-2]
            if x1 == x0 and y1 == y0 + 1:
                x = x1
                y = y1 - 1
            elif x1 == x0 and y1 == y0 - 1:
                x = x1
                y = y1  + 1
            elif y1 == y0 and x1 == x0 + 1:
                x = x1 - 1
                y = y1
            elif y1 == y0 and x1 == x0 - 1:
                x = x1 + 1
                y = y1
            self.points.append((x, y))

            snake.__grow_num__ -= 1
            # print ((x1, y1), (x0, y0), (x, y))      
            
    def __draw__(self):
        global SNAKE_PLANE
        SNAKE_PLANE = pygame.Surface(screen.get_size())
        SNAKE_PLANE.set_colorkey((0, 0, 0))
        self.__grow_up__()
        for point in self.points:
            x, y = point
            pygame.draw.rect(SNAKE_PLANE, (255, 255, 255), (x*10, y*10, 10, 10))
        font = pygame.font.SysFont('ArcadeClassic', size=20)
        text = 'score ' + str(self.__score__)
        txt = font.render(text, True, (211, 211, 211))
        SNAKE_PLANE.blit(txt, (20, 20))
        SNAKE_PLANE = SNAKE_PLANE.convert()
        
    def __crash_check__(self):
        start_point = self.points[0]
        if self.points.count(start_point) != 1:
            self.crash = True
            # print ('Crash')
        x, y = start_point
        if x < 0 or y < 0 or x > 63 or y > 47:
            self.crash = True
            # print ('Crash')
        
    def __move_left__(self):
        start_x, start_y = self.points[0]
        next_x, next_y = self.points[1]
        if start_y == next_y and start_x == next_x + 1:
            go_x = start_x
            go_y = start_y
            self.__move_down__()
        else:
            go_x = start_x - 1
            go_y = start_y
            length = len(self.points)
            for i in range(length):
                if i != length - 1:
                    self.points[length - 1 - i] = self.points[length - 2 - i]
                else:
                    self.points[0] = (go_x, go_y)
       
        self.__draw__()
        
    def __move_up__(self):
        start_x, start_y = self.points[0]
        next_x, next_y = self.points[1]
        if start_x == next_x and start_y == next_y + 1:
            go_x = start_x
            go_y = start_y
            self.__move_right__()
        else:
            go_x = start_x
            go_y = start_y - 1
            length = len(self.points)
            for i in range(length):
                if i != length - 1:
                    self.points[length - 1 - i] = self.points[length - 2 - i]
                else:
                    self.points[0] = (go_x, go_y)
       
        self.__draw__()
        
    def __move_right__(self):
        start_x, start_y = self.points[0]
        next_x, next_y = self.points[1]
        if start_y == next_y and start_x == next_x - 1:
            go_x = start_x
            go_y = start_y
            self.__move_up__()
        else:
            go_x = start_x + 1
            go_y = start_y
            length = len(self.points)
            for i in range(length):
                if i != length - 1:
                    self.points[length - 1 - i] = self.points[length - 2 - i]
                else:
                    self.points[0] = (go_x, go_y)
       
        self.__draw__()

    def __move_down__(self):
        start_x, start_y = self.points[0]
        next_x, next_y = self.points[1]
        if start_x == next_x and start_y == next_y - 1:
            go_x = start_x
            go_y = start_y
            self.__move_left__()
        else:
            go_x = start_x
            go_y = start_y + 1
            length = len(self.points)
            for i in range(length):
                if i != length - 1:
                    self.points[length - 1 - i] = self.points[length - 2 - i]
                else:
                    self.points[0] = (go_x, go_y)
       
        self.__draw__()
        


    def __create_point__(self):
        grid = self.grid[0:]
        for p in self.points:
            try:
                grid.remove(p)
            except:
                pass
        x, y = grid[random.randint(0,len(grid)-1)]
        self.point_putted = True
        self.point_coor = (x * 10, y * 10, 10, 10)
        pygame.draw.rect(SNAKE_PLANE, (0, 0, 0), self.point_coor)
            
    def __get_bounce_check__(self):
        point = (self.point_coor[0]/10, self.point_coor[1]/10)
        if self.points[0] == point:
            # print ('got a bounce...')
            self.__create_point__()
            snake.__grow_num__ = snake.__grow_num__ + 1
            
    
    def update(self, direction):
        if self.crash is False:
            self.__score__ = len(self.points) - 3
            direction = self.direction if direction == None else direction
            if direction == 'down':
                self.__move_down__()
            elif direction == 'left':
                self.__move_left__()
            if direction == 'right':
                self.__move_right__()
            elif direction == 'up':
                self.__move_up__()
            elif direction == 'paused':
                pass
            self.direction = direction if direction != 'paused' else self.direction
            # self.__create_point()
            pygame.draw.rect(SNAKE_PLANE, (255, 255, 255), self.point_coor)
            self.__get_bounce_check__()
            self.__crash_check__()

تابع حلقه اصلی برنامه، تابعی است که در هر دور حلقه اصلی خود منتظر دریافت کلیدی از کاربر می‌شود که مشخص کننده جهت حرکت مار می‌باشد. برای دریافت کلید نیاز به فعال سازی Event Handler داریم.برای فعال سازی این مورد از توابع pygame.event.get() استفاده می‌کنیم. در نهایت برای نمایش همه تغییرات و صفحه‌ها نیاز است که بر روی screen تغییرات با استفاده از متد blit بروز رسانی شود.

def main_game():
    Snake = snake()
    direction = 'left'
    main_loop = True
    #k = 1
    while main_loop:
        clock.tick(FPS)
        for event in pygame.event.get():
                if event.type is pygame.QUIT:   # for press (x) sign top of window 
                    main_loop = False
                elif event.type is pygame.KEYDOWN:
                    if event.key == pygame.K_a:
                        direction = 'left'
                    if event.key == pygame.K_w:
                        direction = 'up'
                    if event.key == pygame.K_s:
                        direction = 'down'
                    if event.key == pygame.K_d:
                        direction = 'right'
                    if event.key == pygame.K_p:
                        direction = 'pause'

        Snake.update(direction)
        screen.blit(background, (0,0))
        screen.blit(SNAKE_PLANE, (0,0))
        pygame.display.flip()
        #pygame.image.save(screen, str(k) + '.jpg')
        #k += 1
        if Snake.crash:
            return 0

همچنین برای برنامه نیاز داریم که یک منوی اولیه طراحی کنیم. در این قسمت پیش از شروع بازی و اجرای تابع main_game یک پنجره نمایش داده می‌شود که حاوی کمی توضیحات است.

if __name__ == '__main__':
    out = None
    while main_loop:
        milliseconds = clock.tick(FPS)
        for event in pygame.event.get():
            if event.type is pygame.QUIT:   # for press (x) sign top of window 
                main_loop = False
            elif event.type is pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    out = main_game()
        if out is None:
            pygame.draw.rect(SNAKE_PLANE, (255, 255, 255), (150, 120, 340, 240))
            SNAKE_PLANE = SNAKE_PLANE.convert()
            font = pygame.font.SysFont('ArcadeClassic', size=25)
            txt = font.render('Press Enter to Start game', True, (0, 0, 0))
            SNAKE_PLANE.blit(txt, (160, 150))
            txt = font.render('or red (x) to close', True, (0, 0, 0))
            SNAKE_PLANE.blit(txt, (160, 180))
            txt = font.render('use w s a d to direction', True, (0, 0, 0))
            SNAKE_PLANE.blit(txt, (160, 210))
            font = pygame.font.SysFont('ArcadeClassic', size=20)
            txt = font.render('P to Pause  Return to Restart game', True, (0, 0, 0))
            SNAKE_PLANE.blit(txt, (160, 240))
        screen.blit(background, (0,0))
        screen.blit(SNAKE_PLANE, (0,0))
        pygame.display.flip()
    pygame.quit()

snake


سایر نکات:

رسم شکل هندسی روی Surface:

در این برنامه دو Surface با نام‌های background و SNAKE_PLANE تعریف کردیم. پیش از رسم شکل باید توجه داشت که مبدا گوشه سمت چپ بالا می‌باشد و محور عمودی معرف مولفه y و محور افقی مولفه x است.در حالت کلی برای رسم شکل مراحل زیر طی می‌شود: * ایجاد یا انتخاب یک Surface * استفاده از یک تابع رسم با ورودی Surface * نمایش Surface بر روی screen با استفاده از متد screen.blit(Surface, (x,y))

توابع رسم در pygame

Rect = pygame.draw.rect(Surface, tuple(color), tuple(Rect), width=0)
pygame.draw.polygon(Surface, tuple(color), list(pointlists), width=0)
pygame.draw.circle(Surface, tuple(color), tuple(centerpointx,y), radius, width=0) 
Rect = pygame.draw.ellipse(Surface, tuple(color_rgb), tuple(Rect_xywh), width)
pygame.draw.arc(Surface, tuple(color), tuple(Rect), width)
pygame.draw.line(Surface, color, start_pos, stop_pos, width>1)
pygame.draw.lines(Surface, tuple(color), bool(closed), list(points), width)
python.draw.aaline(Surface, tuple(color), start, stop, end, blend=1)
python.draw.aalines(Surface, tuple(color), bool(closed), list(points), blend=1)

که اگر پهنا صفر باشد شکل‌ها به صورت توپر رسم می‌شوند.

رسم شکل در پیش‌زمینه:

bg = pygame.image.load(folder, "....")
bg = bg.convert()
screen.blit(bg, (x,y))

پخش صدا

ابتدا باید پخش کننده صوتی پیش تنظیم شود: این کار با دستور زیر انجام می‌شود:

pygame.mixer.pre.load(44100, -16, 2, 2048)

سپس پرونده صوتی را بارگزاری می‌کنیم:

m = pygame.music.load("soundfile.wav")
m.play(-1) # non-stop and repeated

نمایش متن

برای نمایش متن مراحل زیر طی می‌شود:

  • تعیین قلم:

pfont = pygame.SysFont("Name", int(size))

* تولید متن:

text = pfont.render("text", True, tuple(color))
text = text.convert_alpha() # optional

* نمایش متن روی صفحه:

screen.blit(text, (x,y))

مرجع

Releases

No releases published

Packages

 
 
 

Contributors