معرفی
در این آموزش، نحوه ساخت بازی کلاسیک Space Invaders را با استفاده از پایتون
و کتابخانه Pygame یاد میگیرید. این پروژه شامل تمام المانهای
اصلی بازی از جمله فضاپیما، بیگانگان، سیستم شلیک لیزر، موانع و سیستم
امتیازدهی است. بازی دارای مکانیک کامل حرکت، برخورد و مدیریت وضعیت بازی
میباشد.
اهداف یادگیری این پروژه:
- آشنایی با مفاهیم پایه برنامهنویسی بازیهای دو بعدی
- یادگیری کار با کتابخانه
Pygameو امکانات گرافیکی آن - پیادهسازی سیستم تشخیص برخورد (Collision Detection)
- مدیریت وضعیت بازی و منطق اصلی (Game Loop)
- کار با spriteها و انیمیشنهای ساده
- پیادهسازی سیستم امتیازدهی و ذخیره رکورد
- مدیریت صداها و موسیقی در بازی
ویژگیهای اصلی بازی:
- کنترل روان فضاپیما با کلیدهای صفحهکلید
- حرکت گروهی و هوشمند بیگانگان با الگوی مشخص
- سیستم شلیک دوطرفه (فضاپیما و بیگانگان)
- موانع تخریبپذیر که در اثر برخورد از بین میروند
- کشتی مرموز که به صورت تصادفی ظاهر میشود و امتیاز ویژه دارد
- سیستم مدیریت جانها و وضعیت بازی (شروع، پایان، ریست)
- ذخیره و نمایش امتیاز برتر (High Score)
- رابط کاربری ساده و گرافیک کلاسیک
نصب و پیشنیازها
برای اجرای این پروژه نیاز به پایتون و کتابخانه Pygame دارید:
- Python 3.6+ - نسخههای جدیدتر پایتون
Pygame- کتابخانه بازیسازی پایتون- فونت و فایلهای گرافیکی - برای نمایش صحیح بازی
نصب Pygame با دستور: pip install pygame
ساختار پروژه
پروژه از چندین فایل تشکیل شده است:
main.py- فایل اصلی و حلقه بازیspaceship.py- کلاس فضاپیما و مدیریت شلیکalien.py- کلاس بیگانگان و کشتی مرموزlaser.py- کلاس لیزر و مدیریت پرتابههاobstacle.py- کلاس موانع و سیستم بلوکهاgame.py- کلاس اصلی بازی و منطق کسب و کار
ساختار فایلها
ساختار کامل پوشه پروژه:
SpaceInvaders/
├── main.py
├── game.py
├── spaceship.py
├── alien.py
├── laser.py
├── obstacle.py
├── Graphics/
│ ├── spaceship.png
│ ├── alien_1.png
│ ├── alien_2.png
│ ├── alien_3.png
│ └── mystery.png
├── Sounds/
│ └── laser.ogg
└── Font/
└── monogram.ttf
فایل اصلی (main.py)
حلقه اصلی بازی و مدیریت رویدادها:
import pygame, sys, random
from game import Game
pygame.init()
SCREEN_WIDTH = 750
SCREEN_HEIGHT = 700
OFFSET = 50
GREY = (29, 29, 27)
YELLOW = (243, 216, 63)
font = pygame.font.Font("Font/monogram.ttf", 40)
level_surface = font.render("LEVEL 01", False, YELLOW)
game_over_surface = font.render("GAME OVER", False, YELLOW)
score_text_surface = font.render("SCORE", False, YELLOW)
highscore_text_surface = font.render("HIGH-SCORE", False, YELLOW)
screen = pygame.display.set_mode((SCREEN_WIDTH + OFFSET, SCREEN_HEIGHT + 2*OFFSET))
pygame.display.set_caption("Python Space Invaders")
clock = pygame.time.Clock()
game = Game(SCREEN_WIDTH, SCREEN_HEIGHT, OFFSET)
SHOOT_LASER = pygame.USEREVENT
pygame.time.set_timer(SHOOT_LASER, 300)
MYSTERYSHIP = pygame.USEREVENT + 1
pygame.time.set_timer(MYSTERYSHIP, random.randint(4000,8000))
while True:
#Checking for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == SHOOT_LASER and game.run:
game.alien_shoot_laser()
if event.type == MYSTERYSHIP and game.run:
game.create_mystery_ship()
pygame.time.set_timer(MYSTERYSHIP, random.randint(4000,8000))
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] and game.run == False:
game.reset()
#Updating
if game.run:
game.spaceship_group.update()
game.move_aliens()
game.alien_lasers_group.update()
game.mystery_ship_group.update()
game.check_for_collisions()
#Drawing
screen.fill(GREY)
#UI
pygame.draw.rect(screen, YELLOW, (10, 10, 780, 780), 2, 0, 60, 60, 60, 60)
pygame.draw.line(screen, YELLOW, (25, 730), (775, 730), 3)
if game.run:
screen.blit(level_surface, (570, 740, 50, 50))
else:
screen.blit(game_over_surface, (570, 740, 50, 50))
x = 50
for life in range(game.lives):
screen.blit(game.spaceship_group.sprite.image, (x, 745))
x += 50
screen.blit(score_text_surface, (50, 15, 50, 50))
formatted_score = str(game.score).zfill(5)
score_surface = font.render(formatted_score, False, YELLOW)
screen.blit(score_surface, (50, 40, 50, 50))
screen.blit(highscore_text_surface, (550, 15, 50, 50))
formatted_highscore = str(game.highscore).zfill(5)
highscore_surface = font.render(formatted_highscore, False, YELLOW)
screen.blit(highscore_surface, (625, 40, 50, 50))
game.spaceship_group.draw(screen)
game.spaceship_group.sprite.lasers_group.draw(screen)
for obstacle in game.obstacles:
obstacle.blocks_group.draw(screen)
game.aliens_group.draw(screen)
game.alien_lasers_group.draw(screen)
game.mystery_ship_group.draw(screen)
pygame.display.update()
clock.tick(60)
توضیح بخشهای مختلف کد:
۱. بخش راهاندازی اولیه (Initialization):
pygame.init- راهاندازی تمام ماژولهای Pygame-
SCREEN_WIDTHوSCREEN_HEIGHT- تعیین ابعاد صفحه بازی OFFSET- ایجاد حاشیه برای نمایش اطلاعات بازیscreen = pygame.display.set_mode- ایجاد پنجره بازی-
clock = pygame.time.Clock- ایجاد تایمر برای کنترل نرخ فریم -
game = Game- ایجاد شیء اصلی بازی که تمام منطق بازی را مدیریت میکند
۲. سیستم رویدادها (Event System):
-
SHOOT_LASER- رویداد زمانبندی شده برای شلیک خودکار بیگانگان (هر 300 میلیثانیه) -
MYSTERYSHIP- رویداد زمانبندی شده برای ظهور کشتی مرموز (بین 4 تا 8 ثانیه) pygame.event.get- بررسی تمام رویدادهای اتفاق افتادهpygame.QUIT- مدیریت بسته شدن پنجره بازی-
pygame.key.get_pressed- بررسی وضعیت کلیدهای فشرده شده
۳. حلقه اصلی بازی (Main Game Loop):
- بخش رویدادها - مدیریت کلیکها، کلیدها و رویدادهای سیستم
- بخش بهروزرسانی - محاسبه موقعیتها، بررسی برخوردها و تغییر وضعیت بازی
- بخش رسم - نمایش تمام المانهای بازی روی صفحه
۴. رابط کاربری (UI):
pygame.draw.rect- رسم کادر دور بازیpygame.draw.line- رسم خط جداکننده بخش اطلاعاتfont.render- ایجاد متنهای مختلف برای نمایش اطلاعاتscreen.blit- نمایش متنها و تصاویر روی صفحه- نمایش تعداد جانها با استفاده از تصویر فضاپیما
- فرمتبندی امتیازها با صفرهای ابتدایی (مثلاً ۰۰۱۵۰)
۵. مدیریت وضعیت بازی:
game.run- کنترل وضعیت اجرای بازی (فعال یا غیرفعال)game.reset- ریست کردن بازی هنگام فشردن Space- نمایش "LEVEL 01" هنگام اجرای بازی و "GAME OVER" هنگام پایان بازی
۶. سیستم رسم المانها:
game.spaceship_group.draw- رسم فضاپیماlasers_group.draw- رسم لیزرهای فضاپیماobstacle.blocks_group.draw- رسم موانعaliens_group.draw- رسم بیگانگانalien_lasers_group.draw- رسم لیزرهای بیگانگانmystery_ship_group.draw- رسم کشتی مرموز
۷. کنترل نرخ فریم:
clock.tick(60)- محدود کردن بازی به 60 فریم در ثانیه- این کار باعث میشود بازی در تمام سیستمها با سرعت یکسانی اجرا شود
وظایف اصلی این فایل:
- راهاندازی اولیه
Pygameو ایجاد پنجره بازی - ایجاد تایمر برای شلیک خودکار بیگانگان و ظهور کشتی مرموز
- مدیریت حلقه اصلی بازی و پردازش رویدادها
- هماهنگی بین تمام بخشهای بازی (بهروزرسانی و رسم)
- نمایش رابط کاربری شامل امتیاز، جانها و وضعیت بازی
- کنترل نرخ فریم برای اجرای روان بازی
- مدیریت خروج از بازی و ریست کردن آن
نکات مهم:
- این فایل مانند رهبر ارکستر عمل میکند و تمام بخشها را هماهنگ مینماید
- تمامی منطق بازی در کلاس Game پیادهسازی شده و این فایل فقط آن را مدیریت میکند
-
سیستم رویدادهای
Pygameامکان مدیریت همزمان ورودی کاربر و زمانبندی رویدادها را فراهم میکند - الگوی "Update then Draw" استاندارد صنعت بازیسازی است
کلاس فضاپیما (spaceship.py)
مدیریت حرکت و شلیک فضاپیما:
import pygame
from laser import Laser
class Spaceship(pygame.sprite.Sprite):
def __init__(self, screen_width, screen_height, offset):
super().__init__()
self.offset = offset
self.screen_width = screen_width
self.screen_height = screen_height
self.image = pygame.image.load("Graphics/spaceship.png")
self.rect = self.image.get_rect(midbottom = ((self.screen_width + self.offset)/2, self.screen_height))
self.speed = 6
self.lasers_group = pygame.sprite.Group()
self.laser_ready = True
self.laser_time = 0
self.laser_delay = 300
self.laser_sound = pygame.mixer.Sound("Sounds/laser.ogg")
def get_user_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_SPACE] and self.laser_ready:
self.laser_ready = False
laser = Laser(self.rect.center, 5, self.screen_height)
self.lasers_group.add(laser)
self.laser_time = pygame.time.get_ticks()
self.laser_sound.play()
def update(self):
self.get_user_input()
self.constrain_movement()
self.lasers_group.update()
self.recharge_laser()
def constrain_movement(self):
if self.rect.right > self.screen_width:
self.rect.right = self.screen_width
if self.rect.left < self.offset:
self.rect.left = self.offset
def recharge_laser(self):
if not self.laser_ready:
current_time = pygame.time.get_ticks()
if current_time - self.laser_time >= self.laser_delay:
self.laser_ready = True
def reset(self):
self.rect = self.image.get_rect(midbottom = ((self.screen_width + self.offset)/2, self.screen_height))
self.lasers_group.empty()
توضیح بخشهای مختلف کلاس:
۱. سازنده کلاس (__init__):
-
pygame.sprite.Sprite- ارثبری از کلاس پایه Sprite درPygame self.image- بارگذاری تصویر فضاپیما از فایل-
self.rect- ایجاد مستطیل برخورد و قرار دادن فضاپیما در مرکز پایین صفحه -
self.speed = 6- سرعت حرکت فضاپیما (پیکسل در هر فریم) -
self.lasers_group- گروهی برای مدیریت تمام لیزرهای شلیک شده self.laser_ready- وضعیت آماده بودن برای شلیک بعدی-
self.laser_delay = 300- تأخیر 300 میلیثانیهای بین شلیکها self.laser_sound- بارگذاری صدای شلیک لیزر
۲. متد get_user_input - دریافت ورودی کاربر:
-
pygame.key.get_pressed- بررسی وضعیت تمام کلیدهای صفحهکلید pygame.K_RIGHT- حرکت به راست با افزایش موقعیت Xpygame.K_LEFT- حرکت به چپ با کاهش موقعیت X-
pygame.K_SPACE- شلیک لیزر زمانی که کلید Space فشرده شده و شلیک آماده باشد - هنگام شلیک: ایجاد شیء لیزر جدید، اضافه کردن به گروه لیزرها، ثبت زمان شلیک و پخش صدا
۳. متد update - بهروزرسانی در هر فریم:
self.get_user_input- بررسی ورودی کاربر-
self.constrain_movement- محدود کردن حرکت در مرزهای صفحه -
self.lasers_group.update- بهروزرسانی موقعیت تمام لیزرها self.recharge_laser- شارژ مجدد سیستم شلیک
۴. متد constrain_movement - محدودیت حرکت:
- جلوگیری از خروج فضاپیما از سمت راست صفحه
- جلوگیری از خروج فضاپیما از سمت چپ صفحه (با در نظر گرفتن offset)
- این متد تضمین میکند فضاپیما همیشه در منطقه بازی قابل مشاهده باقی بماند
۵. متد recharge_laser - شارژ مجدد شلیک:
- بررسی زمان سپری شده از آخرین شلیک
-
pygame.time.get_ticks- دریافت زمان فعلی در میلیثانیه - اگر زمان سپری شده بیشتر از تأخیر شلیک باشد، سیستم شلیک مجدداً فعال میشود
- این مکانیزم از شلیک پی در پی و نامتعادل جلوگیری میکند
۶. متد reset - بازنشانی موقعیت:
- بازگرداندن فضاپیما به موقعیت اولیه در مرکز پایین صفحه
-
پاک کردن تمام لیزرهای موجود با
self.lasers_group.empty - این متد هنگام شروع بازی جدید یا از دست دادن جان فراخوانی میشود
ویژگیهای کلیدی:
- حرکت روان - با کلیدهای چپ و راست صفحهکلید
- سیستم شلیک متعادل - با کلید Space و تأخیر 300 میلیثانیهای بین شلیکها
- محدودیت حرکت - جلوگیری از خروج فضاپیما از مرزهای صفحه
- مدیریت صدا - پخش صدای شلیک هنگام پرتاب لیزر
- مدیریت گروه لیزر - استفاده از Sprite Group برای مدیریت کارآمد لیزرها
- قابلیت بازنشانی - بازگشت به موقعیت اولیه هنگام ریست بازی
نکات فنی مهم:
- این کلاس از الگوی طراحی "Composition" استفاده میکند (دارای گروه لیزرها است)
-
استفاده از
pygame.sprite.Groupبرای مدیریت خودکار بهروزرسانی و رسم لیزرها - سیستم زمانسنجی برای ایجاد تأخیر بین شلیکها که برای تعادل بازی ضروری است
- مستقل بودن از رزولوشن صفحه - با استفاده از پارامترهای screen_width و screen_height
- مدیریت حافظه با پاک کردن لیزرها هنگام ریست کردن بازی
تعامل با سایر بخشهای بازی:
- با کلاس
Laserبرای ایجاد پرتابهها تعامل دارد - توسط کلاس
Gameمدیریت و بهروزرسانی میشود -
لیزرهای آن در کلاس
Gameبرای تشخیص برخورد بررسی میشوند - موقعیت آن برای تشخیص برخورد با لیزرهای بیگانگان و خود بیگانگان استفاده میشود
کلاس بیگانگان (alien.py)
مدیریت حرکت بیگانگان و کشتی مرموز:
import pygame, random
class Alien(pygame.sprite.Sprite):
def __init__(self, type, x, y):
super().__init__()
self.type = type
path = f"Graphics/alien_{type}.png"
self.image = pygame.image.load(path)
self.rect = self.image.get_rect(topleft = (x, y))
def update(self, direction):
self.rect.x += direction
class MysteryShip(pygame.sprite.Sprite):
def __init__(self, screen_width, offset):
super().__init__()
self.screen_width = screen_width
self.offset = offset
self.image = pygame.image.load("Graphics/mystery.png")
x = random.choice([self.offset/2, self.screen_width + self.offset - self.image.get_width()])
if x == self.offset/2:
self.speed = 3
else:
self.speed = -3
self.rect = self.image.get_rect(topleft = (x, 90))
def update(self):
self.rect.x += self.speed
if self.rect.right > self.screen_width + self.offset/2:
self.kill()
elif self.rect.left < self.offset/2:
self.kill()
توضیح بخشهای مختلف کلاس:
۱. کلاس Alien - بیگانگان معمولی:
سازنده کلاس (__init__):
-
self.type- نوع بیگانه (1، 2 یا 3) که بر اساس سطر قرارگیری تعیین میشود -
path = f"Graphics/alien_{type}.png"- ایجاد مسیر دایnamic برای بارگذاری تصویر مناسب self.image- بارگذاری تصویر بیگانه بر اساس نوع آنself.rect- ایجاد مستطیل برخورد در موقعیت مشخص شده
متد update:
-
direction- پارامتر جهت حرکت که از کلاس Game دریافت میشود (1 برای راست، -1 برای چپ) -
self.rect.x += direction- حرکت افقی بیگانه بر اساس جهت تعیین شده - این متد ساده باعث میشود تمام بیگانگان به صورت هماهنگ حرکت کنند
۲. کلاس MysteryShip - کشتی مرموز:
سازنده کلاس (__init__):
-
self.screen_widthوself.offset- ذخیره ابعاد صفحه برای محاسبات موقعیت self.image- بارگذاری تصویر کشتی مرموز-
random.choice()- انتخاب تصادفی نقطه شروع از دو سمت صفحه -
x = random.choice([self.offset/2, ...])- شروع از چپ یا راست صفحه -
self.speed- تعیین سرعت بر اساس جهت حرکت (مثبت برای راست، منفی برای چپ) -
self.rect- قرار دادن کشتی در موقعیت Y ثابت (90) و X تصادفی
متد update:
-
self.rect.x += self.speed- حرکت افقی کشتی با سرعت ثابت -
self.rect.right > self.screen_width + self.offset/2- بررسی خروج از سمت راست -
self.rect.left < self.offset/2- بررسی خروج از سمت چپ self.kill()- حذف خودکار کشتی هنگام خروج از صفحه
انواع بیگانگان و امتیاز آنها:
- نوع 3 (ردیف اول) - بالاترین امتیاز (300 امتیاز)، ظاهر متفاوت
- نوع 2 (ردیف دوم و سوم) - امتیاز متوسط (200 امتیاز)
- نوع 1 (ردیف چهارم و پنجم) - کمترین امتیاز (100 امتیاز)
- کشتی مرموز - امتیاز ویژه (500 امتیاز)، ظاهر تصادفی
ویژگیهای کلیدی:
- سه نوع مختلف بیگانه - با ظاهر و امتیاز متفاوت
- حرکت گروهی هماهنگ - تمام بیگانگان با هم حرکت میکنند
- کشتی مرموز ویژه - با حرکت تصادفی و امتیاز بالا
- سیستم حذف خودکار - کشتی مرموز پس از خروج از صفحه حذف میشود
- موقعیتدهی پویا - استفاده از پارامترها برای استقلال از رزولوشن
مکانیزم حرکت بیگانگان:
- بیگانگان به صورت گروهی و در الگوی مشخص حرکت میکنند
- هنگام برخورد به لبههای صفحه، جهت حرکت معکوس شده و به پایین حرکت میکنند
- این الگو شبیه به بازی اصلی Space Invaders است
- سرعت حرکت با کاهش تعداد بیگانگان افزایش مییابد
مکانیزم کشتی مرموز:
- به صورت تصادفی بین 4 تا 8 ثانیه ظاهر میشود
- همیشه از بالا و در موقعیت Y ثابت حرکت میکند
- میتواند از چپ به راست یا راست به چپ حرکت کند
- پس از عبور کامل از صفحه، به طور خودکار حذف میشود
- شلیک به آن امتیاز بالایی (500) به بازیکن میدهد
نکات فنی مهم:
- استفاده از f-string برای ایجاد مسیرهای داینامیک به فایلهای تصویری
- کلاس Alien بسیار ساده نگه داشته شده زیرا منطق پیچیده در کلاس Game قرار دارد
- کشتی مرموز از الگوی "Fire and Forget" استفاده میکند - پس از ایجاد خودکار مدیریت میشود
-
استفاده از
self.killبرای مدیریت حافظه و جلوگیری از نشت memory - موقعیت Y ثابت کشتی مرموز (90) آن را در منطقه امن بالای صفحه قرار میدهد
تعامل با سایر بخشهای بازی:
- توسط کلاس
Gameایجاد و مدیریت میشوند - موقعیت آنها برای تشخیص برخورد با لیزرهای فضاپیما بررسی میشود
- بیگانگان میتوانند لیزر شلیک کنند (این قابلیت در کلاس Game پیادهسازی شده)
- برخورد با فضاپیما یا رسیدن به پایین صفحه باعث پایان بازی میشود
کلاس لیزر (laser.py)
مدیریت پرتابهها و برخورد:
import pygame
class Laser(pygame.sprite.Sprite):
def __init__(self, position, speed, screen_height):
super().__init__()
self.image = pygame.Surface((4,15))
self.image.fill((243, 216, 63))
self.rect = self.image.get_rect(center = position)
self.speed = speed
self.screen_height = screen_height
def update(self):
self.rect.y -= self.speed
if self.rect.y > self.screen_height + 15 or self.rect.y < 0:
self.kill()
توضیح بخشهای مختلف کلاس:
سازنده کلاس (__init__):
position- موقعیت شروع لیزر (مرکز فضاپیما یا بیگانه)-
speed- سرعت حرکت لیزر (مثبت برای بالا، منفی برای پایین) screen_height- ارتفاع صفحه برای تشخیص خروج از مرزها-
self.image = pygame.Surface((4,15))- ایجاد یک سطح مستطیلی برای لیزر (4 پیکسل عرض، 15 پیکسل ارتفاع) -
self.image.fill((243, 216, 63))- رنگآمیزی لیزر با رنگ زرد (کد RGB: 243,216,63) -
self.rect = self.image.get_rect(center = position)- ایجاد مستطیل برخورد در مرکز موقعیت داده شده
متد update - بهروزرسانی در هر فریم:
self.rect.y -= self.speed- حرکت لیزر در جهت عمودی- اگر speed مثبت باشد: لیزر به سمت بالا حرکت میکند (شلیک فضاپیما)
- اگر speed منفی باشد: لیزر به سمت پایین حرکت میکند (شلیک بیگانگان)
-
self.rect.y > self.screen_height + 15- بررسی خروج از پایین صفحه self.rect.y < 0- بررسی خروج از بالای صفحهself.kill()- حذف خودکار لیزر هنگام خروج از صفحه
انواع لیزرها در بازی:
لیزر فضاپیما:
- سرعت: 5 (مثبت - به سمت بالا)
- موقعیت شروع: مرکز فضاپیما
- رنگ: زرد (243,216,63)
- کاربرد: نابودی بیگانگان و کشتی مرموز
لیزر بیگانگان:
- سرعت: -6 (منفی - به سمت پایین)
- موقعیت شروع: مرکز یک بیگانه تصادفی
- رنگ: زرد (همان رنگ لیزر فضاپیما)
- کاربرد: آسیب به فضاپیما و تخریب موانع
ویژگیهای کلیدی:
- ایجاد پویا - لیزرها در موقعیتهای مختلف ایجاد میشوند
- حرکت خودکار - پس از ایجاد به صورت خودکار در جهت مشخص حرکت میکنند
- حذف خودکار - هنگام خروج از صفحه به طور خودکار حذف میشوند
- مدیریت سرعت - سرعت متفاوت برای لیزرهای فضاپیما و بیگانگان
- ساده و کارآمد - طراحی مینیمال برای عملکرد بهینه
مکانیزم حرکت و حذف:
- لیزرها در هر فریم به اندازه سرعت تعیین شده جابهجا میشوند
- بررسی مرزهای صفحه در هر فریم انجام میشود
- حذف لیزرها از حافظه هنگام خروج از صفحه برای جلوگیری از مصرف بیرویه منابع
- مقدار 15 پیکسل در بررسی پایین صفحه به عنوان buffer در نظر گرفته شده است
نکات فنی مهم:
-
استفاده از
pygame.Surfaceبرای ایجاد گرافیک ساده و سبک - مستطیل برخورد هممرکز با تصویر لیزر ایجاد میشود
- سرعت به عنوان پارامتر دریافت میشود که امکان استفاده چندگانه از کلاس را فراهم میکند
-
استفاده از
self.kill()برای حذف از گروه spriteها و آزادسازی حافظه - کلاس کاملاً مستقل از منشأ شلیک - میتواند توسط هر spriteای استفاده شود
مزایای طراحی:
- قابلیت استفاده مجدد - یک کلاس برای تمام انواع لیزرها
- کارایی - گرافیک ساده و منطق مینیمال
- انعطافپذیری - پارامترهای قابل تنظیم برای موقعیت و سرعت
- مدیریت حافظه - حذف خودکار از حافظه
-
سازگاری - کار با سیستم sprite گروههای
Pygame
تعامل با سایر بخشهای بازی:
- توسط کلاس
Spaceshipبرای شلیک فضاپیما ایجاد میشود - توسط کلاس
Gameبرای شلیک بیگانگان ایجاد میشود -
در کلاس
Gameبرای تشخیص برخورد با بیگانگان، موانع و فضاپیما بررسی میشود - به صورت گروهی در
pygame.sprite.Groupمدیریت میشوند -
سیستم بهروزرسانی خودکار
Pygameمتد update آنها را فراخوانی میکند
بهینهسازیهای انجام شده:
- عدم استفاده از تصاویر پیچیده - استفاده از Surface ساده
- حذف سریع از حافظه پس از خروج از صفحه
- عدم ذخیره اطلاعات اضافی - فقط دادههای ضروری نگهداری میشوند
-
استفاده از سیستم sprite گروههای
Pygameبرای مدیریت کارآمد
کلاس موانع (obstacle.py)
سیستم شبکهای برای ایجاد موانع:
import pygame
class Block(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((3,3))
self.image.fill((243,216,63))
self.rect = self.image.get_rect(topleft = (x,y))
grid = [
[0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1],
[1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1],
[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1]]
class Obstacle:
def __init__(self, x, y):
self.blocks_group = pygame.sprite.Group()
for row in range(len(grid)):
for column in range(len(grid[0])):
if grid[row][column] == 1:
pos_x = x + column * 3
pos_y = y + row * 3
block = Block(pos_x, pos_y)
self.blocks_group.add(block)
توضیح بخشهای مختلف کد:
۱. کلاس Block - بلوکهای تشکیلدهنده موانع:
سازنده کلاس (__init__):
-
self.image = pygame.Surface((3,3))- ایجاد یک سطح مربعی 3x3 پیکسلی -
self.image.fill((243,216,63))- رنگآمیزی بلوک با رنگ زرد (همان رنگ لیزرها) -
self.rect = self.image.get_rect(topleft = (x,y))- ایجاد مستطیل برخورد در موقعیت مشخص - هر بلوک یک sprite مستقل است که میتواند به طور جداگانه حذف شود
۲. شبکه تعریف شکل موانع (grid):
- یک لیست دو بعدی 13 سطری و 23 ستونی
- 0 - نشاندهنده فضای خالی (بدون بلوک)
- 1 - نشاندهنده وجود بلوک در آن موقعیت
- الگوی شبکه شبیه به یک قوس یا طاق طراحی شده است
- لبههای خارجی نازکتر و مرکز ضخیمتر است
۳. کلاس Obstacle - مدیریت کل مانع:
سازنده کلاس (__init__):
-
self.blocks_group = pygame.sprite.Group- ایجاد گروه برای مدیریت تمام بلوکهای این مانع - حلقههای تو در تو برای پیمایش شبکه دو بعدی
-
for row in range(len(grid))- پیمایش سطرهای شبکه (0 تا 12) -
for column in range(len(grid[0]))- پیمایش ستونهای شبکه (0 تا 22) -
if grid[row][column] == 1- اگر در این موقعیت باید بلوک وجود داشته باشد -
pos_x = x + column * 3- محاسبه موقعیت X بلوک با فاصله 3 پیکسل -
pos_y = y + row * 3- محاسبه موقعیت Y بلوک با فاصله 3 پیکسل block = Block(pos_x, pos_y)- ایجاد شیء بلوک جدید-
self.blocks_group.add(block)- اضافه کردن بلوک به گروه
مشخصات فنی موانع:
- ابعاد کل مانع: 69 پیکسل عرض (23×3) × 39 پیکسل ارتفاع (13×3)
- تعداد بلوکها: 185 بلوک در هر مانع (بر اساس شمارش 1ها در شبکه)
- فاصله بین بلوکها: 0 پیکسل (بلوکها کاملاً به هم چسبیدهاند)
- تعداد موانع در بازی: 4 مانع که به طور مساوی در صفحه توزیع شدهاند
الگوی طراحی شبکه:
- سطرهای 0-3: لبه بالایی با شیب ملایم
- سطرهای 4-9: بدنه اصلی و ضخیم مانع
- سطرهای 10-12: پایه نازک با فضای خالی در مرکز
- این طراحی باعث میشود مانع به تدریج و از لبهها تخریب شود
مکانیزم کار:
- استفاده از شبکه 2D - برای تعریف دقیق شکل موانع
- ایجاد بلوکهای کوچک - هر مانع از صدها بلوک 3x3 پیکسلی تشکیل شده
- سیستم تخریب تدریجی - هر برخورد لیزر فقط یک بلوک را حذف میکند
- موقعیتدهی خودکار - موانع به طور خودکار در موقعیتهای صحیح قرار میگیرند
ویژگیهای کلیدی:
- تخریب پذیری واقعی - موانع به تدریج و به صورت بصری تخریب میشوند
- کارایی بالا - استفاده از sprite گروهها برای مدیریت بهینه
- قابلیت تنظیم آسان - تغییر شکل موانع با ویرایش ساده شبکه
- گرافیک ساده ولی مؤثر - بلوکهای کوچک که در کنار هم شکل پیچیده ایجاد میکنند
- فیزیک واقعی - لیزرها فقط بلوکهای برخوردی را حذف میکنند
نکات فنی مهم:
- استفاده از الگوی Composite - مانع از بلوکهای کوچک تشکیل شده
-
مدیریت کارآمد با
pygame.sprite.Group- بهروزرسانی و رسم خودکار - محاسبات موقعیت بر اساس ضرب - هر ستون 3 پیکسل و هر سطر 3 پیکسل فاصله دارد
- شبکه به عنوان داده ثابت (constant) تعریف شده - قابل تغییر بدون تأثیر بر منطق
- هر مانع مستقل است - تخریب یک مانع بر دیگران تأثیری ندارد
تعامل با سایر بخشهای بازی:
-
توسط کلاس
Gameایجاد و در موقعیتهای مناسب قرار میگیرند - با لیزرهای فضاپیما و بیگانگان برخورد میکنند
- برخورد با لیزر باعث حذف بلوک برخوردی از گروه میشود
- بیگانگان میتوانند با موانع برخورد کرده و بلوکها را حذف کنند
- موانع به عنوان سد بین فضاپیما و بیگانگان عمل میکنند
مزایای این طراحی:
- بازیکن دوستی - موانع استراتژی محافظت را به بازی اضافه میکنند
- واقعگرایی - تخریب تدریجی مشابه بازیهای کلاسیک
- تعادل بازی - موانع هم به نفع بازیکن و هم به نفع دشمنان عمل میکنند
- قابلیت توسعه - آسانی ایجاد شکلهای مختلف مانع با تغییر شبکه
- کارایی - با وجود تعداد زیاد بلوکها، به دلیل سادگی عملکرد خوبی دارد
کلاس بازی (game.py)
منطق اصلی و مدیریت وضعیت بازی:
import pygame, random
from spaceship import Spaceship
from obstacle import Obstacle
from obstacle import grid
from alien import Alien
from laser import Laser
from alien import MysteryShip
class Game:
def __init__(self, screen_width, screen_height, offset):
self.screen_width = screen_width
self.screen_height = screen_height
self.offset = offset
self.spaceship_group = pygame.sprite.GroupSingle()
self.spaceship_group.add(Spaceship(self.screen_width, self.screen_height, self.offset))
self.obstacles = self.create_obstacles()
self.aliens_group = pygame.sprite.Group()
self.create_aliens()
self.aliens_direction = 1
self.alien_lasers_group = pygame.sprite.Group()
self.mystery_ship_group = pygame.sprite.GroupSingle()
self.lives = 3
self.run = True
self.score = 0
self.highscore = 0
self.explosion_sound = pygame.mixer.Sound("Sounds/explosion.ogg")
self.load_highscore()
pygame.mixer.music.load("Sounds/music.ogg")
pygame.mixer.music.play(-1)
def create_obstacles(self):
obstacle_width = len(grid[0]) * 3
gap = (self.screen_width + self.offset - (4 * obstacle_width))/5
obstacles = []
for i in range(4):
offset_x = (i + 1) * gap + i * obstacle_width
obstacle = Obstacle(offset_x, self.screen_height - 100)
obstacles.append(obstacle)
return obstacles
def create_aliens(self):
for row in range(5):
for column in range(11):
x = 75 + column * 55
y = 110 + row * 55
if row == 0:
alien_type = 3
elif row in (1,2):
alien_type = 2
else:
alien_type = 1
alien = Alien(alien_type, x + self.offset/2, y)
self.aliens_group.add(alien)
def move_aliens(self):
self.aliens_group.update(self.aliens_direction)
alien_sprites = self.aliens_group.sprites()
for alien in alien_sprites:
if alien.rect.right >= self.screen_width + self.offset/2:
self.aliens_direction = -1
self.alien_move_down(2)
elif alien.rect.left <= self.offset/2:
self.aliens_direction = 1
self.alien_move_down(2)
def alien_move_down(self, distance):
if self.aliens_group:
for alien in self.aliens_group.sprites():
alien.rect.y += distance
def alien_shoot_laser(self):
if self.aliens_group.sprites():
random_alien = random.choice(self.aliens_group.sprites())
laser_sprite = Laser(random_alien.rect.center, -6, self.screen_height)
self.alien_lasers_group.add(laser_sprite)
def create_mystery_ship(self):
self.mystery_ship_group.add(MysteryShip(self.screen_width, self.offset))
def check_for_collisions(self):
#Spaceship
if self.spaceship_group.sprite.lasers_group:
for laser_sprite in self.spaceship_group.sprite.lasers_group:
aliens_hit = pygame.sprite.spritecollide(laser_sprite, self.aliens_group, True)
if aliens_hit:
self.explosion_sound.play()
for alien in aliens_hit:
self.score += alien.type * 100
self.check_for_highscore()
laser_sprite.kill()
if pygame.sprite.spritecollide(laser_sprite, self.mystery_ship_group, True):
self.score += 500
self.explosion_sound.play()
self.check_for_highscore()
laser_sprite.kill()
for obstacle in self.obstacles:
if pygame.sprite.spritecollide(laser_sprite, obstacle.blocks_group, True):
laser_sprite.kill()
#Alien Lasers
if self.alien_lasers_group:
for laser_sprite in self.alien_lasers_group:
if pygame.sprite.spritecollide(laser_sprite, self.spaceship_group, False):
laser_sprite.kill()
self.lives -= 1
if self.lives == 0:
self.game_over()
for obstacle in self.obstacles:
if pygame.sprite.spritecollide(laser_sprite, obstacle.blocks_group, True):
laser_sprite.kill()
if self.aliens_group:
for alien in self.aliens_group:
for obstacle in self.obstacles:
pygame.sprite.spritecollide(alien, obstacle.blocks_group, True)
if pygame.sprite.spritecollide(alien, self.spaceship_group, False):
self.game_over()
def game_over(self):
self.run = False
def reset(self):
self.run = True
self.lives = 3
self.spaceship_group.sprite.reset()
self.aliens_group.empty()
self.alien_lasers_group.empty()
self.create_aliens()
self.mystery_ship_group.empty()
self.obstacles = self.create_obstacles()
self.score = 0
def check_for_highscore(self):
if self.score > self.highscore:
self.highscore = self.score
with open("highscore.txt", "w") as file:
file.write(str(self.highscore))
def load_highscore(self):
try:
with open("highscore.txt", "r") as file:
self.highscore = int(file.read())
except FileNotFoundError:
self.highscore = 0
توضیح بخشهای مختلف کلاس:
۱. سازنده کلاس (__init__) - راهاندازی اولیه:
-
self.spaceship_group- گروه تکعضوی برای مدیریت فضاپیما self.obstacles- لیستی از ۴ مانع بازی-
self.aliens_group- گروهی برای مدیریت تمام بیگانگان (۵۵ بیگانه) -
self.aliens_direction = 1- جهت اولیه حرکت بیگانگان (1=راست, -1=چپ) -
self.alien_lasers_group- گروهی برای مدیریت لیزرهای بیگانگان -
self.mystery_ship_group- گروه تکعضوی برای کشتی مرموز self.lives = 3- تعداد جانهای بازیکنself.run = True- وضعیت اجرای بازیself.score = 0- امتیاز فعلی بازیکنself.highscore = 0- رکورد امتیازself.explosion_sound- صدای انفجار برای برخوردها-
pygame.mixer.music.play(-1)- پخش موسیقی زمینه به صورت حلقهای
۲. متد create_obstacles - ایجاد موانع:
-
obstacle_width = len(grid[0]) * 3- محاسبه عرض هر مانع (23 ستون × 3 پیکسل = 69 پیکسل) -
gap = (screen_width + offset - (4 * obstacle_width))/5- محاسبه فاصله بین موانع - ایجاد ۴ مانع با فاصلههای مساوی در پایین صفحه
-
موقعیت Y ثابت:
self.screen_height - 100(100 پیکسل از پایین)
۳. متد create_aliens - ایجاد بیگانگان:
- ۵ سطر و ۱۱ ستون بیگانه (مجموعاً ۵۵ بیگانه)
-
x = 75 + column * 55- فاصله افقی ۵۵ پیکسل بین بیگانگان y = 110 + row * 55- فاصله عمودی ۵۵ پیکسل بین سطرها-
تعیین نوع بیگانه بر اساس سطر:
- سطر ۰: نوع ۳ (امتیاز ۳۰۰)
- سطرهای ۱ و ۲: نوع ۲ (امتیاز ۲۰۰)
- سطرهای ۳ و ۴: نوع ۱ (امتیاز ۱۰۰)
۴. متد move_aliens - حرکت گروهی بیگانگان:
-
self.aliens_group.update(self.aliens_direction)- بهروزرسانی موقعیت تمام بیگانگان - بررسی برخورد با لبههای صفحه
- اگر به لبه راست برسند: تغییر جهت به چپ و حرکت به پایین
- اگر به لبه چپ برسند: تغییر جهت به راست و حرکت به پایین
self.alien_move_down(2)- حرکت ۲ پیکسل به پایین
۵. متد alien_shoot_laser - شلیک بیگانگان:
- انتخاب تصادفی یک بیگانه از بین بیگانگان موجود
- ایجاد لیزر با سرعت -۶ (به سمت پایین)
- اضافه کردن لیزر به گروه لیزرهای بیگانگان
۶. متد check_for_collisions - سیستم تشخیص برخورد:
برخورد لیزرهای فضاپیما:
- با بیگانگان: حذف بیگانه، اضافه کردن امتیاز، پخش صدا
- با کشتی مرموز: حذف کشتی، اضافه کردن ۵۰۰ امتیاز، پخش صدا
- با موانع: حذف بلوک برخوردی
برخورد لیزرهای بیگانگان:
- با فضاپیما: کاهش جان، بررسی پایان بازی
- با موانع: حذف بلوک برخوردی
برخورد مستقیم بیگانگان:
- با موانع: حذف بلوکهای برخوردی
- با فضاپیما: پایان فوری بازی
۷. متدهای مدیریت وضعیت بازی:
game_over- توقف بازیreset- بازنشانی کامل بازی به حالت اولیهcheck_for_highscore- بررسی و ذخیره رکورد جدیدload_highscore- بارگذاری رکورد از فایل
سیستم امتیازدهی:
- بیگانه نوع ۱: ۱۰۰ امتیاز
- بیگانه نوع ۲: ۲۰۰ امتیاز
- بیگانه نوع ۳: ۳۰۰ امتیاز
- کشتی مرموز: ۵۰۰ امتیاز
-
امتیازها بر اساس نوع بیگانه محاسبه میشوند:
alien.type * 100
مدیریت وضعیت بازی:
self.run- کنترل اصلی اجرای بازیself.lives- مدیریت جانهای بازیکن- پایان بازی در دو حالت: اتمام جانها یا برخورد مستقیم بیگانگان با فضاپیما
- قابلیت ریست کامل بازی با حفظ رکورد
سیستم صداها:
- موسیقی زمینه به صورت حلقهای و پیوسته
- صدای انفجار برای برخوردهای موفق
- صدای شلیک لیزر در کلاس Spaceship مدیریت میشود
مدیریت فایلها:
- ذخیره و بارگذاری رکورد از فایل
highscore.txt - مدیریت خطا در صورت عدم وجود فایل
- فرمت ساده ذخیرهسازی (متن ساده)
نکات فنی مهم:
-
استفاده از
GroupSingleبرای المانهای منحصربهفرد (فضاپیما، کشتی مرموز) -
استفاده از
spritecollideبا پارامتر True برای حذف خودکار spriteهای برخوردی - مدیریت حافظه با متدهای
emptyوkill - الگوی طراحی "مرکز کنترل" - تمام منطق بازی در یک کلاس متمرکز شده
- استفاده از ثابتهای عددی برای تعادل بازی (فاصلهها، سرعتها، امتیازها)
وظایف اصلی این کلاس:
- مدیریت کامل وضعیت بازی (شروع، اجرا، پایان)
- بررسی و پردازش تمام برخوردهای ممکن در بازی
- مدیریت امتیازات، رکوردها و جانهای بازیکن
- کنترل حرکت گروهی و هوشمند بیگانگان
- ایجاد و موقعیتدهی موانع و بیگانگان
- هماهنگی بین تمام المانهای بازی
- مدیریت صداها و موسیقی بازی
- ذخیره و بازیابی اطلاعات بازی
تعامل با سایر بخشها:
- ایجاد و مدیریت تمام کلاسهای دیگر
- فراخوانی متدهای update برای بهروزرسانی موقعیتها
- بررسی برخورد بین تمام گروههای sprite
- ارائه اطلاعات وضعیت بازی به فایل
main.pyبرای نمایش - واکنش به رویدادهای ایجاد شده در
main.py
ویژگیهای بازی
- سیستم حرکت روان فضاپیما با کلیدهای چپ و راست
- شلیک لیزر با تأخیر برای تعادل بازی
- حرکت گروهی بیگانگان با الگوی مشخص
- کشتی مرموز با حرکت تصادفی و امتیاز ویژه
- سیستم موانع تخریبپذیر
- مدیریت برخورد و تشخیص collision
- سیستم امتیازدهی و highscore
- مدیریت جانها و وضعیت بازی
نحوه اجرا
برای اجرای بازی:
- تمام فایلها را در یک پوشه ذخیره کنید
- پوشههای Graphics، Sounds و Font را ایجاد کنید
- فایلهای گرافیکی و صوتی را در پوشههای مربوطه قرار دهید
- دستور زیر را اجرا کنید:
python main.py
کنترلهای بازی: کلیدهای چپ/راست برای حرکت، Space برای شلیک
دریافت کد کامل از گیتهاب
برای دسترسی به کد کامل پروژه میتوانید از ریپوزیتوری گیتهاب استفاده کنید:
git clone https://github.com/your-username/space-invaders-pygame.git
cd space-invaders-pygame
python main.py
ویژگیهای ریپوزیتوری:
- کد کامل و سازمانیافته تمام کلاسها
- فایلهای گرافیکی و صوتی مورد نیاز
- مستندات راهنمای نصب و اجرا
- قابلیت توسعه و مشارکت (Pull Request)
لینک دسترسی به ریپوزیتوری:
https://github.com/your-username/space-invaders-pygame
توسعه و بهبود
برای توسعه بازی میتوانید:
- اضافه کردن سطحهای مختلف با الگوهای حرکت متنوع
- افزودن قدرتهای ویژه (power-ups)
- ایجاد انیمیشنهای انفجار و اثرات بصری
- اضافه کردن موسیقی و افکتهای صوتی بیشتر
- ایجاد سیستم ذخیره امتیازات
- افزودن دشمنان با رفتارهای هوشمندتر
منابع و مستندات
-
مستندات Pygame:
Python Documentation -
آموزشهای بازیسازی:
Tutle Documentation -
منابع گرافیکی رایگان:
https://opengameart.org/ -
کد کامل پروژه:
https://opengameart.org/
📋 خلاصه جامع پروژه
🎯 هدف پروژه
پیادهسازی کامل بازی کلاسیک Space Invaders با پایتون و
Pygame به عنوان یک پروژه آموزشی برای یادگیری مفاهیم
بازیسازی
🏗️ معماری سیستم
- 6 کلاس اصلی با مسئولیتهای مجزا
- سیستم ماژولار برای توسعه پذیری
- مدیریت وضعیت بازی یکپارچه
⚡ فناوریها
- Python 3.6+
- Pygame Library
- Sprite-based Graphics
- Event-driven Architecture
🎮 مکانیکهای اصلی
- حرکت و شلیک فضاپیما
- حرکت گروهی بیگانگان
- سیستم برخورد پیشرفته
- مدیریت امتیازات
📊 نکات فنی کلیدی
ساختار کد
کد به صورت شیگرا و ماژولار طراحی شده که امکان توسعه و نگهداری آسان را فراهم میکند
بهینهسازی
استفاده از Groupهای Pygame برای مدیریت کارآمد spriteها و
سیستم حذف خودکار اشیاء خارج از صفحه
قابلیت توسعه
ساختار پروژه امکان اضافه کردن سطوح جدید، قدرتهای ویژه و دشمنان متنوع را فراهم میکند
🎓 مهارتهای کسب شده
- برنامهنویسی شیگرا در پایتون
- کار با کتابخانه
Pygameو مدیریت رویدادها - پیادهسازی سیستم فیزیک و برخورد
- مدیریت وضعیت بازی و منطق کسب و کار
- کار با فایلهای چندرسانهای و فونت
- ایجاد رابط کاربری برای بازی
💡 نتیجهگیری
این پروژه نه تنها یک پیادهسازی کامل از
Space Invaders ارائه میدهد، بلکه پایهای مستحکم برای ساخت
بازیهای پیچیدهتر با پایتون ایجاد میکند. ساختار کد و معماری استفاده
شده قابل تعمیم به پروژههای بازیسازی بزرگتر میباشد.
گام بعدی: میتوانید با اضافه کردن ویژگیهای پیشنهادی در بخش توسعه، مهارتهای خود را گسترش دهید و بازی را به سطح حرفهایتری ارتقا دهید.