ساخت بازی Car Race با پایتون و Pygame

پروژه‌های بازی‌سازی پایتون

این مطلب از سایت tool.hamidvalad.ir گرفته شده است

معرفی

بازی Car Race یک بازی ماشین‌سواری ساده و جذاب است که با استفاده از کتابخانه Pygame در پایتون توسعه داده شده است. در این بازی، بازیکن باید ماشین خود را در جاده کنترل کند و از برخورد با ماشین‌های دیگر و دیواره‌ها اجتناب کند. هر چه مدت زمان بیشتری بازی کند، امتیاز بیشتری کسب می‌کند. بازی دارای سیستم ذخیره‌سازی بهترین امتیاز (High Score) است که در یک فایل متنی ذخیره می‌شود.

نصب و پیش‌نیازها

برای اجرای این پروژه، نیاز به نصب پایتون (نسخه ۳.۶ یا بالاتر) و کتابخانه Pygame دارید. همچنین باید فایل‌های تصویری و صوتی بازی را در پوشه‌های مشخص شده قرار دهید.

pip install pygame

ساختار پوشه‌های پروژه باید به صورت زیر باشد:

  • پوشه image/ - حاوی تصاویر ماشین‌ها و دیواره‌ها
  • پوشه music/ - حاوی فایل‌های صوتی
  • پوشه data/ - حاوی فایل ذخیره امتیاز (save.dat)

اگر پوشه data وجود ندارد، بازی به طور خودکار آن را ایجاد می‌کند.

ساختار کلی کد

کد بازی از بخش‌های اصلی زیر تشکیل شده است:

  • تعریف ثابت‌ها: ابعاد پنجره، رنگ‌ها، نرخ فریم (FPS)، سرعت بازی و سایر پارامترها
  • توابع کمکی: terminate()، waitForPlayerToPressKey()، playerHasHitBaddie()، drawText()
  • راه‌اندازی اولیه Pygame: مقداردهی پنجره، بارگذاری تصاویر و صداها
  • حلقه اصلی بازی: مدیریت رویدادهای صفحه‌کلید، تولید ماشین‌های دشمن، حرکت بازیکن، تشخیص برخوردها
  • سیستم امتیازدهی: محاسبه امتیاز، نمایش آن روی صفحه و ذخیره بهترین امتیاز
  • منطق بازی: مدیریت جان‌های بازیکن، صفحه شروع و پایان بازی

مکانیک‌های بازی

حرکت بازیکن

بازیکن می‌تواند با کلیدهای WASD یا کلیدهای جهت‌دار، ماشین خود را در چهار جهت حرکت دهد. کد حرکت به این صورت است:

if moveLeft and playerRect.left > 0:
    playerRect.move_ip(-1 * PLAYERMOVERATE, 0)
if moveRight and playerRect.right < WINDOWWIDTH:
    playerRect.move_ip(PLAYERMOVERATE, 0)
if moveUp and playerRect.top > 0:
    playerRect.move_ip(0, -1 * PLAYERMOVERATE)
if moveDown and playerRect.bottom < WINDOWHEIGHT:
    playerRect.move_ip(0, PLAYERMOVERATE)

تولید ماشین‌های دشمن

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

newBaddie = {'rect': pygame.Rect(random.randint(140, 485), 0 - baddieSize, 23, 47),
            'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED),
            'surface':pygame.transform.scale(random.choice(sample), (23, 47))}

تشخیص برخورد

تابع playerHasHitBaddie بررسی می‌کند که آیا ماشین بازیکن با ماشین دشمن یا دیواره برخورد کرده است یا خیر:

def playerHasHitBaddie(playerRect, baddies):
    for b in baddies:
        if playerRect.colliderect(b['rect']):
            return True
    return False

سیستم تقلب (Cheat Codes)

بازی دو کد تقلب دارد:

  • کلید Z: حرکت دشمنان را معکوس می‌کند (به سمت بالا)
  • کلید X: سرعت دشمنان را بسیار کاهش می‌دهد

استفاده از این کدهای تقلب امتیاز بازیکن را صفر می‌کند.

سیستم امتیازدهی و ذخیره‌سازی

بازی از یک فایل متنی ساده به نام save.dat در پوشه data برای ذخیره بهترین امتیاز استفاده می‌کند.

بارگذاری بهترین امتیاز

در ابتدای بازی، فایل save.dat خوانده می‌شود. اگر فایل وجود نداشته باشد، ایجاد شده و با مقدار صفر پر می‌شود:

if not os.path.exists("data/save.dat"):
    f=open("data/save.dat",'w')
    f.write(str(zero))
    f.close()   
v=open("data/save.dat",'r')
topScore = int(v.readline())
v.close()

ذخیره امتیاز جدید

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

if score > topScore:
    g=open("data/save.dat",'w')
    g.write(str(score))
    g.close()
    topScore = score

نمایش امتیازها

امتیاز فعلی، بهترین امتیاز و تعداد جان‌های باقی‌مانده در گوشه صفحه نمایش داده می‌شوند:

drawText('Score: %s' % (score), font, windowSurface, 128, 0)
drawText('Top Score: %s' % (topScore), font, windowSurface,128, 20)
drawText('Rest Life: %s' % (count), font, windowSurface,128, 40)

کد کامل بازی

در زیر کد کامل و یکپارچه بازی Car Race قرار داده شده است:

import pygame, random, sys ,os,time
from pygame.locals import *

WINDOWWIDTH = 800
WINDOWHEIGHT = 600
TEXTCOLOR = (255, 255, 255)
BACKGROUNDCOLOR = (0, 0, 0)
FPS = 40
BADDIEMINSIZE = 10
BADDIEMAXSIZE = 40
BADDIEMINSPEED = 8
BADDIEMAXSPEED = 8
ADDNEWBADDIERATE = 6
PLAYERMOVERATE = 5
count=3

def terminate():
    pygame.quit()
    sys.exit()

def waitForPlayerToPressKey():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                terminate()
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE: #escape quits
                    terminate()
                return

def playerHasHitBaddie(playerRect, baddies):
    for b in baddies:
        if playerRect.colliderect(b['rect']):
            return True
    return False

def drawText(text, font, surface, x, y):
    textobj = font.render(text, 1, TEXTCOLOR)
    textrect = textobj.get_rect()
    textrect.topleft = (x, y)
    surface.blit(textobj, textrect)

# set up pygame, the window, and the mouse cursor
pygame.init()
mainClock = pygame.time.Clock()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('car race')
pygame.mouse.set_visible(False)

# fonts
font = pygame.font.SysFont(None, 30)

# sounds
gameOverSound = pygame.mixer.Sound('music/crash.wav')
pygame.mixer.music.load('music/car.wav')
laugh = pygame.mixer.Sound('music/laugh.wav')


# images
playerImage = pygame.image.load('image/car1.png')
car3 = pygame.image.load('image/car3.png')
car4 = pygame.image.load('image/car4.png')
playerRect = playerImage.get_rect()
baddieImage = pygame.image.load('image/car2.png')
sample = [car3,car4,baddieImage]
wallLeft = pygame.image.load('image/left.png')
wallRight = pygame.image.load('image/right.png')


# "Start" screen
drawText('Press any key to start the game.', font, windowSurface, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3))
drawText('And Enjoy', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)+30)
pygame.display.update()
waitForPlayerToPressKey()
zero=0
if not os.path.exists("data/save.dat"):
    f=open("data/save.dat",'w')
    f.write(str(zero))
    f.close()   
v=open("data/save.dat",'r')
topScore = int(v.readline())
v.close()
while (count>0):
    # start of the game
    baddies = []
    score = 0
    playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50)
    moveLeft = moveRight = moveUp = moveDown = False
    reverseCheat = slowCheat = False
    baddieAddCounter = 0
    pygame.mixer.music.play(-1, 0.0)

    while True: # the game loop
        score += 1 # increase score

        for event in pygame.event.get():
            
            if event.type == QUIT:
                terminate()

            if event.type == KEYDOWN:
                if event.key == ord('z'):
                    reverseCheat = True
                if event.key == ord('x'):
                    slowCheat = True
                if event.key == K_LEFT or event.key == ord('a'):
                    moveRight = False
                    moveLeft = True
                if event.key == K_RIGHT or event.key == ord('d'):
                    moveLeft = False
                    moveRight = True
                if event.key == K_UP or event.key == ord('w'):
                    moveDown = False
                    moveUp = True
                if event.key == K_DOWN or event.key == ord('s'):
                    moveUp = False
                    moveDown = True

            if event.type == KEYUP:
                if event.key == ord('z'):
                    reverseCheat = False
                    score = 0
                if event.key == ord('x'):
                    slowCheat = False
                    score = 0
                if event.key == K_ESCAPE:
                        terminate()
            

                if event.key == K_LEFT or event.key == ord('a'):
                    moveLeft = False
                if event.key == K_RIGHT or event.key == ord('d'):
                    moveRight = False
                if event.key == K_UP or event.key == ord('w'):
                    moveUp = False
                if event.key == K_DOWN or event.key == ord('s'):
                    moveDown = False

            

        # Add new baddies at the top of the screen
        if not reverseCheat and not slowCheat:
            baddieAddCounter += 1
        if baddieAddCounter == ADDNEWBADDIERATE:
            baddieAddCounter = 0
            baddieSize =30 
            newBaddie = {'rect': pygame.Rect(random.randint(140, 485), 0 - baddieSize, 23, 47),
                        'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED),
                        'surface':pygame.transform.scale(random.choice(sample), (23, 47)),
                        }
            baddies.append(newBaddie)
            sideLeft= {'rect': pygame.Rect(0,0,126,600),
                       'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED),
                       'surface':pygame.transform.scale(wallLeft, (126, 599)),
                       }
            baddies.append(sideLeft)
            sideRight= {'rect': pygame.Rect(497,0,303,600),
                       'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED),
                       'surface':pygame.transform.scale(wallRight, (303, 599)),
                       }
            baddies.append(sideRight)
            
            

        # Move the player around.
        if moveLeft and playerRect.left > 0:
            playerRect.move_ip(-1 * PLAYERMOVERATE, 0)
        if moveRight and playerRect.right < WINDOWWIDTH:
            playerRect.move_ip(PLAYERMOVERATE, 0)
        if moveUp and playerRect.top > 0:
            playerRect.move_ip(0, -1 * PLAYERMOVERATE)
        if moveDown and playerRect.bottom < WINDOWHEIGHT:
            playerRect.move_ip(0, PLAYERMOVERATE)
        
        for b in baddies:
            if not reverseCheat and not slowCheat:
                b['rect'].move_ip(0, b['speed'])
            elif reverseCheat:
                b['rect'].move_ip(0, -5)
            elif slowCheat:
                b['rect'].move_ip(0, 1)

         
        for b in baddies[:]:
            if b['rect'].top > WINDOWHEIGHT:
                baddies.remove(b)

        # Draw the game world on the window.
        windowSurface.fill(BACKGROUNDCOLOR)

        # Draw the score and top score.
        drawText('Score: %s' % (score), font, windowSurface, 128, 0)
        drawText('Top Score: %s' % (topScore), font, windowSurface,128, 20)
        drawText('Rest Life: %s' % (count), font, windowSurface,128, 40)
        
        windowSurface.blit(playerImage, playerRect)

        
        for b in baddies:
            windowSurface.blit(b['surface'], b['rect'])

        pygame.display.update()

        # Check if any of the car have hit the player.
        if playerHasHitBaddie(playerRect, baddies):
            if score > topScore:
                g=open("data/save.dat",'w')
                g.write(str(score))
                g.close()
                topScore = score
            break

        mainClock.tick(FPS)

    # "Game Over" screen.
    pygame.mixer.music.stop()
    count=count-1
    gameOverSound.play()
    time.sleep(1)
    if (count==0):
     laugh.play()
     drawText('Game over', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
     drawText('Press any key to play again.', font, windowSurface, (WINDOWWIDTH / 3) - 80, (WINDOWHEIGHT / 3) + 30)
     pygame.display.update()
     time.sleep(2)
     waitForPlayerToPressKey()
     count=3
     gameOverSound.stop()

نحوه اجرا

برای اجرای بازی مراحل زیر را دنبال کنید:

  1. کد بالا را در یک فایل با نام car_race.py ذخیره کنید.
  2. اطمینان حاصل کنید که Pygame نصب شده است: pip install pygame
  3. پوشه‌های زیر را در کنار فایل اصلی ایجاد کنید:
    • image/ - حاوی فایل‌های تصویری (car1.png, car2.png, car3.png, car4.png, left.png, right.png)
    • music/ - حاوی فایل‌های صوتی (crash.wav, car.wav, laugh.wav)
    • data/ - برای ذخیره فایل امتیاز (به طور خودکار ایجاد می‌شود)
  4. فایل را اجرا کنید: python car_race.py

بعد از اجرا، صفحه شروع نمایش داده می‌شود. با فشردن هر کلید، بازی شروع می‌شود.

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

منابع و مستندات