این حالت نمایش چند صفحه ای قابل پرینت این قسمت میباشد. برای پرینت کلیک کنید..
فصل سیزدهم
- 1: افزودن دکمهی Play
- 2: رسم دکمه روی صفحه و آغاز بازی
- 3: بازنشانی بازی و مدیریت دکمه
- 4: خودآزمایی و بالا بردن سطح بازی
- 5: بازنشانی سرعت و آغاز امتیازدهی
- 6: نمایش امتیازها و ساختن تابلوی امتیازات
- 7: بهروزرسانی و بازنشانی امتیاز
- 8: گرد کردن امتیاز و بالاترین امتیاز
- 9: نمایش مرحله و تعداد سفینهها
- 10: خودآزمایی و خلاصه
1 - افزودن دکمهی Play
در این فصل بازی هجوم بیگانگان را به پایان میرسانیم. یک دکمهی Play اضافه میکنیم تا بازی را بنا به درخواست بازیکن شروع کنیم یا پس از پایان بازی آن را دوباره شروع کنیم. همچنین بازی را به گونهای تغییر میدهیم که سرعت آن با بالا رفتن مرحله افزایش یابد و یک نظام امتیازدهی ایجاد میکنیم. با پایان این فصل در مورد نوشتن بازیهایی به اندازهی کافی خواهید دانست که میزان سختیِ آنها با پیشرفت بازیکن افزایش مییابد و همچنین امتیازات را نمایش میدهد.
در این بخش یک دکمهی Play اضافه میکنیم که پیش از شروع بازی و پس از پایان یافتن آن پدیدار میشود تا بازیکن بتواند بازی را دوباره شروع کند.
فعلاً بازی به محض اجرای alien_invasion.py شروع میشود. اجازه دهید که بازی را در حالت غیرفعّال آغاز کنیم و به کاربر اعلام کنید که برای شروع بازی باید دکمهای را فشار دهد. برای چنین کاری متد __init__() کلاس GameStats را تغییر میدهیم:
حال بازی در حالت غیرفعّال شروع میشود و تا زمانیکه دکمهی Play را اضافه نکنیم بازیکن هیچ راهی برای شروع آن ندارد.
ساختن کلاس Button
ازآنجاییکه پایگیم روشی درونساخته برای ساختن دکمهها ندارد کلاس Button را مینویسیم تا مستطیلی توپر با یک عنوان درست کند. میتوانید از این کد برای ساختن هر دکمهای در یک بازی استفاده کنید. این بخش اوّل کلاس Button است، آن را به عنوان button.py ذخیره کنید:
|
|
ابتدا ماژول pygame.font را وارد میکنیم که به پایگیم امکان نمایش متن را روی صفحه میدهد. متد __init__() پارامترهای self، شیء ai_game و msg را دریافت میکند که آخری متن روی دکمه را شامل میشود. ابعاد دکمه را تعیین میکنیم و button_color را برای تعیین رنگ شیء rect دکمه، سبز روشن قرار میدهیم و با تعیین text_color رنگ متن را سفید میکنیم.
یک مشخّصهی font برای نمایش متن آماده میکنیم. نشانوند None به پایگیم میگوید که از فونت پیشفرض استفاده کند و 48 اندازهی متن را معلوم میکند. یک rect برای دکمه میسازیم تا آن را وسط صفحه قرار دهیم و مشخّصهی center آن را برابر با مشخّصهی مشابه صفحه قرار میدهیم.
پایگیم برای نمایش متنهای شما آنها را به تصویر تبدیل میکند. متد _prep_msg() را به این منظور صدا میکنیم.
این کد _prep_msg() است:
متد _prep_msg() به پارامتر self و متنی برای تبدیل به تصویر (msg) نیاز دارد. فراخوانی font.render() متن ذخیرهشده در msg را به تصویر تبدیل میکند که سپس آن را در self.msg_image ذخیره میکنیم. متد font.render() همچنین یک مقدار بولی را نیز میپذیرد تا خوشنماسازی را فعّال یا غیرفعّال کند. (خوشنماسازی لبههای حروف متن را هموارتر میکند.) باقی نشانوندها به رنگ متن و پسزمینه اختصاص دارد. ما خوشنماسازی را فعّال (True) و رنگ پسزمینه را مطابق رنگ پسزمینهی دکمه قرار میدهیم. (اگر رنگی برای پسزمینه تعیین نکنید پایگیم تلاش خواهد کرد که متن را پسزمینهی شفّاف نمایش دهد.)
یک rect از عکس ساخته و با تنظیم مشخّصهی center آن برای تطبیق با همین مشخّصهی دکمه، تصویرِ متن را وسطِ دکمه قرار میدهیم.
در نهایت متد draw_button() را میسازیم که با فراخوانی آن میتوان دکمهای را روی صفحه نمایش داد:
متد screen.fill() را برای رسم بخش مستطیلی دکمه صدا میکنیم. سپس screen.blit() را فرامیخوانیم تا تصویرِ متن را روی صفحه نشان دهد و یک تصویر و شیء rect مرتبط با تصویر را به آن میدهیم. این کار کلاس Button را تکمیل میکند.
2 - رسم دکمه روی صفحه و آغاز بازی
از کلاس Button برای ساختن دکمهی Play در AlienInvasion استفاده میکنیم. ابتدا عبارتهای import را بهروز میکنیم:
چون فقط یک دکمهی Play نیاز داریم آن را در متد __init__() کلاس AlienInvasion میسازیم. این کد را در پایان __init__() قرار میدهیم:
این کد نمونهای از Button با عنوان Play میسازد امّا این کار دکمه را روی تصویر رسم نمیکند. متد draw_button() را در _update_screen() صدا میکنیم:
برای این که این دکمه را روی دیگر عناصر صفحه نمایش دهیم، آن را پس از رسم تمام عنصرها امّا پیش از نمایش نهایی روی صفحه رسم میکنیم. متد نمایش را در یک بلوک if قرار میدهیم تا دکمه فقط وقتی که بازی در حالت غیرفعّال است دیده شود.
اکنون اگر هجوم بیگانگان را اجرا کنید دکمهی Play را وسط صفحه خواهید دید، مشابه شکل ۱۴-۱.
شکل ۱۴-۱: در زمان غیرفعّال بودن بازی دکمهی Play پدیدار میشود.
آغازِ بازی
برای آغاز شدنِ بازی با فشرده شدن Play، بلوک elif زیر را به انتهای _check_events() اضافه کنید تا رخدادهای ماوس را روی دکمه زیر نظر بگیرید:
اگر بازیکن روی هر جایی از صفحه کلیک کند پایگیم یک رخداد MOUSEBUTTONDOWN تشخیص میدهد ولی ما میخواهیم تنها زمانی که به این رخدادها پاسخ بدهد که بر روی دکمهی Play رخ داده باشند. برای رسیدن به چنین هدفی از pygame.mouse.get_pos() استفاده میکنیم که با فشرده شدن دکمهی ماوس، یک چندتایی شامل مختصّات x و y نشانگر آن برمیگرداند. این مقدارها را به متد جدید _check_play_button() میفرستیم.
این کد _check_play_button() است که ترجیح دادم آن را بعد از _check_events() بیاورم:
برای بررسی تلاقی محل کلیک شدن ماوس و محدودهی تعریفشده در rect دکمهی Play از متد collidepoint() در rect استفاده میکنیم. اگر چنین اتّفاقی افتاده باشد مقدار game_active را True میکنیم و بازی آغاز میشود!
تا اینجا باید قادر باشید که یک بازی کامل را شروع کنید. با پایان یافتنِ بازی مقدار game_active باید False شود و دکمهی Play دوباره پدیدار شود.
3 - بازنشانی بازی و مدیریت دکمه
دکمهی Play که اکنون نوشتهاید تنها اوّلین بازی که بازیکن آن را فشار میدهد کار میکند. ولی پس از پایان اوّلین دور بازی دیگر کار نمیکند زیرا شرطهایی که موجب پایان بازی شده به مقدار اوّلیّهی خود برنگشتهاند.
برای بازنشانی بازی پس از هر بار فشرده شدن دکمه، باید آمار بازی را صفر کنیم، گلولهها و موجودات بیگانهی قدیمی را پاک کنیم، ناوگان جدیدی بسازیم و سفینه را به وسط برگردانیم، چنانکه اینجا میبینیم:
|
|
آمار بازی را صفر میکنیم که این کار به هر بازیکن سه سفینه میدهد. سپس مقدار game_active را True میکنیم تا به محض پایان یافتن این تابع بازی شروع شود. گروههای aliens و bullets را خالی میکنیم و سپس یک ناوگان جدید ساخته و سفینه را به وسط برمیگردانیم.
حال بازی پس از هر بار فشرده شدن دکمهی Play بازنشانی میشود و شما امکان دارید که هر چند بار که بخواهید بازی کنید!
غیرفعّال کردن دکمهی Play
دکمهی Play یک مشکل دارد. حتّی وقتی که این دکمه پیدا نیست، ناحیهای که در برگرفته به کلیکهای ماوس واکنش نشان میدهد. اگر هنگام بازی به طور تصادفی روی ناحیهی دکمه کلیک کنید بازی از نو آغاز خواهد شد.
برای رفع این مشکل کاری میکنیم که بازی تنها هنگام False بودن game_active شروع شود:
پرچم button_clicked مقدار True یا False نگه میدارد و بازی تنها زمانی دوباره شروع خواهد شد که دکمهی Play زده شده باشد و بازی در حال حاضر در حالت فعّال نباشد. برای آزمودن این رفتار بازی جدیدی شروع کنید و پشت سر هم روی محلّی که دکمهی Play باید در آنجا باشد کلیک کنید. اگر همهچیز مطابق انتظار باشد کلیک کردن روی این ناحیه نباید تاثیری بر روند بازی داشته باشد.
پنهان کردن نشانگر ماوس
نمایان بودن نشانگر ماوس برای شروع بازی مطلوب ماست امّا با آغاز بازی این نشانگر جلوی دست و پا را میگیرد. برای حل این مشکل با فعّال شدن بازی نشانگر را غیرفعال میکنیم. این کار را میتوانیم در پایان بلوک if در _check_play_button() انجام دهیم:
set_visible() با دریافت False به پایگیم میگوید که نشانگر ماوس را هنگام حرکت روی پنجرهی بازی پنهان کند.
پس از پایان یافتن بازی نشانگر را مجدّداً نمایش میدهیم تا بازیکن بتواند با زدنِ دوبارهی Play بازی جدیدی را شروع کند. این کدی است که این کار را انجام میدهد:
به محض غیرفعّال شدن بازی نشانگر را دوباره نشان میدهیم. این کار در _ship_hit() رخ میدهد. پرداختن به چنین جزئیاتی باعث میشود بازی شما خیلی حرفهای به نظر بیاید و به بازیکنان امکان میدهد به جای فکر کردن به رابط کاربری روی بازی کردن تمرکز کنند.
4 - خودآزمایی و بالا بردن سطح بازی
خودآزمایی
۱۴-۱- فشردن P برای شروع بازی: از آنجایی که هجوم بیگانگان از ورودی صفحهکلید برای هدایت سفینه استفاده میکند، ایجاد امکان آغاز بازی با فشردن یک کلید سودمند خواهد بود. کدی اضافه کنید که به کاربر اجازه دهد بازی را فشار دادن P شروع کند. جابهجا کردن کدهایی از _check_play_button() به متد _start_game() ممکن است مفید باشد تا بتوان آنها را از _check_play_button() و _check_keydown_events() فراخواند.
۱۴-۲- تمرین هدفگیری: در کنارهی راست صفحه مستطیلی بسازید که با سرعت ثابت بالا و پایین میرود. سپس یک سفینه سمت چپ تصویر ظاهر کنید که بازیکن بتواند آن را بالا و پایین کند و به مستطیل متحرّک شلّیک کند. یک دکمهی Play برای شروع بازی اضافه کنید و زمانی که تیر بازیکن سه بار خطا رفت بازی را تمام کنید و دکمه را دوباره نمایش دهید. به بازیکن اجازه دهید که بازی را با دکمهی Play دوباره شروع کند.
بالا بردن سطحِ بازی
در بازی فعلی به محض این که بازیکن کلّ یک ناوگان را نابود کند، به مرحلهی جدیدی وارد میشود امّا میزان مشکل بودن بازی تغییری نمیکند. بیایید برای هیجانانگیز و چالشیتر کردن بازی سرعت آن را پس از هر بار خالی شدن صفحه بیشتر کنیم.
تغییر دادن تنظیمات سرعت
ابتدا ساختار کلاس Settings را دوباره طوری مرتّب میکنیم که تنظیمات بازی به دو بخش ثابت و متغیّر تقسیم شوند. همچنین مطمئن میشویم که تنظیمات متغیّر با آغاز یک بازی جدید بازنشانی میشوند. این متد __init__() برای settings.py است:
|
|
مقداردهی اوّلیّهی تنظیمات ثابت را مانند قبل در متد __init__() انجام میدهیم. تنظیمی به نام speedup_scale اضافه میکنیم که میزان سریع شدن بازی را کنترل میکند: مقدار 2 سرعت بازی را با بالا رفتن مرحلهی بازی دوبرابر میکند؛ مقدار 1 سرعت بازی را ثابت نگه میدارد. مقداری مثل 1.1 سرعت بازی را به اندازهای افزایش میدهد که بازی چالشی باشد امّا غیرممکن نشود. سرانجام متد initialize_dynamic_settings() را فرامیخوانیم تا مشخّصههایی را که باید در طول برنامه تغییر کنند مقداردهی اوّلیّه کنیم.
این کد متد initialize_dynamic_settings() است:
این متد مقدارهای اوّلیّهی مربوط به سفینه، گلوله و سرعت موجودات بیگانه را تعیین میکند. این سرعتها را همگام با پیشرفت بازیکن در بازی افزایش میدهیم و با آغاز مجدّد آن، مقدار آنها را بازنشانی میکنیم. fleet_direction را هم در این متد آوردهایم تا کاری کنیم که موجودات بیگانه در ابتدای یک بازی جدید همیشه به راست حرکت کنند. نیازی به افزایش مقدار fleet_drop_speed نیست چون زمانیکه موجودات بیگانه در عرض صفحه سریعتر حرکت میکنند، حرکت به سمت پایین آنها هم سریع خواهد بود.
برای این که با ورود بازیکن به مرحلهای جدید سرعت سفینه، گلولهها و موجودات بیگانه را افزایش دهیم متد جدیدی به نام increase_speed() مینویسیم:
برای افزایش سرعت هر یک از این عناصر بازی سرعت آنها را در speedup_scale ضرب میکنیم.
با منهدم شدن آخرین موجود بیگانهی یک ناوگان ضرباهنگ بازی را با فراخوانی increase_speed() در _check_bullet_alien_collisions() افزایش میدهیم:
تغییر مقدار تنظیمات سرعت ship_speed، alien_speed و bullet_speed برای سرعت بخشیدن به کلّ بازی کافیست!
5 - بازنشانی سرعت و آغاز امتیازدهی
بازنشانی سرعت
پیش از این که بازیکن بازی جدیدی شروع کند باید تمام تنظیمات تغییریافته را به مقدار اوّلیّه خود برگردانیم؛ وگرنه بازی هر بار با سرعت افزایشیافتهی بازی قبلی شروع میشود:
|
|
اکنون باید بازی هجوم بیگانگان جذابتر و چالشیتر باشد. هر بار که صفحه را از موجودات بیگانه خالی میکنید سرعت بازی باید افزایش بیاید و بازی کمی سختتر شود. اگر میزان سختی بازی خیلی سریع افزایش مییابد مقدار settings.speedup_scale را کاهش دهید. یا اگر بازی به اندازهی کافی چالشی نیست مقدار آن را کمی افزایش دهید. نقطهی مطلوب را با افزایش شیب سخت شدن بازی در یک مدت زمانی منطقی پیدا کنید. چند صفحهی اوّل بازی باید ساده باشد، چندتای بعد کمی چالشیتر ولی قابل انجام و صفحات بعد تا حدّ غیرممکن سخت باشند.
خودآزمایی
۱۴-۳- تمرین هدفگیری چالشی: کار خود را از تمرین ۱۴-۲ شروع کنید. حرکت هدف را با پیشرفت بازی سریعتر کنید و پس از فشرده شدن دکمهی Play سرعت هدف را به مقدار اوّلیّه برگردانید.
۱۴-۴- سطحهای سختی: مجموعهای از دکمهها برای هجوم بیگانگان بسازید که به بازیکن امکان دهد سختی مناسب را برای شروع بازی تنظیم کند. هر دکمه باید مقداری مناسب به مشخّصههای Settings بدهد تا سطحهای مشکل بودن مختلف ایجاد کند.
امتیازدهی
حال بیایید یک سیستم امتیازدهی برای پیگیری امتیاز بازی در زمان واقعی و نمایش بیشتر امتیاز، سطح و تعداد سفینههای باقیمانده ایجاد کنیم.
امتیاز به آمار بازی مربوطاست پس مشخّصهی score را به GameStats اضافه میکنیم:
برای بازنشانی امتیاز پس از هر بار شروع یک بازی جدید، مقدار score را به جای __init__() در reset_stats() مقداردهی اوّلیّه میکنیم.
6 - نمایش امتیازها و ساختن تابلوی امتیازات
نمایش امتیازها
برای نمایش امتیازات روی صفحه ابتدا کلاسی به نام Scoreboard میسازیم. فعلاً این کلاس تنها امتیاز کنونی را نمایش میدهد امّا در نهایت از آن برای نمایش بیشترین امتیاز، سطح و تعداد سفینههای باقیمانده هم استفاده خواهیم کرد.
این اوّلین بخش کلاس است؛ آن را به نام scoreboard.py ذخیره کنید:
|
|
چون قرار است کلاس متنهایی را روی صفحه بنویسد کار را به وارد کردن ماژول pygame.font شروع میکنیم. سپس به متد __init__() پارامتر ai_game را میدهیم تا به شیءهای settings، screen و stats دسترسی داشته باشد و به کمک آنها مقدارهای مورد نظر ما را گزارش کند. سپس رنگ متن را تعیین کرده و نمونهای از شیء فونت میسازیم.
متنی را که قرار است نمایش داده شود با فراخوانی prep_score() به تصویر تبدیل میکنیم:
|
|
در prep_score() مقدار عددی stats.score را به رشته تبدیل میکنیم، سپس این رشته را به render() میدهیم که آن را به تصویر تبدیل میکند. برای نمایش واضح امتیاز روی صفحه رنگ پسزمینه و رنگ متن را به render() میدهیم.
امتیاز را در گوشهی بالا-راست صفحه قرار میدهیم و با بیشتر شدن آن و افزایش عرض عدد، آن را به سمت چپ گسترش میدهیم. برای این مطمئن باشیم که امتیاز با کنارهی راست صفحه همتراز است، یک rect به نام score_rect میسازیم و حاشیهی راست آن را در فاصلهی ۲۰ پیکسلی کنارهی راست صفحه قرار میدهیم. سپس لبهی بالای آن را ۲۰ پیکسل پایینتر از کنارهی بالایی صفحه تنظیم میکنیم.
سپس متدی به نام show_score() میسازیم تا تصویر تولیدشدهی امتیاز را نشان دهد:
این متد تصویر امتیاز را در موقعیّتی که score_rect تعیین میکند، روی صفحه رسم میکند.
ساختن تابلوی امتیازات
برای نمایش امتیاز در AlienInvasion نمونهای از Scoreboard میسازیم. ابتدا عبارتهای import را بهروز میکنیم:
سپس نمونهای از Scoreboard در __init__() میسازیم:
سپس در _update_screen() تابلوی امتیازات را روی صفحه نمایش میدهیم:
متد show_score() را درست پیش از رسم دکمهی Play میکشیم. اگر اکنون هجوم بیگانگان را اجرا کنید عدد صفر باید در گوشهی بالا سمت راست صفحه نمایان شود. (تا اینجا پیش از توسعهی سیستم امتیازدهی فقط مطمئن میشویم که امتیاز به درستی روی صفحه نمایش داده میشود.) شکل ۱۴-۲ امتیاز را هنگام نمایش پیش از شروع بازی نشان میدهد.
شکل ۱۴-۲: امتیاز در گوشهی بالا راست صفحه نمایان میشود.
در ادامه به هر موجود بیگانه امتیازی نسبت میدهیم!
7 - بهروزرسانی و بازنشانی امتیاز
بهروزرسانی امتیاز با نابودی موجودات بیگانه
برای نمایش زندهی امتیاز روی صفحه مقدار stats.score را با نابودی هر موجود بیگانه بهروزرسانی میکنیم و سپس prep_score() را برای بهروزرسانی تصویرِ امتیاز صدا میکنیم. امّا ابتدا اجازه امتیازی را که هر بازیکن با زدن هر موجود بیگانه کسب میکند تعیین کنیم:
با پیشرفت بازی امتیاز هر موجود بیگانه را افزایش خواهیم داد. برای اطمینان از بازنشانی این امتیاز با شروع دوبارهی بازی، مقدار آن را در initialize_dynamic_settings() تعیین میکنیم.
با هر بار نابود شدن یک موجود بیگانه امتیاز را در _check_bullet_alien_collisions() بهروز میکنیم:
|
|
زمانی که گلولهای به یک موجود بیگانه اصابت میکند پایگیم دیکشنری collisions را برمیگرداند. ما وجود این دیکشنری را بررسی میکنیم و در صورت وجود مقدار ارزش موجود بیگانه را به امتیاز اضافه میکنیم. سپس prep_score() را برای ساختن تصویر جدیدی از امتیاز بهروزشده صدا میکنیم.
اکنون هنگامی که هجوم بیگانگان را بازی میکنید باید بتوانید امتیاز خود را حساب کنید!
بازنشانی امتیاز
تا اینجا ما امتیاز جدید را پس از نابودی یک موجود بیگانه نمایش میدهیم که در بیشتر قسمتهای بازی همینطور باید باشد. امّا در آغاز یک بازی جدید و تا پیش از این که اوّلین موجود بیگانه کشته شود، همچنان امتیاز قدیمی را میبینیم.
این مسأله را با نمایش امتیاز هنگام شروع یک بازی جدید برطرف میکنیم:
پس از بازنشانی آمارهای بازی هنگام شروع یک بازی جدید متد prep_score() را فرامیخوانیم. این کار امتیاز ۰ را روی تابلوی امتیازات نمایش میدهد.
اطمینان یافتن از ثبت امتیاز تمام شلّیکهای موفّق
کدی که تا اینجا نوشتهایم امتیاز کشتن برخی موجودات بیگانه را جا میاندازد. برای مثال اگر در یک اجرای حلقه دو گلوله به موجودات بیگانه برخورد کند یا در صورتیکه برای کشتن همزمان چند موجود بیگانه گلولهای عریضتر در نظر بگیریم، بازیکن تنها امتیاز کشتن یک موجود بیگانه را دریافت خواهد کرد. برای حل این مشکل روش تشخیص برخورد گلوله و موجود بیگانه را بهبود میبخشیم.
در _check_bullet_alien_collisions() هر گلولهای که به یک موجود بیگانه برخورد کند به کلیدی در دیکشنری collisions تبدیل خواهد شد. مقدار مرتبط با هر کلید لیستی از موجودات بیگانه است که گلوله به آنها اصابت کرده است. در مقدارهای دیکشنری collisions حلقه میزنیم تا مطمئن شویم که به کشتنِ هر موجود بیگانه امتیازی اختصاص دادهایم:
اگر دیکشنری collisions تعریف شده باشد در تمام مقدارهای آن حلقه میزنیم. به یاد داریم که هر مقدار لیستی از موجودات بیگانهای است که با یک گلوله کشته شدهاند. ارزش هر کدام از موجودات بیگانه را در تعداد آنها در هر لیست ضرب کرده و این مقدار را به امتیاز فعلی اضافه میکنیم. برای آزمایش درستی این کد عرض هر گلوله را به ۳۰۰ پیکسل افزایش دهید و امتیازهای کسبشده برای کشتن هر موجود بیگانه را با این گلولهی عریض بررسی کنید؛ سپس عرض گلوله را به مقدار اوّلیّهی خود برگردانید.
افزایش مقدار امتیازها
چون با رسیدن بازیکن به مرحلهای جدید بازی سختتر میشود، موجودات بیگانه در مرحلههای بالاتر ارزش بیشتری دارند. برای پیادهسازی این قابلیّت کدی اضافه میکنیم تا با افزایش سرعت بازی، مقدار امتیاز افزایش یابد:
|
|
نرخی به نام score_scale تعریف میکنیم که امتیازها بر اساس آن افزایش مییابد. افزایش کوچکی در سرعت (1.1) بازی را به سرعت چالشی میکند. امّا برای ایجاد تغییر قابل توجّهتری در امتیازدهی باید ارزش امتیاز هر موجود بیگانه را با مقدار بزرگتری (1.5) تغییر دهیم. حال همزمان با افزایش سرعت بازی ارزش امتیاز هر شلّیک موفّق را هم افزایش میدهیم. از تابع int() برای افزایش ارزش امتیاز بر اساس یک عدد صحیح استفاده میکنیم.
برای دیدن ارزش امتیاز کشتن هر موجود بیگانه یک print() به متد increase_speed() در Settings اضافه میکنیم:
print() را حذف کنید زیرا ممکن است بر عملکرد بازی تاثیر بگذارد و حواس بازیکن را پرت کند.8 - گرد کردن امتیاز و بالاترین امتیاز
گرد کردن امتیاز
در بیشتر بازیهای شبهآرکید امتیازات به صورت مضربهایی از ۱۰ گزارش میشوند بنابراین ما نیز همین رویّه را پی میگیریم. علاوه بر این به امتیازهای بزرگ جداکنندهی کاما اضافه میکنیم. این تغییر را در Scoreboard اضافه میکنیم:
تابع round() معمولاً یک عدد اعشاری را تا تعداد رقمی که در نشانوند دوم آن تعیین شده گرد میکند. امّا اگر عددی منفی را به عنوان نشانوند دوم وارد کنیم این تابع عدد را به نزدیکترین ۱۰، ۱۰۰، ۱۰۰۰ و … گرد میکند. کد به پایتون میگوید که مقدار stats.score را به نزدیکترین مضرب ۱۰ گرد و در rounded_score ذخیره کن.
از یک راهنمای شکلدهی رشته استفاده میکنیم تا پایتون هنگام تبدیل عدد به رشته، آن را سه رقم سه رقم با کاما جدا کند تا مثلاً به جای 1000000، 1,000,000 را نمایش دهد. حال اگر بازی را اجرا کنید امتیاز را به شکلی مرتّب و همچنین گردشده مشاهده خواهید کرد حتّی اگر امتیازهای خیلی زیادی کسب کرده باشید، چنانکه در شکل ۱۴-۳ دیده میشود.
شکل ۱۴-۳: امتیاز گردشده با جداکنندهی کاما
بالاترین امتیاز
هر بازیکنی دوست دارد که بالاترین رکورد بازی را بشکند بنابراین باید بالاترین امتیاز کسبشده را ذخیره و گزارش کنیم تا بازیکنان انگیزهای برای پیشرفت در بازی داشته باشند. بیشترین امتیازها را در GameStats ذخیره میکنیم:
از آنجاییکه بیشترین امتیاز هیچگاه نباید بازنشانی شود آن را به جای reset_stats() در __init__() مقداردهی اوّلیّه میکنیم.
سپس Scoreboard را طوری تغییر میدهیم که بالاترین امتیاز را نشان دهد. از متد __init__() شروع میکنیم:
بیشترین امتیاز را جدا از امتیاز نمایش میدهیم پس به متدی جدید، prep_high_score() احتیاج داریم تا تصویر بیشترین امتیاز را آماده کند.
این متد prep_high_score() است:
|
|
بیشترین امتیاز را به نزدیکترین مضرب ۱۰ گرد کرده و آن را با کاما جدا میکنیم. سپس تصویری از بیشترین امتیاز میسازیم، rect آن را افقی وسطچین میکنیم و مشخّصهی top آن را با بالای تصویر امتیازها همتراز میکنیم.
اکنون متد show_score() امتیاز فعلی را در بالا-راست و بیشترین امتیاز را در بالا-وسط صفحه نشان میدهد:
برای بررسی بیشترین امتیازها متد جدیدی به نام check_high_score() در Scoreboard مینویسیم:
متد check_high_score() امتیاز فعلی را با بالاترین امتیاز مقایسه میکند. اگر بیشتر باشد مقدار high_score را بهروز میکنیم و برای بهروزرسانی عکس بالاترین امتیاز prep_high_score() را صدا میکنیم.
باید متد check_high_score() را پس از مورد اصابت قرار گرفتن هر موجود بیگانه و پس از بهروزرسانی امتیاز در _check_bullet_alien_collisions() بهروز کنیم:
متد check_high_score() را زمانی فرامیخوانیم که دیکشنری collisions موجود باشد و این کار را پس از بهروزرسانی امتیاز مربوط به کشتن تمام موجودات بیگانه انجام میدهیم.
اوّلین بازی که هجوم بیگانگان را بازی میکنیم امتیاز شما بالاترین امتیاز خواهد بود بنابراین به عنوان امتیاز فعلی و بالاترین امتیاز نمایش داده خواهد شد. ولی زمانی که بازی دیگری را شروع کنید بالاترین امتیاز در میانهی صفحه و امتیاز فعلیتان در سمت راست دیده خواهید شد چنانکه در شکل ۱۴-۴ میبینیم.
شکل ۱۴-۴: بالاترین امتیاز در وسط-بالای صفحه نمایش داده میشود.
9 - نمایش مرحله و تعداد سفینهها
نمایش دادن مرحله
برای نمایش مرحلهای از بازی که بازیکن در آن قرار دارد، باید ابتدا شاخصهای به GameStats اضافه کنیم که بیانگر مرحلهی فعلی باشد. برای بازنشانی مرحله در ابتدای هر بازی جدید آن را در reset_stats() مقداردهی اوّلیّه میکنیم:
برای این که Scoreboard مرحلهی فعلی را نمایش دهد تابعی جدید را به نام prep_level() از __init__() صدا میکنیم:
این متد prep_level() است:
|
|
متد prep_level() تصویری از مقدار ذخیرهشده در stats.level میسازد و مقدار مشخّصهی right آن را متناسب با مشخّصهی right امتیازها تعیین میکند. سپس مشخّصهی top تصویر را ۱۰ پیکسل پایینتر از انتهای تصویر امتیاز قرار میدهد تا بین امتیاز و مرحله فاصلهای بیفتد.
باید show_score() را هم بهروز کنیم:
خطّی که به تازگی اضافه شده تصویر مرحله را روی صفحه نشان میدهد.
در _check_bullet_alien_collisions() مقدار stats.level را افزایش داده و تصویر مرحله را بهروز میکنیم:
اگر ناوگانی نابود شود مقدار stats.level را افزایش میدهیم و prep_level() را فرامیخوانیم تا مطمئن شویم که مرحلهی جدید به درستی نمایش داده میشود.
برای اطمینان از بهروزرسانی درست تصویر مرحله در آغاز یک بازی جدید، هنگامی که بازیکن دکمهی Play را فشار میدهد نیز یک بار متد prep_level() را صدا میکنیم:
prep_level() را درست پس از فراخوانی prep_score() صدا میکنیم.
حال تعداد مراحلی را که به پایان رساندهاید خواهید دید، مانند شکل ۱۴-۵.
شکل ۱۴-۵: مرحلهی فعلی درست زیر امتیاز فعلی نمایان میشود.
font.render() در Scoreboard اضافه کنید.نمایش تعداد سفینهها
در نهایت تعداد سفینههایی را که برای بازیکن باقی مانده نمایش میدهیم امّا این بار از تصویرهای گرافیکی استفاده میکنیم. برای چنین کاری، همانند بسیاری از بازیهای آرکید، سفینهها را در گوشهی بالا-چپ صفحه میکشیم تا بیانگر تعداد سفینههای باقیمانده باشد.
ابتدا باید کاری کنیم که Ship از Sprite ارث ببرد تا بتوانیم گروهی از سفینهها بسازیم:
اینجا Sprite را وارد میکنیم، مطمئن میشویم که Ship از Sprite ارث میبرد و super() را در ابتدای __init__() فرامیخوانیم.
سپس باید Scoreboard را طوری تغییر دهیم تا گروهی از سفینهها برای نمایش بسازد. این عبارتهای import در Scoreboard است:
چون گروهی از سفینهها میسازیم Group و Ship را وارد میکنیم.
این __init__() است:
نمونهی بازی را به یک مشخّصه نسبت میدهیم زیرا آن را برای ساختن چند سفینه نیاز داریم. prep_ships() را پس از فراخوانی prep_level() صدا میکنیم.
این prep_ships() است:
متد prep_ships() گروهی خالی به نام self.ships میسازد تا نمونههای سفینه را در آن نگه دارد. برای پر کردن این گروه حلقهای یک بار برای هر کدام از سفینههای باقیمانده اجرا میشود. داخل حلقه سفینه جدیدی میسازیم و مقدار مختصّهی x آن را طوری تنظیم میکنیم تا سفینهها کنار یکدیگر و با یک حاشیهی ۱۰ پیکسلی از سمت چپ گروه سفینهها دیده شوند. مقدار مختصّهی y را ده پیکسل پایینتر از بالای صفحه میگذاریم تا سفینهها در گوشهی بالا-چپ صفحه دیده شوند. سپس هر سفینه جدید را به گروه ships اضافه میکنیم.
حال باید سفینهها را روی صفحه بکشیم:
برای نمایش سفینهها روی صفحه draw() را روی هر گروه اعمال میکنیم و پایگیم هر سفینه را رسم میکند.
برای این که به بازیکن نشان دهیم که چند سفینه برای شروع بازی در اختیار دارد، prep_ships() را هنگام شروع یک بازی جدید فرامیخوانیم. این کار را در _check_play_button() در AlienInvasion انجام میدهیم:
این متد را هنگام مورد اصابت قرار گرفتن یک سفینه هم صدا میکنیم تا تصویر سفینهها را هنگام از دست رفتن یک سفینه بهروز کنیم:
متد prep_ships() را پس از کاهش دادن مقدار ships_left صدا میکنیم تا هنگام نابود شدن هر سفینه تعداد درست سفینهها نمایش داده شود.
شکل ۱۴-۶ نظام امتیازدهی کامل را به همراه تعداد سفینههای باقیمانده در گوشهی بالا-چپ تصویر نشان میدهد.
شکل ۱۴-۶: نظام کامل امتیازدهی برای هجوم بیگانگان
10 - خودآزمایی و خلاصه
خودآزمایی
۱۴-۵- بالاترین امتیاز تمام دوران: پس از هربار بسته شدن بازی و اجرای مجدّد آن، بالاترین امتیاز از دست میرود. این مشکل را با نوشتن بالاترین امتیاز در یک فایل پیش از فراخوانی sys.exit() و خواندن آن هنگام مقداردهی اوّلیّهی آن در GameStats برطرف کنید.
۱۴-۶- بازآرایی: دنبال متدهایی بگردید که بیش از یک وظیفه را انجام میدهند و آن را برای سازماندهی و بهینهتر کردن کد خود، بازآرایی کنید. برای مثال بخشی از کد _check_bullet_alien_collisions() را که با نابودی کامل یک ناوگان مرحلهی جدیدی را آغاز میکند، به تابعی به نام start_new_level() منتقل کنید. همچنین چند فراخوانی مجزّای موجود در __init__() در Scoreboard را به متد جدیدی به نام prep_images() منتقل کنید تا __init__() را کوتاهتر کنید. همچنین اگر متد _check_play_button() را قبلاً بازآرایی کرده باشید، متد prep_images() میتواند همچنین باعث سادهتر شدن متدهای _check_play_button() یا start_game() شود.
۱۴-۷- گسترش دادن بازی: به راهی برای گسترش دادن هجوم بیگانگان فکر کنید. برای مثال میتوانید برنامه را جوری بنویسید که موجودات بیگانه امکان شلیک گلوله به سمت سفینه داشته باشند یا محافظهایی برای سفینهها اضافه کنید تا پشت آنها پنهان شوند ولی با برخورد گلوله از هر طرف نابود شوند. یا از چیزی مثل ماژول pygame.mixer استفاده کنید تا صدای انفجار یا تیراندازی به بازی اضافه کند.
۱۴-۸- نسخهی نهایی تیرانداز حاشیهای: توسعهی تیرانداز حاشیهای را با هر آنچه در این پروژه انجام دادهاید ادامه دهید. دکمهی Play اضافه کنید، سرعت بازی را در امتیازهای خاصّی افزایش دهید و یک نظام امتیازدهی توسعه دهید. مطمئن شوید که همزمان با کار خود کد را بازآرایی کنید و به دنبال فرصتهایی بگردید تا بازی را فراتر از آنچه در این فصل نشان داده شده توسعه دهید.
خلاصه
در این فصل آموختید که چگونه یک دکمهی Play برای شروع بازی جدید اضافه کنید، رخدادهای ماوس را تشخیص دهید و نشانگر آن را در بازیهای فعّال پنهان کنید. میتوانید از آموختههای خود در این فصل استفاده کنید تا دکمههای دیگری به بازی اضافه کنید، مانند دکمهی Help که راهنمایی برای چگونه بازی کردن نشان میدهد. همچنین یاد گرفتید که چگونه سرعت بازی را همزمان با پیشرفت آن افزایش دهید، یک سیستم امتیازدهی پیشرو طرّاحی کنید و اطّلاعات را به روشهای متنی و غیرمتنی نمایش دهید.