بازی Space Invaders یا مهاجمان فضایی در سال 1978 توسط Tomohiro Nishikado ساخته شد. ژانر این بازی تیراندازی است.مهاجمان فضایی اولین بازی تیراندازی ثابت نیز است. هدف بازی این است که گروهی از بیگانگان را با یک لیزر متحرک افقی از بین ببرد و با این کار امتیاز به دست آورد .مهاجمان فضایی یکی از تاثیرگذارترین بازی های ویدئویی در تمام دوران به شمار می رود. این بازی به گسترش صنعت بازی های ویدئویی از یک صنعت جدید به صنعت جهانی کمک کرد و عصر طلایی بازی های ویدئویی بازی را آغاز کرد. این بازی الهام بخش بسیاری از بازی های ویدئویی و طراحان بازی در ژانرهای مختلف بود و به اشکال مختلف ساخته و دوباره منتشر شد. در این نوشته بخش یک بازی را پیاده سازی خواهیم کرد.تصویر بازی در زیر آمده است.
برای شروع ساخت بازی چهار تا پوشه اصلی به نام های audio، code، graphics، font می سازیم مانند زیر:
سپس در پوشه code فایلی به نام main.py ایجاد می کنیم.مغز بازی در این فایل قرار دارد و کارهای اصلی در این جا انجام می شوند.
در این پوشه code فایل main.py را ساخته و سپس پنجره خود را در آن ایجاد می کنیم. کد نمایش پنجره در زیر آمده است:
import pygame,sys if __name__ == "__main__": pygame.init() WIDTH=600 HEIGHT=600 SCREEN=pygame.display.set_mode((WIDTH,HEIGHT)) Clock=pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() SCREEN.fill((30,30,30)) pygame.display.flip() Clock.tick(60)
در خط نخست کتابخانه pygame و sys را وارد برنامه می کنیم.در خط پس از آن باید یک شرط را بررسی کنیم.چون در این بازی چندین فایل با نام های گوناگون داریم باید مطمئن شویم که برای اجرای بازی فایل main اجرا می شود یا به عبارت دیگر اگر نام فایلی که اجرا می کنیم main بود، بازی اجرا شود.به همین علت از خط "__if "__name__" == "__main برای بررسی این شرط استفاده می کنیم. اگر کد بالا را اجرا کنیم پنجره زیر را می بینیم:
این بازی را به صورت شی گرا می سازیم.برای این منظور کلاس Game را در فایل main.py ایجاد می کنیم.این کلاس فعلا یک متد سازنده و یک متد به نام run برای اجرای برنامه خواهد داشت. بعدا متدهای دیگری به آن می افزاییم.بخش اصلی بازی در متد run قرار دارد. یعنی مدیریت کردن رویدادها، رسم تصاویر، به روزآوری پنجره و غیره همگی در این متد انجام می شوند.برای ساخت کلاس Game کد زیر را بالای خط "__if "__name__" == "__main قرار می دهیم.
class Game: def __init__(): pass def run(self): pass
یک نمونه از کلاس Game ایجاد می کنیم.خط زیر را پس از Clock در متد run قرار می دهیم:
game=Game()
سپس با استفاده از شی ای که ساختیم یعنی game متد run را فرا خوانی می کنیم مانند زیر:
game.run()
اگر کد بالا را اجرا کنید تغییری را نمی بینید و دوباره همان پنجره خاکستری را می بینیم.
import pygame,sys class Game: def __init__(): pass def run(self): pass if __name__ == "__main__": pygame.init() WIDTH=600 HEIGHT=600 SCREEN=pygame.display.set_mode((WIDTH,HEIGHT)) Clock=pygame.time.Clock() game=Game() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() SCREEN.fill((30,30,30)) game.run() pygame.display.flip() Clock.tick(60)
یک فایل جدید به نام player.py در پوشه code درست می کنیم و کدهای زیر را در آن وارد می کنیم:
import pygame class Player(pygame.sprite.Sprite): def __init__(self,pos): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos)
برای ساخت بازیکن از کد بالا استفاده می کنیم.خط نخست pygame را وارد برنامه می کند.خط بعدی کلاس Player را می سازد.این کلاس از pygame.sprite.Sprite ارث بری می کند. pygame.sprite.Sprite یک کلاس پایه ساده برای ساخت اشیا مرئی بازی است.خط بعد متد سازنده این کلاس است. این متد متغیر pos را به عنوان ورودی می گیرد. از این متغیر برای تعیین جای بازیکن استفاده می کنیم.
بازیکن دارای یک تصویر است که در پوشه graphics قرار دارد. برای بارگذاری عکس از self.image و برای ایجاد یک مستطیل دور تصویر از self.rect استفاده می کنیم. توجه داشته باشید رشته ای را که برای pygame.image.load می فرستیم باید مسیر تصویر در سیستم تان باشد. من پوشه بازی را در Desktop خود قرار داده ام و مسیر پوشه من با شما فرق می کند. تغییرهای زیر را در main.py ایجاد می کنیم تا بتوانیم از کلاس Player استفاده کنیم. کلاس Player را با خط from player import Player وارد برنامه می کنیم. در متد __init__ جای تصویر را مشخص می کنیم.
import pygame,sys,os from player import Player class Game: def __init__(self): player_sprite=Player((WIDTH/2,HEIGHT)) self.player=pygame.sprite.GroupSingle(player_sprite) def run(self): self.player.draw(SCREEN) if __name__ == "__main__": pygame.init() WIDTH=600 HEIGHT=600 SCREEN=pygame.display.set_mode((WIDTH,HEIGHT)) Clock=pygame.time.Clock() game=Game() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() SCREEN.fill((30,30,30)) game.run() pygame.display.flip() Clock.tick(60)
اگر کد بالا را اجرا کنیم تصویر بازیکن را خواهیم دید:
برای دریافت همه ورودی های کاربر تابع get_input را در فایل player.py می سازیم. این متد تک تک ورودی هایی را که کاربر تولید می کند دریافت می کند. از میان همه این ورودی ها تنها به دو ورودی برای حرکت بازیکن اهمیت می دهیم. حرکت به راست و حرکت به چپ. برای حرکت به راست باید رویداد pygame.K_RIGHT و برای حرکت به چپ باید pygame.K_LEFT را بررسی کنیم. اگر رویداد pygame.K_RIGHT بود سرعت 5 واحد افرایش می یابد و اگر pygame.K_LEFT بود 5 واحد کاهش می یابد.
def get_input(self): keys=pygame.key.get_pressed() if keys[pygame.K_RIGHT]: self.rect.x += self.speed elif keys[pygame.K_LEFT]: self.rect.x -= self.speed
متد update را برای به روزآوری رویدادها در این فایل می سازیم:
def update(self): self.get_input()
سرعت حرکت بازیکن را نیز در این فایل با self.speed تعریف می کنیم و مقدار آن را 5 قرار می دهیم. تا این لحظه کلاس Player به شکل زیر خواهد بود:
import pygame class Player(pygame.sprite.Sprite): def __init__(self,pos): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=5 def get_input(self): keys=pygame.key.get_pressed() if keys[pygame.K_RIGHT]: self.rect.x += self.speed elif keys[pygame.K_LEFT]: self.rect.x -= self.speed def update(self): self.get_input()
اگر بازی را اجرا کنیم و کلیدهای چپ و راست را فشار دهیم می بینیم که سفینه ما حرکت می کند.
با اجرای بازی می بینیم که اگر دست خود را روی دکمه چپ یا راست بگذاریم و برنداریم سفینه از صفحه خارج می شود.برای محدود کردن سفینه به پنجره بازی، مقدار 5 را به سازنده Player در کلاس Game می افزاییم:
def __init__(self): player_sprite=Player((WIDTH/2,HEIGHT),WIDTH,5) self.player=pygame.sprite.GroupSingle(player_sprite)
به دنبال آن باید تغییرهای لازم را در کلاس Player اعمال کنیم:
class Player(pygame.sprite.Sprite): def __init__(self,pos,constraint,speed): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=speed self.max_x_constraint=constraint
تابع constraint برای محدود کردن حرکت سفینه است. آن را نیز به صورت زیر به کلاس Player می افزاییم:
def constraint(self): if self.rect.left <= 0: self.rect.left=0 if self.rect.right >= self.max_x_constraint: self.rect.right= self.max_x_constraint
اگر مقدار left یا سمت چپ سفینه منفی شود یعنی سفینه از سمت چپ صفحه خارج شده است و اگر right سفینه از max_x_constraint که برابر با عرض است بیش تر شود یعنی سفینه از سمت راست صفحه خارج شده است. در هر دو این حالت ها سفینه باید در پنجره بماند. پس اگر بخواهد از سمت چپ خارج شود self.rect.left باید صفر شود و از این مقدار کمتر نشود و اگر بخواهد از سمت راست خارج شود self.rect.right باید برابر با self.max_x_constraint شود و از آن بیش تر نشود. برای دیدن نتیجه کار باید متد constraint را فراخوانی کنیم.این متد را در update در کلاس Player فراخوانی می کنیم.
def update(self): self.get_input() self.constraint()
کد Player تا این لحظه به صورت زیر خواهد بود:
import pygame class Player(pygame.sprite.Sprite): def __init__(self,pos,constraint,speed): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=speed self.max_x_constraint=constraint def get_input(self): keys=pygame.key.get_pressed() if keys[pygame.K_RIGHT]: self.rect.x += self.speed elif keys[pygame.K_LEFT]: self.rect.x -= self.speed def constraint(self): if self.rect.left <= 0: self.rect.left=0 if self.rect.right >= self.max_x_constraint: self.rect.right= self.max_x_constraint def update(self): self.get_input() self.constraint()
اگر بازی را اجرا کنیم و می بینیم که سفینه دیگر از پنجره خارج نمی شود.
دوست داریم با فشردن کلید space سفینه لیزر شلیک کند.لیزر یک مستطیل کوچک سفید رنگ است. برای ساخت لیزر کد های زیر را به کلاس Player می افزاییم:
if keys[pygame.K_SPACE]: self.shoot_laser()
سپس متد shoot_laser را می سازیم:
def shoot_laser(self): print("laser")
برای اطمینان از کار کردن متد shoot_laser از متد print استفاده می کنیم. اگر space را فشار دهیم کلمه laser در کنسول چاپ می شود. این کلمه بی توقف نوشته می شود مانند تصویر زیر:
برای این که لیزر دارای یک آستانه کارکرد شود یعنی پشت سر هم نتواند شلیک کند و پس از آن خنک شود و دوباره شلیک را آغاز کند خط زیر را به متد سازنده __init__ در کلاس Player می افزاییم:
self.ready=True self.laser_time=0 self.laser_cooldown=600
آمادگی سفینه برای شلیک کردن را متغیر ready مشخص می کند. laser_cooldown مدت زمانی است که طول می کشد سفینه خنک شود و دوباره آماده شلیک شود. برای پیاده سازی سیستم خنک سازی سفینه از timer (زمان سنج) استفاده می کنیم. از متغیر laser_time برای این منظور استفاده می کنیم.
if keys[pygame.K_SPACE] and self.ready: self.shoot_laser() self.ready=False
کد بالا این شرط را بررسی می کند که اگر کلید space فشار داده شده باشد و ready دارای مقدار True باشد شلیک بکند. حالا اگر کد را اجرا کنیم و space را فشار دهیم کلمه laser یک بار نوشته می شود. کد فایل player.py تا این لحظه:
import pygame class Player(pygame.sprite.Sprite): def __init__(self,pos,constraint,speed): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=speed self.max_x_constraint=constraint self.ready=True self.laser_time=0 self.laser_cooldown=600 def get_input(self): keys=pygame.key.get_pressed() if keys[pygame.K_RIGHT]: self.rect.x += self.speed elif keys[pygame.K_LEFT]: self.rect.x -= self.speed if keys[pygame.K_SPACE] and self.ready: self.shoot_laser() self.ready=False def constraint(self): if self.rect.left <= 0: self.rect.left=0 if self.rect.right >= self.max_x_constraint: self.rect.right= self.max_x_constraint def shoot_laser(self): print("laser") def update(self): self.get_input() self.constraint()
اگر بازی را اجرا کنیم خواهیم دید که کلمه لیزر تنها یکبار در کنسول نوشته می شود. برای این که با هر بار فشار دادن space کلمه laser نوشته شود باید ready دارای مقدار True شود پس کد زیر را داریم:
if keys[pygame.K_SPACE] and self.ready: self.shoot_laser() self.ready=False self.laser_time=pygame.time.get_ticks()
pygame.time.get_ticks زمان را از هنگامی که pygame.init فراخوانی شده است در یکای میلی ثانیه برمی گرداند. با تابع recharge مقدار ready را True می کنیم تا سفینه دوباه بتواند شلیک داشته باشد:
def recharge(self): if not self.ready: current_time=pygame.time.get_ticks() if current_time - self.laser_time >= self.laser_cooldown: self.ready = True
در تابع بالا اگر تفاوت زمان فعلی یعنی current_time و laser_time از 600 میلی ثانیه بیش تر باشد به این معنی است که سفینه خنک شده است و دوباره آماده شلیک است. پس ready را True می کنیم و در آخر باید این تابع را در update فراخوانی کنیم تا تاثیر آن را ببینیم:
def update(self): self.get_input() self.constraint() self.recharge()
کد کلاس Player تا این لحظه:
import pygame class Player(pygame.sprite.Sprite): def __init__(self,pos,constraint,speed): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=speed self.max_x_constraint=constraint self.ready=True self.laser_time=0 self.laser_cooldown=600 def get_input(self): keys=pygame.key.get_pressed() if keys[pygame.K_RIGHT]: self.rect.x += self.speed elif keys[pygame.K_LEFT]: self.rect.x -= self.speed if keys[pygame.K_SPACE] and self.ready: self.shoot_laser() self.ready=False self.laser_time=pygame.time.get_ticks() def recharge(self): if not self.ready: current_time=pygame.time.get_ticks() if current_time - self.laser_time >= self.laser_cooldown: self.ready = True def constraint(self): if self.rect.left <= 0: self.rect.left=0 if self.rect.right >= self.max_x_constraint: self.rect.right= self.max_x_constraint def shoot_laser(self): print("laser") def update(self): self.get_input() self.constraint() self.recharge()
با اجرای بازی و هر بار فشار دادن space کلمه laser تنها یک بار نوشته می شود.
برای نمایش لیزر یک فایل جدید با نام laser.py درست می کنیم:
import pygame class Laser(pygame.sprite.Sprite): def __init__(self,pos): super().__init__() self.image=pygame.Surface((4,20)) self.image.fill('white') self.rect=self.image.get_rect(center=pos)
سپس فایل laser.py را در کلاس Player وارد می کنیم:
import pygame from laser import Laser
سپس داریم:
class Player(pygame.sprite.Sprite): def __init__(self,pos,constraint,speed): super().__init__() self.image=pygame.image.load(r"C:/Users/Rahmani/Desktop/space_invaders/graphics/player.png") self.rect=self.image.get_rect(midbottom=pos) self.speed=speed self.max_x_constraint=constraint self.ready=True self.laser_time=0 self.laser_cooldown=600 self.lasers=pygame.sprite.Group()
متد shoot_laser را به صورت زیر اصلاح می کنیم:
def shoot_laser(self): self.lasers.add(Laser(self.rect.center))
اگر space را فشاد دهیم هیچ اتفاقی نمی افتد.در فایل main.py کد زیر را می افزاییم:
def run(self): self.player.update() self.player.sprite.lasers.draw(SCREEN) self.player.draw(SCREEN)
اگر بازی را اجرا کنیم تصویر زیر را خواهیم دید:
لیزر ثابت است و حرکتی نمی کند.برای این لیزر حرکت کند تغییرات زیر را در فایل laser.py اعمال می کنیم:
import pygame class Laser(pygame.sprite.Sprite): def __init__(self,pos,speed=-8): super().__init__() self.image=pygame.Surface((4,20)) self.image.fill('white') self.rect=self.image.get_rect(center=pos) self.speed=speed def update(self): self.rect.y += self.speed
اگر بازی را اجرا کنیم و space را فشار دهیم می بینیم که لیزر به سمت بالا شلیک می شود.سرعت آن 8- است. برای این که لیزر به سمت بالا حرکت کند این عدد را منفی درنظر گرفته ایم.برای نشان داده شدن لیزر کد زیر را به فایل player.py می افزاییم:
def update(self): self.get_input() self.constraint() self.recharge() self.lasers.update()
تغییرهای زیر را برای حذف لیزرها(در فایل laser.py) داریم:
import pygame class Laser(pygame.sprite.Sprite): def __init__(self,pos,speed=-8,screen_height): super().__init__() self.image=pygame.Surface((4,20)) self.image.fill('white') self.rect=self.image.get_rect(center=pos) self.speed=speed self.height_y_constraint=screen_height def destroy(self): if self.rect.y <= -50 or self.rect.y >= self.height_y_constraint+50: self.kill() def update(self): self.rect.y += self.speed self.destroy()
سپس در فایل player.py داریم:
def shoot_laser(self): self.lasers.add(Laser(self.rect.center,-8,self.rect.bottom))
و باید اصلاح زیر در فایل laser.py انجام شود:
def __init__(self,pos,speed,screen_height):
یک فایل جدید با نام obstacle.py ایجاد می کنیم.
import pygame class Block(pygame.sprite.Sprite): def __init__(self,size,color,x,y): super().__init__() self.image = pygame.Surface((size,size)) self.image.fill(color) self.rect = self.image.get_rect(topleft = (x,y)) shape = [ ' xxxxxxx', ' xxxxxxxxx', 'xxxxxxxxxxx', 'xxxxxxxxxxx', 'xxxxxxxxxxx', 'xxx xxx', 'xx xx']
سپس در فایل main داریم:
import obstacle
در ادامه در فایل main داریم:
class Game: def __init__(self): # player setup player_sprite=Player((WIDTH/2,HEIGHT),WIDTH,5) self.player=pygame.sprite.GroupSingle(player_sprite) # obstacle setup self.shape=obstacle.shape self.block_size=6 self.blocks=pygame.sprite.Group() self.create_obstacle() def create_obstacle(self): for row_index,row in enumerate(self.shape): for col_index,col in enumerate(row): if col =='x': x=col_index*self.block_size y=row_index*self.block_size block=obstacle.Block(self.block_size,(241,79,80),x,y) self.blocks.add(block)
ابتدا تنظیم های اولیه برای مانع ها را ایجاد می کنیم. چهار تا مانع برای سفینه داریم. متغیرهای shape، block_size و blocks را برای هر یک از مانع ها (obstacle) و متد create_obstacle را برای ساخت آن ها داریم. متد create_obstacle را به صورت زیر تعریف می کنیم:
def create_obstacle(self): for row_index,row in enumerate(self.shape): for col_index,col in enumerate(row): if col =='x': x=col_index*self.block_size y=row_index*self.block_size block=obstacle.Block(self.block_size,(241,79,80),x,y) self.blocks.add(block)
برای نمایش مانع ها خط زیر را به متد run می افزاییم:
def run(self): self.player.update() self.player.sprite.lasers.draw(SCREEN) self.player.draw(SCREEN) self.blocks.draw(SCREEN)
اگر بازی را اجرا کنیم تصویر زیر را خواهیم دید:
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.