شلّیک به موجودات بیگانه

تا اینجا سفینه و ناوگان بیگانگان را ساخته‌ایم ولی گلوله‌ها با رسیدن به موجودات بیگانه صرفاً از آن‌ها رد می‌شوند زیرا برخوردها را بررسی نمی‌کنیم. در برنامه‌نویسی بازی برخورد زمانی رخ می‌دهد که عناصر بازی با هم هم‌پوشانی پیدا کنند. برای این که کاری کنیم گلوله‌ها موجودات بیگانه را نابود کنند از متد sprite.groupcollide() استفاده می‌کنیم تا برخورد بین اعضای دو گروه را تشخیص دهیم.

تشخیص برخورد گلوله

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

تابع sprite.groupcollide() مستطیل‌های تک‌تک اعضای یک گروه را با اعضای گروه دیگر مقایسه می‌کند. در مورد مسأله‌ی ما این تابع rect هر گلوله را با rect هر موجود بیگانه مقایسه می‌کند و آن‌هایی را که با هم برخورد داشته‌اند در قالب یک دیکشنری برمی‌گرداند. هر کلید دیکشنری یک گلوله و مقدار متناظرش موجود بیگانه‌ای خواهد بود که گلوله با آن برخورد کرده است. (از این دیکشنری در فصل ۱۴ برای پیاده‌سازی سیستم امتیازدهی استفاده خواهیم کرد.)

کد زیر را به آخر _update_bullets() اضافه کنید تا برخورد بین گلوله‌ها و موجودات بیگانه را تشخیص دهید:

1
2
3
4
5
6
7
def _update_bullets(self):
    """Update position of bullets and get rid of old bullets."""
    --snip--
    # Check for any bullets that have hit aliens.
    # If so, get rid of the bullet and the alien.
    collisions = pygame.sprite.groupcollide(
            self.bullets, self.aliens, True, True)

کد جدیدی که اضافه کرده‌ایم موقعیّت تمام گلوله‌های self.bullets را با تمام بیگانه‌ها در self.aliens مقایسه می‌کند و هر کدام را که هم‌پوشانی داشته باشند تشخیص می‌دهد. هر گاه مستطیل‌های یک گلوله و موجود بیگانه روی هم بیفتند groupcollide() یک جفت کلید-مقدار به دیکشنری برگشتی خود اضافه می‌کند. دو نشان‌وند True به پای‌گیم می‌گوید که گلوله‌ها و موجودات بیگانه‌ای را که با هم برخورد کرده‌اند حذف کند. (اگر بخواهید گلوله‌ای پُرتوان درست کنید که هنگام حرکت به سمت بالای صفحه تمام موجودات بیگانه‌ی سر راهش را نابود کند، می‌توانید نشان‌وند بولی اوّل را False کنید و دومی را درست (True) نگه دارید. موجودات بیگانه‌ای که مورد اصابت قرار گرفته‌اند ناپدید می‌شوند امّا گلوله‌ها تا زمان خارج شدن از بالای صفحه باقی می‌مانند.)

اگر اکنون هجوم بیگانگان را اجرا کنید، بیگانگانی که مورد اصابت قرار گرفته‌اند باید ناپدید شوند. شکل ۱۳-۵ ناوگانی را نشان می‌دهد که بخشی از آن نابود شده است.

![][image8]

شکل ۱۳-۵: می‌توانیم به بیگانگان شلیّک کنیم!

بزرگ کردن آزمایشی گلوله‌ها

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

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

تغییر دلخواه من در هجوم بیگانگان استفاده از یک گلوله‌ی بسیار عریض است که پس از برخورد با موجود بیگانه فعّال باقی می‌ماند (شکل ۱۳-۶ را ببینید.) مقدار bullet_width را ۳۰۰ یا حتّی ۳۰۰۰ قرار دهید و ببینید که چه‌قدر سریع می‌توانید ناوگان را نابود کنید!

![][image9]

شکل ۱۳-۶: گلوله‌های بسیار قدرتمند آزمودن برخی از جنبه‌های بازی را ساده‌تر می‌کند.

بازسازی ناوگان

یکی از ویژگی‌های اصلی هجوم بیگانگان بی‌امان بودن هجوم موجودات بیگانه است: هر بار که ناوگانی نابود می‌شود ناوگانی جدید باید ظاهر شود.

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

1
2
3
4
5
6
def _update_bullets(self):
    --snip--
    if not self.aliens:
        # Destroy existing bullets and create new fleet.
        self.bullets.empty()
        self._create_fleet()

در ۱ خالی بودن گروه aliens را بررسی می‌کنیم. یک گروه خالی False ارزیابی می‌شود پس این راه ساده‌ای برای بررسی خالی بودن گروه است. اگر چنین باشد تمام گلوله‌های موجود را به کمک متد empty() حذف می‌کنیم. این متد اسپرایت‌های باقی مانده در یک گروه را حذف می‌کند (۲). ما همچنین _create_fleet() را فرا می‌خوانیم تا صفحه را دوباره با موجودات بیگانه پر کند.

حال اگر ناوگانی را نابود کنید بلافاصله ناوگان جدیدی ظاهر می‌شود.

سرعت دادن به گلوله‌ها

اگر شلّیک گلوله‌ها را در وضعیّت فعلی بازی امتحان کرده باشید احتمالاً دریافته‌اید که گلوله‌ها با بهترین سرعت ممکن، که با روند بازی متناسب باشد، حرکت نمی‌کنند. سرعت آن‌ها در سیستم شما ممکن است کمی کُند یا خیلی تُند باشد. در این نقطه شما می‌توانید با تغییر تنظیمات روند بازی را در سیستم خود جذّاب و لذّت‌بخش کنید.

سرعت گلوله‌ها را با تنظیم مقدار bullet_speed در settings.py تغییر می‌دهیم. من در سیستم خودم این مقدار را 1.5 گذاشته‌ام تا گلوله کمی سریع‌تر حرکت کند:

1
2
3
4
# Bullet settings
self.bullet_speed = 1.5
self.bullet_width = 3
--snip--

بهترین مقدار برای این تنظیمات به سرعت سیستم شما بستگی دارد پس مقداری را بیاید که برای شما به کار بیاید. تنظیمات دیگر را هم می‌توانید عوض کنید.

بازآرایی _update_bullets()

اکنون _update_bullets() را بازآرایی می‌کنیم تا این همه وظیفه‌های متنوّع را بر عهده نداشته باشد. کد مربوط به بررسی برخورد گلوله و بیگانه را به متدی جدا منتقل می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def _update_bullets(self):
    --snip--
    # Get rid of bullets that have disappeared.
    for bullet in self.bullets.copy():
        if bullet.rect.bottom <= 0:
            self.bullets.remove(bullet)
    self._check_bullet_alien_collisions()

def _check_bullet_alien_collisions(self):
    """Respond to bullet-alien collisions."""
    # Remove any bullets and aliens that have collided.
    collisions = pygame.sprite.groupcollide(
            self.bullets, self.aliens, True, True)
    if not self.aliens:
        # Destroy existing bullets and create new fleet.
        self.bullets.empty()
        self._create_fleet()

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

خودآزمایی

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