سيُمنح اللاعب بعضًا من الجوائز ليجمعها ويعزز نقاطه في هذا الإصدار من اللعبة المطورة باستخدام بايثون 3 والوحدة Pygame المتخصصة بألعاب الفيديو، والبداية مع روابط مقالات هذه السلسلة.
- بناء لعبة نرد بسيطة بلغة بايثون.
- بناء لعبة رسومية باستخدام بايثون ووحدة الألعاب PyGame.
- إضافة لاعب إلى اللعبة المطورة باستخدام بايثون و Pygame.
- تحريك شخصية اللعبة باستخدام PyGame.
- إضافة شخصية العدو للعبة.
- إضافة المنصات إلى لعبة بايثون باستخدام الوحدة Pygame.
- محاكاة أثر الجاذبية في لعبة بايثون.
- إضافة خاصية القفز والركض إلى لعبة بايثون.
- إضافة الجوائز إلى اللعبة المطورة بلغة بايثون.
- تسجيل نتائج اللعبة المطورة بلغة بايثون وعرضها على الشاشة.
- إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون.
عرضت هذه السلسلة المبادئ الأساسية لتطوير ألعاب الفيديو باستخدام بايثون، ولابدّ أنها منحتك أساسًا جيدًا تنطلق منه لبناء لعبتك الخاصة، لا تتقيد حرفيًا بأسلوب السلسلة، بل استخدم المبادئ نفسها بطرقٍ جديدة، عدّلها لتبرمج عناصرك الخاصة وشروط اللعب التي تريدها.
مقال اليوم هو خيرُ مثالٍ على ذلك، إذ ستلاحظ التشابه الكبير في المبادئ بين إضافة المنصات التي تعلمتها سابقًا وإضافة الجوائز، فكلاهما من كائنات اللعبة التي لا تخضع لتحكم خارجي من قبل المستخدم، إنما تُمرر مع عالم اللعبة خلال مستوياتها المتتابعة، وفي الحالتين تحتاج إلى طريقةٍ برمجية لاكتشاف التصادم بين كائن البطل وهذه الكائنات واحتساب تأثيره على نقاط اللاعب وصحته سواءً سلبًا أو إيجابًا.
وقبل أن تبدأ جهّز صورًا تمثل الكنوز أو الجوائز التي يجمعها اللاعب، اختر صورًا لعملات معدنية أو جواهر أو ما شابه، يمكنك تحميل حزمة الصور نفسها من موقع kenney.nl التي أخذنا منها صور البلاطات المربعة لبناء منصات اللعبة فهي تتضمن صور جواهر ومفاتيح وكلاهما يفي بالغرض.
بناء دالة الجوائز
إضافة الجوائز والكنوز إلى لعبة مشابه جدًا لإضافة المنصات كما ذكرنا في المقدمة، في الواقع هو مشابه لدرجة تجعلنا نستدعي صنف المنصة نفسه Platform
لإنتاجها.
ابدأ بإنشاء دالة جديدة تحمل الاسم loot
في صنف المستوى level
إذ من المحتمل جدًا أن يتبدل نوع الجوائز بين مستوى وآخر، وتمامًا مثلما فعلنا مع المنصات العائمة والأرضية والأعداء أنشئ في هذه الدالة مجموعة خاصة بكائنات الجوائز سميها loot_list
وأضف إليها الجوائز، ستُفيدك هذه المجموعة في عملية اكتشاف التصادمات.
def loot(lvl): if lvl == 1: loot_list = pygame.sprite.Group() loot = Platform(tx*9, ty*5, tx, ty, 'loot_1.png') loot_list.add(loot) if lvl == 2: print(lvl) return loot_list
استخدم هذا المثال أبعاد قطع البلاط ty
و tx
لتحديد إحداثيات ظهور الجوائز من خلال مضاعفاتها للسهولة فقط، ستفيدك هذه الطريقة إن خططت للمستوى على أوراق الرسم البياني مع تمثيل كل بلاطة بمربع أو كان المستوى الذي صممته طويلًا، لكن في النهاية يعود الخيار لك إذ يمكنك عوضًا ذلك كتابة إحداثيات كل جائزة صراحةً برقم مقدر بالبكسل فتصبح هذه الأرقام مدمجة مع برنامج اللعبة hard-coded.
لا تنسَ أن باستطاعتك إظهار القدر الذي تريده من الجوائز المتنوعة لكن بشرط أن تضيفها جميعًا لمجموعة الجوائز.
أما بالنسبة لوسطاء صنف المنصة Platform
في هذه الحالة فهم إحداثيات موقع ظهور الجائزة X و Y بالإضافة إلى أبعاد صورة الجائزة، وننصحك بالتخطيط لأماكن توضع الجوائز منذ البداية وتضمينها في مخطط تصميم المستوى واختيار صور مربعة لتمثيلها تعادل أبعادها أبعاد بلاطات الأرضية والمنصات فهذا سيوفر عليك الكثير.
استدعِ الآن دالة الجوائز في مقطع الإعدادات ضمن البرنامج وفق التالي:
loot_list = Level.loot(1)
وأخيرًا اذكرها ضمن الحلقة الرئيسية لتظهر على الشاشة:
loot_list.draw(world)
شغل اللعبة ولاحظ النتائج.
ظهرت صورة الجائزة بعد تشغيل اللعبة، إلّا أنها لم تُحدث أي تأثير على اللاعب عندما اصطدم بها ولم تُمرر مع تحرك اللاعب وتمرير عالم اللعبة، وهذا هو موضوع فقراتنا اللاحقة.
تمرير الجوائز مع عالم اللعبة
تمامًا مثل المنصات ينبغي أن تُمرر الجوائز مع عالم اللعبة إلى الأمام والخلف تبعًا لحركة البطل.
اكتب السطرين الأخيرين من التعليمات التالية لتُمرر الجوائز إلى الأمام:
for e in enemy_list: e.rect.x -= scroll for l in loot_list: # تمرير الجوائز l.rect.x -= scroll # تمرير الجوائز
واكتب السطرين الأخيرين من التالي لتُمررها للخلف:
for e in enemy_list: e.rect.x += scroll for l in loot_list: # تمرير الجوائز l.rect.x += scroll # تمرير الجوائز
تفقد اللعبة الآن واختبر التمرير بالاتجاهين، ولاحظ أن الجوائز أصبحت تتصرف وكأنها عنصر أصيل من عناصر عالم اللعبة وليست مجرد صورة.
اكتشاف التصادم
استخدم ما تعلمته سابقًا عند اكتشاف تصادم البطل مع الأرضية والمنصات العائمة والأعداء لتكتشف لتصادمه مع الجوائز، الطريقة نفسها تمامًا إلّا أن النتائج المترتبة على التصادم ستكون مختلفة فهي لن تتعلق بالجاذبية ولن تُنقص من صحة اللاعب وأدواره بل ستؤدي إلى زيادة نقاطه واختفاء الجائزة التي اصطدم بها من عالم اللعبة، وذلك عبر إزالتها من مجموعة كائنات الجوائز loot_list
ما يعني أنها لن تظهر مجددًا في المرة القادمة التي تُعيد فيها الحلقة الرئيسية رسم عالم اللعبة، وهذا منطقي فالبطل اغتنمها وحاز على بعض النقاط بسببها.
اكتب التعليمات التالية في الدالة update
ضمن صنف اللاعب Player
تمامًا فوق التعليمات المسؤولة عن اكتشاف التصادم مع المنصات، وانتبه للسطر الأخير فهو لتبيان السياق فقط وما من داعٍ لتعيد كتابته:
loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False) for loot in loot_hit_list: loot_list.remove(loot) self.score += 1 print(self.score) plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
عرّف الآن المتغير الذي استخدمه لزيادة نقاط اللاعب بعد الاصطدام بالجائزة في الدالة __init__
الموجودة في صنف اللاعب وهي المسؤولة عن تحديد خصائصه المختلفة، وذلك بكتابة السطر الثالث من التعليمات أدناه:
self.frame = 0 self.health = 10 self.score = 0
طبق المهارات التي تعلمتها في السابق
طبق المهارات التي تعلمتها خلال هذه السلسلة وأضف الأعداء في أماكن مناسبة، خطط لذلك ومن ثم عدّل برنامج اللعبة، حاول أيضًا الاستفادة من المبادئ الأساسية، ووظفها بطرقٍ أخرى، وصمم ألعابًا متنوعة وإن كانت وحيدة المستوى في المرحلة كبداية، ولا تنسَ التخطيط الجيد للعبة قبل البرمجة، فكر بأوسع نطاق تريده وبرمج على هذا الأساس، يساعدك ذلك في الحصول على إصداراتٍ جيدة لا تتطلب كثيرًا من التعديلات.
وفي ختام المقال نذكرك ببرنامج اللعبة كاملًا من بداية السلسلة.
#!/usr/bin/env python3 # by Seth Kenlon # GPLv3 # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pygame import sys import os ''' Variables ''' worldx = 960 worldy = 720 fps = 40 ani = 4 world = pygame.display.set_mode([worldx, worldy]) forwardx = 600 backwardx = 120 BLUE = (25, 25, 200) BLACK = (23, 23, 23) WHITE = (254, 254, 254) ALPHA = (0, 255, 0) ''' Objects ''' # x location, y location, img width, img height, img file class Platform(pygame.sprite.Sprite): def __init__(self, xloc, yloc, imgw, imgh, img): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(os.path.join('images', img)).convert() self.image.convert_alpha() self.image.set_colorkey(ALPHA) self.rect = self.image.get_rect() self.rect.y = yloc self.rect.x = xloc class Player(pygame.sprite.Sprite): """ Spawn a player """ def __init__(self): pygame.sprite.Sprite.__init__(self) self.movex = 0 self.movey = 0 self.frame = 0 self.health = 10 self.score = 0 self.is_jumping = True self.is_falling = True self.images = [] for i in range(1, 5): img = pygame.image.load(os.path.join('images', 'hero' + str(i) + '.png')).convert() img.convert_alpha() img.set_colorkey(ALPHA) self.images.append(img) self.image = self.images[0] self.rect = self.image.get_rect() def gravity(self): if self.is_jumping: self.movey += 3.2 def control(self, x, y): """ control player movement """ self.movex += x def jump(self): if self.is_jumping is False: self.is_falling = False self.is_jumping = True def update(self): """ Update sprite position """ # moving left if self.movex < 0: self.is_jumping = True self.frame += 1 if self.frame > 3 * ani: self.frame = 0 self.image = pygame.transform.flip(self.images[self.frame // ani], True, False) # moving right if self.movex > 0: self.is_jumping = True self.frame += 1 if self.frame > 3 * ani: self.frame = 0 self.image = self.images[self.frame // ani] # collisions enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False) for enemy in enemy_hit_list: self.health -= 1 # print(self.health) ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False) for g in ground_hit_list: self.movey = 0 self.rect.bottom = g.rect.top self.is_jumping = False # stop jumping # fall off the world if self.rect.y > worldy: self.health -=1 print(self.health) self.rect.x = tx self.rect.y = ty plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False) for p in plat_hit_list: self.is_jumping = False # stop jumping self.movey = 0 if self.rect.bottom <= p.rect.bottom: self.rect.bottom = p.rect.top else: self.movey += 3.2 if self.is_jumping and self.is_falling is False: self.is_falling = True self.movey -= 33 # how high to jump loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False) for loot in loot_hit_list: loot_list.remove(loot) self.score += 1 print(self.score) plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False) self.rect.x += self.movex self.rect.y += self.movey class Enemy(pygame.sprite.Sprite): """ Spawn an enemy """ def __init__(self, x, y, img): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(os.path.join('images', img)) self.image.convert_alpha() self.image.set_colorkey(ALPHA) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.counter = 0 def move(self): """ enemy movement """ distance = 80 speed = 8 if self.counter >= 0 and self.counter <= distance: self.rect.x += speed elif self.counter >= distance and self.counter <= distance * 2: self.rect.x -= speed else: self.counter = 0 self.counter += 1 class Level: def ground(lvl, gloc, tx, ty): ground_list = pygame.sprite.Group() i = 0 if lvl == 1: while i < len(gloc): ground = Platform(gloc[i], worldy - ty, tx, ty, 'tile-ground.png') ground_list.add(ground) i = i + 1 if lvl == 2: print("Level " + str(lvl)) return ground_list def bad(lvl, eloc): if lvl == 1: enemy = Enemy(eloc[0], eloc[1], 'enemy.png') enemy_list = pygame.sprite.Group() enemy_list.add(enemy) if lvl == 2: print("Level " + str(lvl)) return enemy_list # x location, y location, img width, img height, img file def platform(lvl, tx, ty): plat_list = pygame.sprite.Group() ploc = [] i = 0 if lvl == 1: ploc.append((200, worldy - ty - 128, 3)) ploc.append((300, worldy - ty - 256, 3)) ploc.append((550, worldy - ty - 128, 4)) while i < len(ploc): j = 0 while j <= ploc[i][2]: plat = Platform((ploc[i][0] + (j * tx)), ploc[i][1], tx, ty, 'tile.png') plat_list.add(plat) j = j + 1 print('run' + str(i) + str(ploc[i])) i = i + 1 if lvl == 2: print("Level " + str(lvl)) return plat_list def loot(lvl): if lvl == 1: loot_list = pygame.sprite.Group() loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png') loot_list.add(loot) if lvl == 2: print(lvl) return loot_list ''' Setup ''' backdrop = pygame.image.load(os.path.join('images', 'stage.png')) clock = pygame.time.Clock() pygame.init() backdropbox = world.get_rect() main = True player = Player() # spawn player player.rect.x = 0 # go to x player.rect.y = 30 # go to y player_list = pygame.sprite.Group() player_list.add(player) steps = 10 eloc = [] eloc = [300, 0] enemy_list = Level.bad(1, eloc) gloc = [] tx = 64 ty = 64 i = 0 while i <= (worldx / tx) + tx: gloc.append(i * tx) i = i + 1 ground_list = Level.ground(1, gloc, tx, ty) plat_list = Level.platform(1, tx, ty) enemy_list = Level.bad( 1, eloc ) loot_list = Level.loot(1) ''' Main Loop ''' while main: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() try: sys.exit() finally: main = False if event.type == pygame.KEYDOWN: if event.key == ord('q'): pygame.quit() try: sys.exit() finally: main = False if event.key == pygame.K_LEFT or event.key == ord('a'): player.control(-steps, 0) if event.key == pygame.K_RIGHT or event.key == ord('d'): player.control(steps, 0) if event.key == pygame.K_UP or event.key == ord('w'): player.jump() if event.type == pygame.KEYUP: if event.key == pygame.K_LEFT or event.key == ord('a'): player.control(steps, 0) if event.key == pygame.K_RIGHT or event.key == ord('d'): player.control(-steps, 0) # scroll the world forward if player.rect.x >= forwardx: scroll = player.rect.x - forwardx player.rect.x = forwardx for p in plat_list: p.rect.x -= scroll for e in enemy_list: e.rect.x -= scroll for l in loot_list: l.rect.x -= scroll # scroll the world backward if player.rect.x <= backwardx: scroll = backwardx - player.rect.x player.rect.x = backwardx for p in plat_list: p.rect.x += scroll for e in enemy_list: e.rect.x += scroll for l in loot_list: l.rect.x += scroll world.blit(backdrop, backdropbox) player.update() player.gravity() player_list.draw(world) enemy_list.draw(world) loot_list.draw(world) ground_list.draw(world) plat_list.draw(world) for e in enemy_list: e.move() pygame.display.flip() clock.tick(fps)
ترجمة -وبتصرف- للمقال Put some loot in your Python platformer game لصاحبيه Seth Kenlon و Jess Weichler.
اقرأ أيضًا
- المقال السابق: إضافة خاصية القفز والركض إلى لعبة في بايثون
- النسخة العربية الكاملة من كتاب البرمجة بلغة بايثون
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.