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

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

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

برای پیدا کردن تعداد موجوداتی که در یک سطر جا می‌شوند به کل فضای افقی موجود نگاهی می‌اندازیم. عرض صفحه در settings.screen_width ذخیره می‌شود امّا به حاشیه‌ای خالی در دو طرف صفحه هم نیاز داریم. این حاشیه را برابر با عرض یکی از موجودات بیگانه در نظر می‌گیریم. چون در هر دو طرف حاشیه داریم، فضای موجود برای بیگانگان برابر می‌شود با عرض صفحه منهای دو برابر عرض هر موجود بیگانه:

1
available_space_x = settings.screen_width - (2 * alien_width)

علاوه بر این باید بین هر دو موجود بیگانه هم فاصله‌ای بگذاریم؛ این فاصله را مساویِ عرض هرکدام از آن‌ها می‌گیریم. فضای مورد نیاز برای نمایش یک موجود بیگانه دوبرابر عرض آن است: یک عرض برای خود موجود و یکی برای فاصله‌ی خالی سمت راست آن. برای پیدا کردن تعداد موجودات بیگانه‌ای که در عرض صفحه جا می‌شوند فضای موجود را بر دوبرابرِ عرض یکی از این موجودات تقسیم می‌کنیم. از تقسیمِ کف (//) استفاده می‌کنیم که پس از تقسیم دو عدد، بخشِ اعشاری عدد را می‌اندازد، بنابراین تعداد موجودات بیگانه‌ی حاصل عددی صحیح خواهد بود:

1
number_aliens_x = available_space_x // (2 * alien_width)

هنگام ایجاد ناوگان از این محاسبات بهره خواهیم گرفت.

یکی از جنبه‌‌های مهم محاسبات در برنامه‌نویسی این است که وقتی اوّلین بار آن‌ها را می‌نویسید نیاز نیست از درستی آن‌ها مطمئن باشید. می‌توانید آن‌ها را امتحان کنید و ببینید که درست‌اند یا نه. در بدترین حالت موجودات بیگانه از صفحه بیرون می‌زنند یا تعداد بسیار کمی از آن‌ها بر صفحه ظاهر می‌شوند. شما این امکان را دارید که بر اساس مشاهدات خود محاسبات خود را مورد بازبینی قرار دهید.

ساختن سطری از موجودات بیگانه

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def _create_fleet(self):
    """Create the fleet of aliens."""
    # Create an alien and find the number of aliens in a row.
    # Spacing between each alien is equal to one alien width.
    alien = Alien(self)
    alien_width = alien.rect.width
    available_space_x = self.settings.screen_width - (2 * alien_width)
    number_aliens_x = available_space_x // (2 * alien_width)

    # Create the first row of aliens.
    for alien_number in range(number_aliens_x):
        # Create an alien and place it in the row.
        alien = Alien(self)
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        self.aliens.add(alien)

پیش از این به بیشتر بخش‌های این کد فکر کرده‌ایم. پیش از انجام هر محاسبه‌ای نیاز داریم که طول و عرض هر موجود بیگانه را پیدا کنیم پس در ۱ یکی از این موجودات می‌سازیم. این یکی جزئی از ناوگان نخواهد بود پس آن را به گروه aliens اضافه نمی‌کنیم. در ۲ عرض موجود بیگانه را از روی مشخّصه‌ی rect آن محاسبه و مقدار آن را در alien_width ذخیره می‌کنیم تا هر بار نیاز نباشد با مشخّصه‌ی rect سر و کار داشته باشیم. در ۳ فضای افقی موجود برای موجودات را محاسبه و تعداد موجودات بیگانه‌ای را که در آن فضا جا می‌گیرند پیدا می‌کنیم.

در ادامه حلقه‌ای می‌نویسیم که از صفر تا تعداد موجودات بیگانه‌ی موردنیاز می‌شمارد (۴). در بدنه‌ی اصلی این حلقه یک موجود بیگانه‌ی جدید ساخته و مختصه‌ی x آن را برابر با محل قرارگیری آن در سطر قرار می‌دهیم (۵). هر موجود بیگانه به اندازه‌ی یک عرض خود از حاشیه‌ی سمت چپ فاصله می‌گیرد. بعد عرض موجود بیگانه را دوبرابر می‌کنیم تا فضای اشغال‌شده‌ی هر کدام، با در نظر گرفتنِ فضای خالی سمت راست آن، به دست بیاید و در نهایت این مقدار را در موقعّیتِ موجود بیگانه در سطر ضرب می‌کنیم. از مشخّصه‌ی x هر موجود برای تعیین موقعیّت مستطیل آن استفاده می‌کنیم. سپس هر موجود بیگانه‌ی جدید را به گروه aliens اضافه می‌کنیم.

اکنون که هجوم بیگانگان را اجرا می‌کنید باید اوّلین سطر موجود بیگانگان را همانند شکل ۱۳-۳ ببینید:

![][image6]

شکل ۱۳-۳: اوّلین سطر موجودات بیگانه

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

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

بازآرایی

اگر کدی که نوشته‌ایم تمام کاری بود که برای ساختن ناوگان نیاز داشتیم، احتمالاً می‌توانستیم _create_fleet() را به همان شکلی که بود رها کنیم. امّا در ادامه کار بیشتری برای انجام داریم پس اجازه دهید که این متد را کمی مرتّب کنیم. یک متد کمکی جدید به نام _create_alien() می‌سازیم و آن را در _create_fleet() صدا می‌زنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def _create_fleet(self):
    --snip--
    # Create the first row of aliens.
    for alien_number in range(number_aliens_x):
        self._create_alien(alien_number)

def _create_alien(self, alien_number):
    """Create an alien and place it in the row."""
    alien = Alien(self)
    alien_width = alien.rect.width
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    self.aliens.add(alien)

متد _create_alien() به جز self یک پارامتر دیگر هم می‌خواهد: تعداد موجودات بیگانه‌ای که اکنون می‌سازیم. از همان بدنه‌ی _create_fleet() استفاده می‌کنیم به جز این که به جای منتقل کردن عرض یک موجود بیگانه به عنوان یک نشان‌وند، آن را داخل متد به دست می‌آوریم. این بازآرایی کمک می‌کند که سطرهای جدید و یک ناوگان کامل را آسان‌تر اضافه کنیم.

افزودن سطرها

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

1
available_space_y = settings.screen_height - (3 * alien_height) - ship_height

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

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

1
number_rows = available_space_y // (2 * alien_height)

حال که تعداد سطرهای هر ناوگان مشخّص شد، کد را برای ساختن یک سطر تکرار می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def _create_fleet(self):
    --snip--
    alien = Alien(self)
    alien_width, alien_height = alien.rect.size
    available_space_x = self.settings.screen_width - (2 * alien_width)
    number_aliens_x = available_space_x // (2 * alien_width)

    # Determine the number of rows of aliens that fit on the screen.
    ship_height = self.ship.rect.height
    available_space_y = (self.settings.screen_height -
        (3 * alien_height) - ship_height)
    number_rows = available_space_y // (2 * alien_height)

    # Create the full fleet of aliens.
    for row_number in range(number_rows):
        for alien_number in range(number_aliens_x):
            self._create_alien(alien_number, row_number)

def _create_alien(self, alien_number, row_number):
    """Create an alien and place it in the row."""
    alien = Alien(self)
    alien_width, alien_height = alien.rect.size
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
    self.aliens.add(alien)

ما عرض و ارتفاع یک موجود بیگانه را می‌خواهیم پس در ۱ از مشخّصه‌ی size استفاده می‌کنیم که یک دوتایی شامل عرض و ارتفاع شیء rect را در بر می‌گیرد. برای محاسبه‌ی تعداد سطرهای قابل نمایش محاسباتِ available_space_y را درست بعد از محاسباتِ available_space_x قرار می‌دهیم (۲). محاسبات داخل پرانتز قرار گرفته‌اند پس می‌توان آن را در خط نمایش داد. این کار که معمولاً توصیه می‌شود، باعث می‌شود تعداد کاراکترهای هر خط برنامه کمتر از ۷۹ باشد.

برای ساختن چند سطر از حلقه‌های تودرتو استفاده می‌کنیم، یک حلقه‌ی بیرونی و یک حلقه‌ی درونی (۳). حلقه‌ی درونی موجودات بیگانه‌ی یک سطر را می‌سازد. حلقه‌ی بیرون از صفر تا تعداد سطرهای مورد نیاز می‌شمارد؛ پایتون از این کد برای ساختن یک سطر و سپس تکرار این کار به اندازه‌ی number_rows استفاده می‌کند.

برای تودرتو کردن سطرها، حلقه‌ی for جدید را بنویسید و کد داخل آن را تورفته کنید. (بیشتر ویرایشگرهای متنی برای تورفته کردن همزمان چند خط روش‌های ساده‌ای دارند، برای آگاهی بیشتر پیوست ب را ببینید.) اکنون هر بار که _create_fleet() را فراخوانی کنیم نشان‌وندی برای تعداد سطرها به آن می‌دهیم و سطرها یکی یکی از بالا به پایین صفحه قرار می‌گیرند.

در تعریف _create_alien() به پارامتری نیاز داریم که تعداد سطر را نگه دارد. داخل _create_alien() مختصّات y یک موجود بیگانه را طوری تغییر می‌دهیم که در سطر اوّل نباشد (۴) و رسم سطرها را با در نظر گرفتن یک فضای خالی در بالای صفحه به اندازه‌ی ارتفاع یک موجود بیگانه شروع می‌کنیم. هر سطر از فاصله‌ای به اندازه‌ی دوبرابرِ ارتفاع یک موجود بیگانه تا سطر قبلی شروع می‌شود بنابراین ارتفاع موجود بیگانه را در ۲ و سپس در تعداد سطرها ضرب می‌کنیم. شماره‌ی اوّلین سطر ۰ است پس جاگیری عمودی اوّلین سطر دست نمی‌خورد. تمام سطرهای بعدی به ترتیب به سمت پایین صفحه قرار می‌گیرند.

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

![][image7]

شکل ۱۳-۴: ناوگان کامل پدیدار می‌شود.

در بخش بعدی ناوگان را به حرکت درخواهیم آورد!

خودآزمایی

۱۳-۱ ستاره‌ها: تصویر یک ستاره را پیدا کنید. تعدادی ستاره را به صورت شبکه‌ای روی صفحه نشان دهید.

۱۳-۲ ستاره‌های بهتر: می‌توانید با قرار دادن هر ستاره در مکانی تصادفی، الگوی نمایشی واقعی‌تری ایجاد کنید. به یاد بیاورید که تولید اعداد تصادفی به این شکل انجام می‌گرفت:

1
2
from random import randint
random_number = randint(-10, 10)

این کد عددی تصادفی بین ۱۰- و ۱۰ برمی‌گرداند. با استفاده از کد خود در تمرین ۱۳-۱ موقعّیت هر ستاره را به صورت تصادفی تعیین کنید.