معرفی — چه کاری قرار است انجام دهیم؟
در این آموزش قدمبهقدم یک نسخهٔ ساده از بازی
Flappy Bird را با استفاده از
python و pygame میسازیم. هدف این آموزش
این است که مبتدیها با مفاهیم پایهٔ بازیسازی دو بعدی آشنا شوند:
رسم تصویر در صفحه، حرکت اشیاء، برخورد (collision)، و پخش صدا.
پیشنیازها (کتابخانهها و ابزارها)
- + Python 3.7
- pip (مدیر بستهٔ پایتون)
- pygame — کتابخانهٔ اصلی برای رسم و کار با صدا
- ماژولهای استاندارد پایتون:
random, time, os
راهاندازی محیط (Virtual Environment)
mkdir flappy_project
cd flappy_project
python -m venv flappy_project_env
flappy_project_env\Scripts\activate # Linux/Mac source flappy_project_env/bin/activate
pip install pygame
بعد از اجرای دستورات بالا، محیط مجازی فعال است و پکیجها داخل همان محیط نصب میشوند.
چگونه بازی را اجرا کنیم
-
اگر از virtual environment استفاده میکنید آن را فعال کنید.
-
در پاورشل یا ترمینال دستور زیر را اجرا کنید:
دستور فعال سازی محیط مجازی
flappy_project_env\Scripts\activate # Linux/Mac source flappy_project_env/bin/activate
py flappy_bird.py # یا python flappy_bird.py
کد کامل پروژه
این کد را در فایلی به نام flappy_bird.py ذخیره کنید.
import pygame
import random
import time
from pygame.locals import *
# تنظیمات بازی
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
SPEED = 20
GRAVITY = 2.5
GAME_SPEED = 15
GROUND_WIDTH = 2 * SCREEN_WIDTH
GROUND_HEIGHT = 100
PIPE_WIDTH = 80
PIPE_HEIGHT = 500
PIPE_GAP = 150
# مسیر فایلهای صوتی
wing_sound = 'assets/audio/wing.wav'
hit_sound = 'assets/audio/hit.wav'
# راهاندازی صدا
pygame.mixer.init()
class Bird(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# تصاویر پرنده
self.images = [
pygame.image.load('assets/sprites/bluebird-upflap.png').convert_alpha(),
pygame.image.load('assets/sprites/bluebird-midflap.png').convert_alpha(),
pygame.image.load('assets/sprites/bluebird-downflap.png').convert_alpha()
]
self.speed = SPEED
self.current_image = 0
self.image = self.images[0]
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect[0] = SCREEN_WIDTH / 6
self.rect[1] = SCREEN_HEIGHT / 2
def update(self):
# انیمیشن بال زدن
self.current_image = (self.current_image + 1) % 3
self.image = self.images[self.current_image]
# اعمال جاذبه
self.speed += GRAVITY
self.rect[1] += self.speed
def bump(self):
# پرش پرنده
self.speed = -SPEED
def begin(self):
# انیمیشن در صفحه شروع
self.current_image = (self.current_image + 1) % 3
self.image = self.images[self.current_image]
class Pipe(pygame.sprite.Sprite):
def __init__(self, inverted, xpos, ysize):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('assets/sprites/pipe-green.png').convert_alpha()
self.image = pygame.transform.scale(self.image, (PIPE_WIDTH, PIPE_HEIGHT))
self.rect = self.image.get_rect()
self.rect[0] = xpos
if inverted:
# لوله بالایی
self.image = pygame.transform.flip(self.image, False, True)
self.rect[1] = - (self.rect[3] - ysize)
else:
# لوله پایینی
self.rect[1] = SCREEN_HEIGHT - ysize
self.mask = pygame.mask.from_surface(self.image)
def update(self):
# حرکت لوله به چپ
self.rect[0] -= GAME_SPEED
class Ground(pygame.sprite.Sprite):
def __init__(self, xpos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('assets/sprites/base.png').convert_alpha()
self.image = pygame.transform.scale(self.image, (GROUND_WIDTH, GROUND_HEIGHT))
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect[0] = xpos
self.rect[1] = SCREEN_HEIGHT - GROUND_HEIGHT
def update(self):
# حرکت زمین به چپ
self.rect[0] -= GAME_SPEED
def is_off_screen(sprite):
# بررسی خروج از صفحه
return sprite.rect[0] < -(sprite.rect[2])
def get_random_pipes(xpos):
# تولید لولههای تصادفی
size = random.randint(100, 300)
pipe = Pipe(False, xpos, size) # لوله پایین
pipe_inverted = Pipe(True, xpos, SCREEN_HEIGHT - size - PIPE_GAP) # لوله بالا
return pipe, pipe_inverted
# شروع بازی
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Flappy Bird')
# بارگذاری تصاویر پسزمینه
BACKGROUND = pygame.image.load('assets/sprites/background-day.png')
BACKGROUND = pygame.transform.scale(BACKGROUND, (SCREEN_WIDTH, SCREEN_HEIGHT))
BEGIN_IMAGE = pygame.image.load('assets/sprites/message.png').convert_alpha()
# ایجاد گروهها
bird_group = pygame.sprite.Group()
bird = Bird()
bird_group.add(bird)
ground_group = pygame.sprite.Group()
for i in range(2):
ground = Ground(GROUND_WIDTH * i)
ground_group.add(ground)
pipe_group = pygame.sprite.Group()
for i in range(2):
pipes = get_random_pipes(SCREEN_WIDTH * i + 800)
pipe_group.add(pipes[0])
pipe_group.add(pipes[1])
clock = pygame.time.Clock()
# صفحه شروع
begin = True
while begin:
clock.tick(15)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == KEYDOWN:
if event.key == K_SPACE or event.key == K_UP:
bird.bump()
pygame.mixer.music.load(wing_sound)
pygame.mixer.music.play()
begin = False
# نمایش صفحه شروع
screen.blit(BACKGROUND, (0, 0))
screen.blit(BEGIN_IMAGE, (120, 150))
# بروزرسانی زمین
if is_off_screen(ground_group.sprites()[0]):
ground_group.remove(ground_group.sprites()[0])
new_ground = Ground(GROUND_WIDTH - 20)
ground_group.add(new_ground)
bird.begin()
ground_group.update()
# رسم اجزا
bird_group.draw(screen)
ground_group.draw(screen)
pygame.display.update()
# حلقه اصلی بازی
while True:
clock.tick(15)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == KEYDOWN:
if event.key == K_SPACE or event.key == K_UP:
bird.bump()
pygame.mixer.music.load(wing_sound)
pygame.mixer.music.play()
# نمایش پسزمینه
screen.blit(BACKGROUND, (0, 0))
# مدیریت زمین
if is_off_screen(ground_group.sprites()[0]):
ground_group.remove(ground_group.sprites()[0])
new_ground = Ground(GROUND_WIDTH - 20)
ground_group.add(new_ground)
# مدیریت لولهها
if is_off_screen(pipe_group.sprites()[0]):
pipe_group.remove(pipe_group.sprites()[0])
pipe_group.remove(pipe_group.sprites()[0])
pipes = get_random_pipes(SCREEN_WIDTH * 2)
pipe_group.add(pipes[0])
pipe_group.add(pipes[1])
# بروزرسانی همه اجزا
bird_group.update()
ground_group.update()
pipe_group.update()
# رسم همه اجزا
bird_group.draw(screen)
pipe_group.draw(screen)
ground_group.draw(screen)
pygame.display.update()
# بررسی برخورد
if (pygame.sprite.groupcollide(bird_group, ground_group, False, False, pygame.sprite.collide_mask) or
pygame.sprite.groupcollide(bird_group, pipe_group, False, False, pygame.sprite.collide_mask)):
pygame.mixer.music.load(hit_sound)
pygame.mixer.music.play()
time.sleep(1)
break
فایلها و ساختار پوشهها (assets)
پس از ذخیرهٔ کد بالا، پوشهٔ assets را ایجاد کنید و
فایلهای زیر را در آن قرار دهید:
flappy_project/
├── flappy_bird.py
└── assets/
├── sprites/
│ ├── bluebird-upflap.png
│ ├── bluebird-midflap.png
│ ├── bluebird-downflap.png
│ ├── pipe-green.png
│ ├── base.png
│ ├── background-day.png
│ └── message.png
└── audio/
├── wing.wav
└── hit.wav
اگر این تصاویر و صداها را ندارید، میتوانید از تصاویر مشابه استفاده کنید یا فایلهای پروژهٔ نمونه را دانلود کنید.
پوشه کامل (assets) را از
اینجا
دریافت کنید
توضیح بخشهای مهم کد (گامبهگام)
-
راهاندازی Pygame: با
pygame.initو ساخت صفحه باdisplay.set_mode. -
کلاسها: سه کلاس اصلی داریم:
Bird(پرنده)،Pipe(لولهها)،Ground(زمین). هرکدام از اینها ازpygame.sprite.Spriteارث میبرند تا مدیریت و رسم سادهتر شود. -
ماسک بر مبنای تصویر: با استفاده از
pygame.mask.from_surfaceبرخورد دقیقتری بین پیکسلها انجام میشود. - حلقهٔ بازی: حلقهٔ شروع (نمایش پیام شروع) و حلقهٔ اصلی بازی که رویدادها را پردازش، موقعیتها را بروز و صفحه را بازنقش میکند.
-
تولید لولهها: با تابع
get_random_pipesاندازهٔ تصادفی برای لولهها تولید میکنیم و هر بار که لوله از صفحه خارج شد، یک جفت جدید اضافه میکنیم. -
صداها: با
pygame.mixer.music.loadصداهای ضربه و بالزدن پخش میشوند (در صورت نبود فایل، برنامه بدون صدا کار میکند).
عیبیابی و نکات
نکات خوب
- ساختار ساده و خلوت؛ مناسب برای یادگیری اولیه کلاسها و اسپرایتها.
- استفاده از ماسک برخورد برای دقت بالا.
- تقسیم وظایف بین کلاسها (قابل توسعه و خواناتر).
خطاهای رایج هنگام اجرا و روش رفع آنها❗
-
❌ FileNotFoundError:
اگر خطایی مشابهFileNotFoundError: No file 'assets/sprites/bluebird-upflap.png'دریافت کردید، یعنی پوشهٔassetsیا مسیر تصاویر اشتباه است. مطمئن شوید فایلها دقیقاً در مسیرassets/sprites/وassets/audio/قرار گرفته باشند. -
❌ ModuleNotFoundError: No module named 'pygame'
این خطا نشان میدهد کهpygameنصب نیست. دستور زیر را اجرا کنید:اگر از محیط مجازی استفاده میکنید (Virtual Environment)، ابتدا آن را فعال کنید:pip install pygameflappy_project_env\Scripts\activate # Linux/Mac source flappy_project_env/bin/activate -
⚠️ صدای بازی پخش نمیشود:
ممکن است فرمت صدا پشتیبانی نشود. از فرمت WAV برای فایلهای صوتی استفاده کنید (مثلwing.wavوhit.wav). همچنین بررسی کنید مسیرها درست نوشته شده باشند. -
⚠️ صفحه شروع کار نمیکند:
اگر صفحهی "Press Space to Start" نمایش داده نمیشود، احتمالاً نام تصویرmessage.pngاشتباه است یا در مسیر صحیح قرار ندارد.
💡 پیشنهاد آموزشی: اگر با ارورهای بالا برخورد کردید، سعی کنید خطا را درک کنید نه فقط برطرف — هر پیام خطا یک فرصت یادگیری است.
منبع اصلی و دریافت کد
این پروژه با الهام از نسخهٔ اصلی بازی Flappy Bird ساخته شده و کد منبع اولیه از مخزن زیر گرفته شده است:
🔗 github.com/LeonMarqs/Flappy-bird-python
اگر میخواهید نسخهٔ اصلی و فایلهای assets را دریافت
کنید، میتوانید با دستور زیر مخزن را کلون کنید:
git clone https://github.com/LeonMarqs/Flappy-bird-python.git
cd Flappy-bird-python
python -m venv flappy_project_env
flappy_project_env\Scripts\activate
pip install pygame
پس از کلون کردن، وارد پوشهٔ پروژه شوید، محیط مجازی (virtualenv) را فعال کنید و بازی را با دستور زیر اجرا نمایید:
python flappy.py