مرور پروژه و ساختن اوّلین موجود بیگانه

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

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

پیش از نوشتن هر کد جدیدی برای افزودن ناوگانی از بیگانگان به صفحه اجازه دهید که به پروژه نگاهی بیندازیم و طرح خود را به‌روز کنیم.

مرور پروژه

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

  • کد خود را بررسی می‌کنیم تا معلوم کنیم پیش از پیاده‌سازی ویژگی‌های جدید نیاز به بازآرایی دارد یا نه.
  • یک موجود بیگانه‌ی تکّی به گوشه‌ی بالا-چپ صفحه با فاصله‌گذاریِ مناسب در اطراف آن اضافه می‌کنیم.
  • بر اساس فاصله‌ی اطراف موجود اوّل و اندازه‌ی کلّی صفحه تعداد موجوداتی را که روی صفحه جا می‌گیرند حساب می‌کنیم. حلقه‌ای خواهیم نوشت که موجودات بیگانه را برای پر کردن بخش بالایی صفحه تولید کند.
  • ناوگان را به سمت پایین و اطراف حرکت می‌دهیم تا جایی که تمام ناوگان نابود شود، یکی از بیگانگان به سفینه بخورد و یا یکی از آن‌ها به زمین برسد. اگر تمام ناوگان نابود شود یک ناوگان جدید می‌سازیم. اگر یکی از موجودات بیگانه به سفینه یا زمین بخورد سفینه را نابود می‌کنیم و یک ناوگان جدید می‌سازیم.
  • تعداد سفینه‌های قابل استفاده‌ی هر بازیکن را محدود می‌کنیم و با تمام شدنِ سفینه‌های اختصاص‌یافته به بازیکن بازی را به پایان می‌رسانیم.

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

شما باید هنگام شروع کار بر روی مجموعه‌ی جدید ویژگی‌ها در یک پروژه، کدهای قبلی را هم مرور کنید. از آن‌جایی‌که هر فاز جدید معمولاً پروژه را پیچیده‌تر می‌کند بهتر است که کدهای درهم‌ریخته یا غیربهینه را اصلاح کنیم. ما همزمان با پیش رفتنِ کار کد را بازآرایی کردیم بنابراین فعلاً کدِ نیازمندِ بازآرایی وجود ندارد.

ساختن اوّلین موجود بیگانه

قرار دادن یک موجود بیگانه روی صفحه مثلِ قرار دادن سفینه روی صفحه است. کلاسی به نام Alien رفتار هر موجود بیگانه را کنترل می‌کند که ساختار آن را شبیه کلاس Ship ایجاد می‌کنیم. برای سادگی به استفاده از تصویر‌های بیت‌مپ ادامه می‌دهیم. خودتان می‌توانید تصویری برای موجودات بیگانه پیدا کنید یا از نمونه‌ی نشان داده شده در شکل ۱۳-۱ استفاده کنید که از منابع کتاب به آدرس https://nostarch.com/pythoncrashcourse2e دریافت کنید. رنگ پس‌زمینه‌ی این تصویر خاکستری است که به رنگ پس‌زمینه‌ی صفحه می‌خورد. مطمئن شوید عکس انتخابی خود را در پوشه‌ی images ذخیره کنید.

![][image4]

شکل ۱۳-۱ تصویری که برای موجود بیگانه به کار خواهیم برد.

ساختن کلاس Alien

اکنون کلاسی به نام Alien می‌سازیم و آن را در alien.py ذخیره می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pygame

from pygame.sprite import Sprite

class Alien(Sprite):
    """A class to represent a single alien in the fleet."""

    def __init__(self, ai_game):
        """Initialize the alien and set its starting position."""
        super().__init__()
        self.screen = ai_game.screen

        # Load the alien image and set its rect attribute.
        self.image = pygame.image.load('images/alien.bmp')
        self.rect = self.image.get_rect()

        # Start each new alien near the top left of the screen.
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height

        # Store the alien's exact horizontal position.
        self.x = float(self.rect.x)

بخش زیادی از این کلاس، به جز جاگیری آن روی صفحه، شبیه به کلاس Ship است. در ابتدا هر موجود بیگانه را در نزدیکی گوشه‌ی بالا-چپ صفحه قرار می‌دهیم؛ فاصله‌ای خالی به اندازه‌ی عرض آن در قسمت چپ و فاصله‌ای به اندازه‌ی ارتفاع آن به بالای موجود بیگانه اضافه می‌کنیم (۱) تا آسان‌تر دیده شود. سرعت افقی موجود بیگانه برای ما اهمّیّت بیشتری دارد پس موقعیّت افقی آن را دقیق‌تر دنبال می‌کنیم (۲)

کلاس Alien به متدی برای رسم شدن روی صفحه نیاز ندارد؛ به جای آن از متد گروه‌بندی پای‌گیم استفاده خواهیم کرد که تمام اعضای گروه را به طور خودکار روی صفحه رسم می‌کند.

ساختن نمونه‌ای از Alien

می‌خواهیم نمونه‌ای از Alien بسازیم تا اوّلین موجود بیگانه را روی صفحه ببینیم. چون این بخشی از بخش راه‌اندازی کار است، کد این نمونه را به انتهای متد __init__() در AlienInvasion اضافه می‌کنیم. ما در نهایت می‌خواهیم ناوگانی کامل از این موجودات بیگانه بسازیم، که این کمی کار خواهد برد، بنابراین متد کمکی جدیدی به نام _create_fleet() می‌سازیم.

ترتیب متدهای یک کلاس مهم نیست تا زمانی که در نحوه‌ی قرارگیری آن‌ها ثبات نسبی برقرار باشد. من متد _create_fleet() را درست قبل از _update_screen() قرار می‌دهم ولی هر جای دیگرِ AlienInvasion هم قرار بگیرد کار خواهد کرد. ابتدا کلاس Alien را وارد می‌کنیم.

اینجا عبارت‌های مربوط به وارد کردن ماژول‌ها را در alien_invasion.py به‌روز می‌کنیم:

–snip–

1
2
from bullet import Bullet
from alien import Alien

و اینجا متد __init__() را به‌روزرسانی می‌کنیم:

1
2
3
4
5
6
def __init__(self):
    --snip--
    self.ship = Ship(self)
    self.bullets = pygame.sprite.Group()
    self.aliens = pygame.sprite.Group()
    self._create_fleet()

گروهی برای نگهداری ناوگان موجودات بیگانه می‌سازیم و _create_fleet() را که الان می‌خواهیم بنویسیم، فرامی‌خوانیم.

این متد جدید _create_fleet() است:

1
2
3
4
5
def _create_fleet(self):
    """Create the fleet of aliens."""
    # Make an alien.
    alien = Alien(self)
    self.aliens.add(alien)

در این متد یک نمونه از Alien می‌سازیم و سپس آن را به گروهِ شامل ناوگان اضافه می‌کنیم. موجود بیگانه در جای پیش‌فرضِ بالا-چپ روی صفحه قرار می‌گیرد که برای اوّلین موجود بیگانه عالی است.

برای پدیدار کردن موجود بیگانه باید متد draw() گروه را در _update_screen() صدا کنیم:

1
2
3
4
5
6
7
8
def _update_screen(self):
    --snip--
    for bullet in self.bullets.sprites():
        bullet.draw_bullet()

    self.aliens.draw(self.screen)

    pygame.display.flip()

زمانی که draw() را بر روی یک گروه اعمال می‌کنید، پای‌گیم هر عنصر داخل آن گروه را در موقعیّتی که مشخّصه‌ی rect آن مشخّص کرده رسم می‌کند. متد draw() تنها یک نشان‌وند می‌خواهد: رویه‌ای که عنصرهای گروه قرار است روی آن رسم شوند. شکل ۱۳-۲ اوّلین موجود بیگانه را روی صفحه نمایش می‌دهد.

![][image5]

شکل ۱۳-۲ اوّلین موجود بیگانه نمایان می‌شود.

اکنون که اوّلین موجود بیگانه به درستی نمایش داده شد کد مربوط به رسم کل ناوگان را می‌نویسیم.