前言
作為一個(gè)編程小白選手,經(jīng)過一段時(shí)間的基礎(chǔ)語法學(xué)習(xí),終于迎來了首個(gè)項(xiàng)目學(xué)習(xí)眨唬,從一步步的模仿到理解到每個(gè)步驟的思路想法会前,每個(gè)類之間的聯(lián)系與構(gòu)造,還有些編程中的一些小細(xì)節(jié)匾竿,如何養(yǎng)成一個(gè)編程的好習(xí)慣瓦宜。
分析
外星人入侵作為一個(gè)入門小游戲項(xiàng)目,整體來說思路是比較簡單搂橙,代碼行也不多歉提,總計(jì)就是500+行,除去一些縮進(jìn)空行区转,就沒有多少了。當(dāng)我們?nèi)プ鲆粋€(gè)項(xiàng)目的時(shí)候版扩,無論是難的還是簡單的废离,都是需要進(jìn)行一個(gè)分析,這樣我們實(shí)際去寫的時(shí)候礁芦,思路就會清晰很多蜻韭,把一個(gè)項(xiàng)目的功能拆分出來,再各個(gè)實(shí)現(xiàn)柿扣,最后串聯(lián)起來肖方。
言歸正傳,這個(gè)項(xiàng)目主要分為9個(gè)模塊未状,9個(gè)模塊及每個(gè)模塊的作用如下:
-
alien.py →class_Alien 主要是用來放繪制外星人的圖像及位置信息
-
bullet.py→class_Bullet 主要是用來放置繪制飛船發(fā)射子彈圖像及位置信息
-
button.py→class_Button 主要是用來放置繪制游戲開始按鈕的圖像及位置信息
-
game_stats.py→ class Game_stats 主要是游戲中的數(shù)據(jù)參數(shù)俯画,如得分,游戲等級等等
-
scoreboard.py→ class Scoreboard 主要是用來放置游戲中各個(gè)計(jì)分板的圖像繪制和位置信息
-
settings.py→class Settings 主要是用來放置游戲中初始化參數(shù)司草,像屏幕大小顏色等等
-
ship.py→class Ship 主要用來放置繪制飛船的圖像及位置信息
-
alien_invasion.py 主要是用來放置運(yùn)行游戲的主方法
-
game_functions.py 主要是用來放置游戲功能的實(shí)現(xiàn)艰垂,如屏幕的圖像更新,飛船的移動埋虹,飛船如何發(fā)射子彈等等猜憎,基本功能代碼都會集中在這個(gè)包里面
image.png
步驟如下:
創(chuàng)建游戲窗口→添加飛船圖像→控制飛船移動→添加子彈圖像及移動→
添加一個(gè)外星人圖像→添加一群外星人圖像及移動→檢測外星人圖像與子彈碰撞,與飛船碰撞和碰到屏幕底端的情況及反應(yīng)→添加游戲開始按鈕圖像及反應(yīng)→添加計(jì)分板圖像:包括玩家得分搔课,歷史最高得分胰柑,難度級別顯示,玩家剩余飛船數(shù)量
實(shí)現(xiàn)
創(chuàng)建游戲窗口
首先來創(chuàng)建游戲窗口爬泥,創(chuàng)建一個(gè)alien_invasion.py的包柬讨,導(dǎo)入pygame庫(這個(gè)項(xiàng)目是基于pygame的庫下完成的),創(chuàng)建一個(gè)def run_game()的主方法急灭,pygame.init()初始化背景參數(shù)姐浮,創(chuàng)建一個(gè)screen的對象pygame.display.set_mode()函數(shù)傳入我們設(shè)置好的寬高參數(shù)就可以創(chuàng)建窗口,用(1200(寬),650(高))一個(gè)元組的實(shí)參傳入(具體參數(shù)可以根據(jù)自身屏幕大小設(shè)置)葬馋,再用函數(shù)pygame.display.set_caption()傳入str可以命名窗口卖鲤,將顯示在窗口的左上角肾扰。我們創(chuàng)建的screen對象是一個(gè)surface(面板),還有接下來我們創(chuàng)建的飛船蛋逾,子彈集晚,都是一個(gè)surface(面板)set_mode函數(shù)返回的就是一個(gè)surface。
接下來就是游戲需要的主循環(huán)区匣,用來不斷更新的屏幕的圖像和玩家行為偷拔,直至玩家退出游戲,我們用while True來循環(huán)亏钩,并在里面設(shè)置一個(gè)檢測pygame.QUIT的事件跳出循環(huán)莲绰,避免造成死循環(huán)。用函數(shù)pygame.event.get()可以得到玩家鼠標(biāo)和按鍵響應(yīng)事件姑丑,再用一個(gè)for in循環(huán)得到每個(gè)事件蛤签,并進(jìn)行判斷是否有pygame.QUIT事件,如果有就用sys.exit()函數(shù)來退出游戲跳出循環(huán)(這個(gè)時(shí)候我們就需要導(dǎo)入sys栅哀,在代碼最上面import sys)最后再用pygame.display.flip()繪制的圖像顯示出來震肮,這個(gè)是放于run_game()最后的地方。代碼如下:
此時(shí)運(yùn)行run_game()留拾,我們將得到一個(gè)黑色的窗口:
接下來我們給窗口上色戳晌,用的是RGB值,用紅色痴柔,綠色沦偎,藍(lán)色組成,每個(gè)值取值范圍為0到255竞帽,創(chuàng)建一個(gè)screen_color賦值(230扛施,230,230),我們需要不斷更新這個(gè)顏色屹篓,所以放在while循環(huán)里疙渣,用fill()函數(shù),傳入我們的screen_color實(shí)參堆巧,在我們創(chuàng)建的窗口屏幕screen上妄荔,所以代碼如下:
def run_game():
pygame.init()
screen = pygame.display.set_mode((1200,650))
pygame.display.set_caption("Alien Invasion")
screen_color = (230,230,230)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(screen_color)
pygame.display.flip()
運(yùn)行得到畫面:
建議可以熟悉下pycharm的快捷鍵,這樣會更快的提高谍肤,例如當(dāng)你不熟悉一個(gè)函數(shù)的使用時(shí)候啦租,可以選中之后按Ctrl + B或者Ctrl + 鼠標(biāo)點(diǎn)擊函數(shù)可以直接跳轉(zhuǎn)到函數(shù)的聲明,會幫助你更加理解函數(shù)的使用荒揣,變量也是可以這樣操作篷角,當(dāng)你看完之后可以按Alt + 左鍵跳回就非常放便
快捷鍵使用
setting模塊(存放游戲設(shè)置參數(shù))
接下來可以創(chuàng)建一個(gè)setting.py的模塊,里面創(chuàng)建一個(gè)Settting類系任,用于存放游戲參數(shù)恳蹲,這樣以來就方便引用虐块,而且當(dāng)你需要修改的時(shí)候不需要全部都去修改,而是只在Setting的類中修改即可嘉蕾,然后回到alien_invasion.py中導(dǎo)入Setting創(chuàng)建一個(gè)Setting的對象ai_settings贺奠,之后只需用對象.變量名即可引用,整理結(jié)果如下:
class Setting():
def __init__(self):
#初始化游戲的設(shè)置
#屏幕參數(shù)設(shè)置
self.screen_width = 1200
self.screen_height = 650
self.screen_color = (230,230,230)
import pygame
import sys
from setting import Setting
def run_game():
pygame.init()
ai_settings = Setting()
screen = pygame.display.set_mode(
(ai_settings.screen_width,ai_settings.button_height))
pygame.display.set_caption("Alien Invasion")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.screen_color)
pygame.display.flip()
run_game()
創(chuàng)建飛船
接下來我們就要創(chuàng)建飛船了错忱,使用位圖.bmp文件儡率,需要在項(xiàng)目文件夾中創(chuàng)建一個(gè)Images的文件夾,將圖片放入文件夾中以清,再創(chuàng)建一個(gè)ship.py的模塊儿普,里面有一個(gè)class Ship類并進(jìn)行初始化,def init(self,screen)這里需要傳入我們創(chuàng)建的窗口screen玖媚,因?yàn)槲覀兊娘w船是出現(xiàn)在這個(gè)窗口屏幕上的箕肃,然后需要讀取我們飛船的圖片,創(chuàng)建一個(gè)self.image接收今魔,用pygame.image.loda(Images’/ship.bmp)前面為文件夾名,后面會文件名障贸,再用get_rect()函數(shù)來得到這個(gè)圖片的矩形rect對象错森,這個(gè)rect對象會有一個(gè)坐標(biāo)位置信息由四個(gè)參數(shù)組成(left,top篮洁,width涩维,height),width和height又表示rect矩形的寬和高
圖像都是從原點(diǎn)(0,0)創(chuàng)建的
- top和left可以理解成圖像距離原點(diǎn)的坐標(biāo)
- bottom是rect矩形下邊緣距離x軸的距離袁波,也可理解成y坐標(biāo)
- width就是rect圖像的寬度瓦阐,height是rect圖像的高度
*center是圖像的中心,然后分出centerx(中心在x軸上的坐標(biāo))
centery(中心在y軸上的坐標(biāo)) - right 可以理解成rect矩形圖像右邊緣x軸坐標(biāo)
所以我們導(dǎo)入ship.bmp篷牌,創(chuàng)建ship_rect接受用函數(shù)get_rect()得到飛船rect矩形圖像的坐標(biāo)位置信息睡蟋。要將飛船放于screen屏幕的底部中間,所以要將飛船的bottom等于屏幕的bottom枷颊,在將飛船的centerx等于screen屏幕的centerx戳杀,之后在創(chuàng)建一個(gè)繪制飛船圖像的方法def blitme(),方法里面用blit()在屏幕上screen繪制飛船夭苗,傳入飛船的圖像self.image和飛船圖像的self.rect信卡,代碼如下:
class Ship():
def __init__(self,screen):
self.screen = screen
#飛船參數(shù)
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
#將飛船放在屏幕底部中間
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
def blitme(self):
#繪制飛船
self.screen.blit(self.image,self.rect)
然后在alien_invasion中導(dǎo)入Ship的類,創(chuàng)建一個(gè)Ship類對象题造,需要傳入screen參數(shù)傍菇,最后在循環(huán)中使用ship.blitme()方法繪制飛船圖像
ship == Ship(screen)
whlie True:
...
ship.blitme()
控制飛船
接下來就是控制飛船移動的,還是在遍歷event.get()里用if語句判斷界赔,如果檢測到事件是按下鍵盤事件丢习,然后在用if語句判斷牵触,按下鍵盤事件的按鈕是不是對應(yīng)的方向鍵,根據(jù)上面的坐標(biāo)圖泛领,向左鍵則是ship.rect.centerx -=1荒吏,向右則是ship.rect.centerx += 1;當(dāng)玩家持續(xù)按下左右鍵的時(shí)候渊鞋,就需要判斷按下按鍵事件和松開按鍵事件绰更,先在Ship類里面的init方法里添加兩個(gè)變量self.moving_right 和self.moving_left并賦值False,在創(chuàng)建一個(gè)方法def update()用if語句判斷如果為True的反應(yīng)
def update():
if moving_right:
self.rect.centerx += 1
if moving_left:
self.rect.centerx -= 1
再回到for循環(huán)判斷锡宋,按下和松開的響應(yīng):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key ==pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
接下來我們需要限制飛船的運(yùn)行范圍儡湾,從上面的坐標(biāo)圖來看,我們可以判斷飛船圖像的right如果小于screen窗口的right执俩,那就是還可以繼續(xù)向右移動徐钠,如果飛船的left是大于0的話,那就是還沒有到屏幕坐邊緣役首,還可以繼續(xù)向左移動尝丐,所以我們可以在ship.update里面加多一個(gè)判斷條件,用and連接衡奥,只有同時(shí)滿足這兩個(gè)條件爹袁,飛船才能繼續(xù)移動,因?yàn)橐玫斤w船的rect和screen的rect矮固,所以需要在update方法里面?zhèn)魅雜hip和screen作為參數(shù)失息,代碼如下:
def update(self):
if moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += 1
if moving_left and self.rect.left > 0:
self.rect.centerx -= 1
如果覺得飛船移動的太慢的話,可以設(shè)置一個(gè)飛船速度的變量档址,建議賦值浮點(diǎn)值盹兢,這樣可以更細(xì)致的控制飛船速度,所以我們把+1-1換成+-我們嘎剛剛設(shè)置的飛船速度變量守伸,但是rect是整數(shù)绎秒,整數(shù)不能和浮點(diǎn)值加減,所以先把rect.centerx轉(zhuǎn)換為浮點(diǎn)值float賦給變量center含友,最后加減完之后再賦值給center賦值給rect.centerx
def __init__(self):
...
self.ship_centerx = float(self.rect.centerx)
def ship_update(self):
#更新飛船位置
if self.ship_moving_left and self.rect.left > 0:
self.ship_centerx -= self.ai_settings.ship_speed_factor
if self.ship_moving_right and self.rect.right < self.screen_rect.right:
self.ship_centerx += self.ai_settings.ship_speed_factor
self.rect.centerx = self.ship_centerx
注意這里飛船速度的變量是在Setting里創(chuàng)建的替裆,所以我們在Ship里面要導(dǎo)入Setting類,并在init里面?zhèn)魅隺i_settings對象,ship對象里面我們也要傳入相應(yīng)的實(shí)參ai_settings
class Ship():
def __init__(self,ai_settings,screen):
...
self.ai_settings = ai_settings
ship = Ship(ai_settings窘问,screen)
game_functions的作用(整理代碼辆童,讓代碼更容易管理和解讀)
while True里的循環(huán)現(xiàn)在代碼比較多,也為了更好理解代碼惠赫,現(xiàn)在創(chuàng)建一個(gè)game_functions的模塊把鉴,用于存放功能代碼,在里面創(chuàng)建def check_events(按鍵和鼠標(biāo)響應(yīng)事件)和screen_update(窗口更新,把需要在窗口上更新的圖像歸在里面)庭砍,check_events里面還可以在分出響應(yīng)按下和松開的兩個(gè)方法场晶。最后在alien_invasion.py中導(dǎo)入game_function(用import game_function as gs 導(dǎo)入,并簡化為gs怠缸,通過gs來調(diào)用就可以了)如下:
import sys
import pygame
def check_event(ai_settings,screen,ship):
#響應(yīng)按鍵和鼠標(biāo)事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown(event,ai_settings,screen,ship)
elif event.type == pygame.KEYUP:
check_keyup(event,ship)
def check_keydown(event,ai_settings,screen,ship):
#響應(yīng)按下
if event.key == pygame.K_RIGHT:
ship.ship_moving_right = True
elif event.key == pygame.K_LEFT:
ship.ship_moving_left = True
def check_keyup(event,ship):
#響應(yīng)松開
if event.key == pygame.K_RIGHT:
ship.ship_moving_right = False
if event.key == pygame.K_LEFT:
ship.ship_moving_left = False
def screen_update(ai_settings,ship,screen):
#更新屏幕圖像
screen.fill(ai_settings.screen_color)
ship.blitme()
pygame.display.flip()
import game_functions as gs
...
while True:
#監(jiān)聽鼠標(biāo)和按鍵事件
gs.check_event(ai_settings,screen,ship)
ship.ship_update()
gs.screen_update(ai_settings,ship,screen)
子彈的創(chuàng)建
接下來創(chuàng)建飛船需要的子彈诗轻,在bullet模塊里創(chuàng)建,過程跟上面的飛船差不多揭北,但是需要通過繼承Sprite類使用精靈將游戲相關(guān)的元素編組并操作編組中的所有元素扳炬,同時(shí)子彈是沒有bmp位圖的,所有需要用pygame.Rect函數(shù)來繪制搔体,需要傳輸x恨樟,y軸上的坐標(biāo)和子彈的寬高,同時(shí)在Settging里面創(chuàng)建子彈需要的寬高和顏色疚俱,子彈的速度劝术,我們在原點(diǎn)創(chuàng)建,之后在把子彈設(shè)置子彈飛船的中心呆奕,這樣看起來就是子彈從飛船中射出养晋,所以子彈的矩形圖像就是:
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self,ai_settings,screen,ship):
'''
python2.7中需要super(self,Bullet).init()這種寫法,python3也可以這樣寫梁钾,
但是也可省略匙握,用下面這種寫法
'''
super().init()
self.rect = pygame.Rect(0,0,ai_setting.bullet_width,ai_setting.bullet_height)
子彈的更新方法,因?yàn)槭菑娘w船射出陈轿,所以是在屏幕從下往上的,所以self.rect.y -= 子彈速度秦忿,可以借鑒飛船移動速度用float值麦射。
子彈的繪制用pygame.draw.rect(Surface,color灯谣,rect)或者Surface.fill(color潜秋,rect)都可以
在alien_invasion中創(chuàng)建一個(gè)子彈編組bullets = Group()用于存放每一個(gè)創(chuàng)建的子彈。在while循環(huán)里面通過編組bullets調(diào)用update方法可以對編組中每個(gè)元素調(diào)用update方法胎许。
子彈的射擊
子彈的射擊同樣通過按鍵響應(yīng)事件判斷峻呛,如果玩家按下的是空格鍵,就創(chuàng)建一個(gè)子彈對象辜窑,并將子彈加入子彈編組里面钩述,在通過遍歷子彈編組在屏幕上繪制出每個(gè)子彈,bullets.sprites()可以返回一個(gè)列表穆碎。
def screen_update(ai_settings,screen,ship,bullets):
...
for bullet in bullets.sprites():
bullet.draw_bullet()
子彈管理
會發(fā)現(xiàn)發(fā)射的子彈雖然到達(dá)了屏幕頂端之后消失牙勘,其實(shí)并沒有消失,子彈位置還在不斷往Y軸負(fù)數(shù)區(qū)域更新,這樣會到導(dǎo)致子彈越來越多方面,可以用過bullets.copy()復(fù)制一個(gè)bullets放钦,遍歷判斷子彈的位置,如果子彈的bottom位置超過屏幕也就是0恭金,那就使用Group.remove()函數(shù)操禀,將超過屏幕的子彈刪除,同時(shí)我們限制子彈的數(shù)量横腿,通過len(bullets)得到子彈編組的數(shù)量颓屑,用if語句判斷如果小于我們設(shè)定的子彈數(shù)量就創(chuàng)建子彈
外星人創(chuàng)建
外星人的創(chuàng)建就和飛船的創(chuàng)建比較相似,因?yàn)槎际怯衎mp位圖的蔑水,就不需要我們自己創(chuàng)建邢锯,只需要將外星人的圖片讀取進(jìn)來就可以了,唯一不同的就是外星人是一群的搀别,所以我們要借鑒創(chuàng)建子彈的方法丹擎,繼承Spirte類,創(chuàng)建一個(gè)外星人列表歇父,同時(shí)要計(jì)算窗口可以容納多少外星人蒂培,在使用for循環(huán)嵌套創(chuàng)建外星人群,最后在screen_update中用編組調(diào)用draw(surface)方法繪制
每個(gè)外星人最初都是從(0榜苫,0)的地方創(chuàng)建护戳,所以我們將外星人的寬度設(shè)置成外星人的左邊距,將外星人的高度設(shè)置為外星人的上邊距
計(jì)算屏幕可以容納多少個(gè)外星人垂睬,每行容納的外星人空間可以通過窗口的寬度 - 兩個(gè)外星人寬度(預(yù)設(shè)屏幕兩端的空余空間)媳荒,將其設(shè)置為available_space_x
同時(shí)兩個(gè)外星人之間需要有一定空間,所以設(shè)定一個(gè)外星人實(shí)際需要的空間是外星人寬度的兩倍驹饺,所以得出每行可以容納的外星人數(shù)量為:
number_aliens_x = int(available_space_x/(2*alien_width))
我們在game_functions.py創(chuàng)建一個(gè)alien_fleet()方法钳枕,用于創(chuàng)建外星人群:
def alien_fleet():
alien = Alien(ai_settings,screen)
alien_width = alien.rect.width
available_space_x = ai_settings.screen_width - 2*alien_width
number_aliens_x = int(available_space_x/(2*alien_width))
for alien_number in range(number_aliens_x):
alien = Alien(ai_settings,screen)
alien.x = alien.width + 2 *alien_width *alien_number
alien.rect.x = alien.x
aliens.add(alien)
接下來計(jì)算屏幕可以放下幾行外星人,存放外星人的空間 = 窗口高度 - 第一行外星人的上邊距 - 外星人之間的間隔 - 留給玩家反應(yīng)的距離 - 飛船的距離赏壹,將前面三種參數(shù)都設(shè)置成外星人高度鱼炒,即為3*外星人高度 - 飛船高度,屏幕存放外星人的空間為: available_space_y = ai_settings.screen_height - 3 * alien_height - ship._height
可以存放的行數(shù)為:number_rows = int(available_space_y / (2 * alien_height)),結(jié)合起來代碼:
def alien_fleet():
...
for row_number in range(number_rows):
for alien_number in range((number_aliens_x):
...
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
最后整理下代碼蝌借,將其整理四個(gè)方法昔瞧,def get_number_aliens_x();
def get_number_rows()菩佑;def create_alien()自晰;def create_fleet(),分別完成獲取外星人每行存放多少個(gè)外星人擎鸠,獲取可以存放幾行外星人缀磕,for循環(huán)里面創(chuàng)造外星人及創(chuàng)建外星人群
外星人群的移動
設(shè)置外星人群的平行移動速度alien_speed_factor = 1,下降速度alien_drop_speed = 10,alien_direction = 1為左右移動(1為向右移動,-1為向左移動)袜蚕,通過外星人群的left和right屬性判斷是否有到達(dá)窗口邊緣糟把,如果有,那么外星人群alien.rect.y += 下降速度(根據(jù)坐標(biāo)圖可以看出下降y軸坐標(biāo)是加大的)牲剃,在alien類創(chuàng)建一個(gè)check_edges()方法和一個(gè)update方法
def check_edges(self):
if self.rect.right >= screen.rect.right:
return True
if self.rect.left <= 0:
return True
def updates(self):
self.x += self.settings.alien_speed_factor * self.settings.alien_fleet_direction
self.rect.x = self.x
在game_functions.py中創(chuàng)建check_fleet_edges()方法遣疯,根據(jù)check_edges()返回的結(jié)果,在進(jìn)行一次if判斷凿傅,如果為True缠犀,就for循環(huán)遍歷一次aliens.sprites列表聪舒,將每個(gè)外星人都下移辨液,并將alien_direciton *= -1,這樣外星人的左右方向就會改變
檢測子彈和外星人接觸
在game_functions.py里按創(chuàng)建一個(gè)def check_alien_bullet_collisions()方法,使用函數(shù)pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb, collided=None)燎悍,接受子彈編組和外星人編組宏怔,和兩個(gè)True,是檢測兩個(gè)編組中的精靈的rect是有否重疊臊诊,兩個(gè)True是刪除碰撞的兩個(gè)精靈思劳。當(dāng)所有外星人都被打完,就可以用len(aliens)判斷妨猩,如果為等于0,就使用bullets.empy()函數(shù)清空編組然后調(diào)用create_fleet()創(chuàng)造新的外星人群
def bullet_update(bullets,ai_settings,screen,ship,aliens,stats,sb):
#更新子彈位置
bullets.update()
#刪除子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb)
def check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb):
#檢查子彈是否有擊中外星人
#如果有擊中秽褒,就刪除對應(yīng)的子彈和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
接下來可以檢測外星人與飛船碰撞的情況壶硅,函數(shù)pygame.sprite.spritecolideany(sprite, group, collided=None),接受一個(gè)精靈和一個(gè)編組,檢測是否有重疊销斟,并且返回一個(gè)布爾值庐椒,根據(jù)這個(gè)布爾值來進(jìn)行if判斷,飛船數(shù)量-1蚂踊,子彈清空约谈,外星人清空,重新創(chuàng)建外星人群,同時(shí)將飛船重新放回屏幕下端中央棱诱,并將這些功能整理成ship_hit函數(shù)泼橘,飛船的數(shù)量,需要創(chuàng)建一個(gè)專門管理游戲數(shù)據(jù)的類迈勋,命名game_stats.py→ class Game_stats炬灭。
class Setting():
def __init__(self):
...
self.ship_allow = 3
class Game_stats():
def __init__(self,ai_settings):
#初始化游戲數(shù)據(jù)統(tǒng)計(jì)
self.ai_settings = ai_settings
self.ships_break = self.ai_settings.ship_allow
from time import Sleep
def aliens_update(ai_settings,screen,ship,stats,aliens,sb,bullets):
#檢測是否有外星人碰到邊緣,并更新外星人位置
check_fleet_edges(ai_settings,aliens)
check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets)
aliens.update()
if pygame.sprite.spritecollideany(ship,aliens):
#當(dāng)外星人和飛船碰撞反應(yīng)
ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets)
def ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets):
if stats.ships_break > 0:
#減少一只飛船的數(shù)量
stats.ships_break -= 1
#更新飛船記分牌
sb.prep_ships()
#清空飛船列表和子彈列表
aliens.empty()
bullets.empty()
#重新創(chuàng)造一組外星人练链,并把飛船放到屏幕下端中央
creat_fleet(ai_settings,screen,ship,aliens)
ship.ship_centerx = ship.screen_rect.centerx
#停止0.5秒凿滤,以便讓玩家知道飛船撞到鹏控,或者外星人碰到底端
sleep(0.5)
遍歷aliens編組,如果有外星人到達(dá)屏幕底端鼻吮,也需要調(diào)用ship_hit方法
def check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets):
#檢查外星人是否有碰到底端及其反應(yīng)
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
ship_hit(ai_settings,screen,ship,stats,aliens,bullets)
break
def aliens_update(ai_settings,screen,ship,stats,aliens,sb,bullets):
...
check_aliens_bottom(ai_settings,screen,ship,stats,aliens,bullets)
游戲開始的按鈕
首先在games_stats.py中設(shè)置游戲狀態(tài)為True,可以ship_hit中加一個(gè)if较鼓,else的判斷椎木,如果飛船數(shù)量大于0就執(zhí)行剛剛的代碼,否則就游戲狀態(tài)設(shè)置成為False笨腥,再回到alien_invasion.py中的while循環(huán)開頭加上if判斷拓哺,如果游戲狀態(tài)為True才執(zhí)行。
接下來就需要來繪制play按鈕了脖母,按鈕的繪制與子彈一樣士鸥,只不過要加上字,就需要用到pygame提供的將文字熏染成圖像的函數(shù)谆级,首先用函數(shù)pygame.font.SysFont(None,48)用一個(gè)變量接收,None位置傳入的是字體烤礁,None使用默認(rèn)字體,48為大小肥照。再用變量調(diào)用render(self, text, antialias, color, background=None)脚仔,text需要的文字,antlalia為抗鋸齒選項(xiàng)舆绎,接收True和False鲤脏,讓文字更平滑,后面兩個(gè)參數(shù)就是文字顏色和背景顏色吕朵。并且讓文字的放于按鈕的中間猎醇,最后創(chuàng)建draw_button方法,分別把背景和文字的圖像繪制出來即可努溃。
class Button():
def __init__(self,ai_settings,screen,msg):
#初始化按鈕參數(shù)
self.screen = screen
self.ai_settings = ai_settings
self.screen_rect = screen.get_rect()
#設(shè)置按鈕顏色及文本顏色硫嘶、文本字體
self.color = ai_settings.button_color
self.msg_color = ai_settings.text_color
self.font = pygame.font.SysFont(None,48)
#設(shè)置按鈕位置
self.rect = pygame.Rect(0,0,ai_settings.button_width,ai_settings.button_height)
self.rect.center = self.screen_rect.center
#設(shè)置文本位置
self.msg_image = self.font.render(msg,True,self.msg_color,self.color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
#繪制按鈕
self.screen.fill(self.color,self.rect)
self.screen.blit(self.msg_image,self.msg_image_rect)
檢測鼠標(biāo)點(diǎn)擊play按鈕
先把游戲狀態(tài)設(shè)置為False,用if語句判斷梧税,如果為False沦疾,就繪制按鈕称近。
在檢測按鍵鼠標(biāo)事件的if判斷加上的鼠標(biāo)判斷。pygame.mouse.get_pos()函數(shù)可以檢測鼠標(biāo)點(diǎn)擊哮塞,返回一個(gè)(x,y)的坐標(biāo)刨秆。用函數(shù)
collidepoint(self,x,y),檢測是否在rect矩形內(nèi)有碰撞彻桃,傳入?yún)?shù)x,y坐標(biāo)坛善,返回結(jié)果是布爾值。如果為Ture邻眷,就讓游戲狀態(tài)為True眠屎。
當(dāng)結(jié)束之后,play的按鈕會出現(xiàn)肆饶,這是會發(fā)現(xiàn)的情況是繼續(xù)游戲改衩,而不是重新開始,如果我們還需要加上一下重置游戲的設(shè)置驯镊,除了讓參數(shù)回到初始化葫督,還需要將子彈清空,外星人清空板惑,并創(chuàng)建新的外星人群橄镜,并讓飛船回到原來的位置。
def check_play_button(ai_settings,screen,ship,bullets,aliens,stats,sb,play_button,mouse_x,mouse_y):
button_clickde = play_button.rect.collidepoint(mouse_x,mouse_y)
#檢測鼠標(biāo)點(diǎn)擊行為是否在paly按鈕區(qū)域及反應(yīng)
if button_clickde and not stats.game_state:
ai_settings.game_initialization()
stats.game_state = True
stats.reset_stats()
aliens.empty()
bullets.empty()
#創(chuàng)造外星人群并將飛船放于屏幕下端中央
creat_fleet(ai_settings, screen, ship, aliens)
ship.ship_centerx = ship.screen_rect.centerx
游戲開始之后會發(fā)現(xiàn)冯乘,現(xiàn)在點(diǎn)擊按鈕的位置洽胶,還是會重置游戲,所以在判斷加上游戲狀態(tài)為False的時(shí)候才重置裆馒,而且讓鼠標(biāo)光標(biāo)在游戲開始之后隱藏起來姊氓,pygame.mouse.set_visible(False),并在游戲結(jié)束的時(shí)候?qū)⒃O(shè)置為True顯示出來。
游戲難度的升級
可以設(shè)置一個(gè)倍數(shù)變量喷好,在判斷外星人數(shù)量為0的if判斷里讓原來的參數(shù)乘與這個(gè)倍數(shù)翔横,同時(shí)也要在游戲開始判斷里重置參數(shù),達(dá)到重新開始的效果
游戲計(jì)分板
需要記錄玩家游戲得分梗搅,最高得分記錄禾唁,難度的等級,還有玩家的剩余飛船數(shù)量圖標(biāo)无切,需要設(shè)置初始得分為0蟀俊,每個(gè)外星人的分值,初始難度為1订雾。
- 還記得之前檢測子彈和外星人碰撞的函數(shù)groupcollide,返回的是一個(gè)字典存儲在collisions里矛洞,我們遍歷collisions字典中的value值洼哎,就可以得到外星人被子彈碰撞的數(shù)量烫映,將它乘以我們設(shè)置好的外星人的分值,并將分值用render函數(shù)渲染成圖片繪制出來噩峦,同時(shí)做一個(gè)if判斷锭沟,如果當(dāng)前得分高過歷史最高得分,就將它賦值給歷史最高得分變量识补,注意的是這個(gè)歷史最高得分變量不會因?yàn)橛螒蛑匦麻_始而重置族淮,同樣用render函數(shù)繪制出來。
- 難度等級可放于外星人群數(shù)量等于0的if判斷中凭涂,如果為True祝辣,等級變量+1,同樣用render函數(shù)繪制切油。
- 創(chuàng)建一個(gè)ships的編組蝙斜,Ship類就需要繼承Sprite,將玩家飛船數(shù)量遍歷出來按照外星人群的方法繪制澎胡,并加到ships編組中孕荠。在飛船與外星人碰撞的if判斷中繪制。
def check_bullets_aliens_collisions(ai_settings,screen,ship,bullets,aliens,stats,sb):
#檢查子彈是否有擊中外星人
#如果有擊中攻谁,就刪除對應(yīng)的子彈和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if collisions:
#擊中外星人玩家得分
for alines in collisions.values():
stats.score += ai_settings.alien_points * len(alines)
sb.prep_point()
#記錄玩家最高得分
if stats.score > stats.hight_score:
stats.hight_score = stats.score
sb.prep_hightscore()
if len(aliens) == 0:
#刪除現(xiàn)有的子彈并生成新的外星人
bullets.empty()
#升級難度級別
stats.level += 1
#更新難度級別記分牌稚伍,增加游戲難度,并創(chuàng)建新外星人群
sb.prep_level()
...
def ship_hit(ai_settings,screen,ship,stats,aliens,sb,bullets):
if stats.ships_break > 0:
#減少一只飛船的數(shù)量
stats.ships_break -= 1
#更新飛船記分牌
sb.prep_ships()
...
class Scoreboard():
def __init__(self,ai_settings,screen,stats):
...
#記分牌圖像
self.prep_point()
self.prep_hightscore()
self.prep_level()
self.prep_ships()
def prep_point(self):
# rounded_score = int(round(self.stats.score,-1))
# self.score = str(self.stats.score)
#玩家得分轉(zhuǎn)化為渲染圖像
score ='{:,}'.format(self.stats.score)
self.score_image = self.font.render(score,True,self.point_color,self.ai_settings.screen_color)
#將得分記分牌放于屏幕左上角
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
def prep_hightscore(self):
#將玩家最高得分轉(zhuǎn)化為渲染的圖像
hight_score = '{:,}'.format(self.stats.hight_score)
self.hight_score_image = self.font.render(hight_score,True,self.point_color,self.ai_settings.screen_color)
#將最高法記分牌放于屏幕上端中央
self.hight_score_rect = self.hight_score_image.get_rect()
self.hight_score_rect.centerx = self.screen_rect.centerx
self.hight_score_rect.top = 20
def prep_level(self):
#將等級轉(zhuǎn)換為渲染的圖像
self.level_str = str(self.stats.level)
self.level_image = self.font.render(self.level_str,True,self.point_color,self.ai_settings.screen_color)
#將等級記分牌放于得分牌下發(fā)
self.level_rect = self.level_image.get_rect()
self.level_rect.right = self.screen_rect.right - 20
self.level_rect.top = self.score_rect.bottom + 5
def prep_ships(self):
#顯示飛船數(shù)量
self.ships = Group()
for ships_number in range(self.stats.ships_break):
ship = Ship(self.ai_settings,self.screen)
ship.rect.x = 10 + ships_number * ship.rect.width
ship.rect.top = self.screen_rect.top + 10
self.ships.add(ship)
def draw_score(self):
#繪制記分牌圖像
self.screen.blit(self.score_image,self.score_rect)
self.screen.blit(self.hight_score_image, self.hight_score_rect)
self.screen.blit(self.level_image,self.level_rect)
self.ships.draw(self.screen)
易錯(cuò)點(diǎn)
小游戲項(xiàng)目完成了戚宦,現(xiàn)在就可以運(yùn)行體驗(yàn)了个曙。
整個(gè)編程下來,感覺容易出錯(cuò)導(dǎo)致報(bào)錯(cuò)的阁苞,往往都是一些非常細(xì)節(jié)的東西困檩,例如:
- 調(diào)用類方法或者類對象的調(diào)用錯(cuò)誤,或者傳入的參數(shù)沒有對不上那槽,出現(xiàn)漏掉或者位置錯(cuò)誤悼沿,當(dāng)然也可以指定參數(shù)賦值,但是過于繁瑣骚灸,建議寫方法之前糟趾,可以先不用傳參數(shù),寫完之后在將方法中需要的參數(shù)填上在復(fù)制粘貼統(tǒng)一即可甚牲。
- 代碼的整潔性义郑,代碼塊不易過長,會導(dǎo)致不好理解丈钙,而且修改的時(shí)候也不好修改非驮,要養(yǎng)成寫完一個(gè)功能代碼之后整理一下,將功能細(xì)分成更多小功能代碼塊雏赦,這個(gè)需要日后多次項(xiàng)目經(jīng)歷培養(yǎng)形成劫笙。
*注釋芙扎,對每個(gè)代碼塊進(jìn)行注釋,讓閱讀者更快的了解代碼的作用性填大,注釋的規(guī)范戒洼,可放于代碼的上方或者下方,用#號鍵注釋允华,最好注釋之后可以空行圈浇,會更美觀。