一肥哎、Python及Pygame簡(jiǎn)介
作為一款開(kāi)源的程序設(shè)計(jì)語(yǔ)言篡诽,Python有著眾多優(yōu)勢(shì)。憑借著越來(lái)越豐富朱浴、功能越來(lái)越強(qiáng)大的擴(kuò)展庫(kù)达椰,Python在各個(gè)領(lǐng)域越來(lái)越突出:數(shù)據(jù)分析(R語(yǔ)言)啰劲、矩陣運(yùn)算(Matlab)、深度學(xué)習(xí)……Python的強(qiáng)大正在于它有著如此多的外部擴(kuò)展庫(kù)廷支,你可以只關(guān)注你感興趣的功能,而不必在意它的底層是如何實(shí)現(xiàn)的垛孔。 目前流行的版本是2.7與3.5施敢,后者是作為新版本有某些改變,但Python2用戶(hù)仍居多概作。不過(guò)值得注意的是Python3對(duì)于Unicode的處理更加方便悯许,也因此逐漸被普及先壕。本文的所使用Python版本為3.5谆甜。
Pygame是Python的一個(gè)package,是一個(gè)很經(jīng)典的游戲制作包谆棺,可以完成幾乎所有的2D游戲制作罕袋。Pygame需要通過(guò)pip安裝(具體可百度)浴讯,其中各模塊的方法和屬性信息可在Pygame官網(wǎng)的幫助手冊(cè)查詢(xún)。本文使用的Pygame版本匹配Python3.5-win64仰猖。
**二奈籽、初次實(shí)現(xiàn)
現(xiàn)在我們嘗試著用Pygame來(lái)制作一個(gè)微信打飛機(jī)小游戲衣屏。首先我們要做的是制作一個(gè)簡(jiǎn)單的游戲模型,而不是在一開(kāi)始就讓程序過(guò)于復(fù)雜膨疏。因此,我們從最簡(jiǎn)單的開(kāi)始卖局,先通過(guò)以下幾步來(lái)實(shí)現(xiàn)一只飛機(jī)下降的動(dòng)畫(huà)双霍。
(1)初始化Pygame主框架
(2)初始化plane類(lèi)
(3)對(duì)按鍵進(jìn)行檢查并響應(yīng)
(4)更新畫(huà)面
(5)重復(fù)(3)(4)
首先洒闸,導(dǎo)入相關(guān)庫(kù)。
import sys,pygame
from pygame.locals import*
from random import randrange
然后初始化pygame主框架单鹿。我們將會(huì)使用到display模塊下這些函數(shù):
set_mode:設(shè)定顯示的類(lèi)型和尺寸深纲。默認(rèn)為窗口,有可選參數(shù)FULLSCREEN(全屏)儒喊。
get_surface:返回一個(gè)可畫(huà)圖的Surface對(duì)象币呵,相當(dāng)于一幀。一個(gè)Surface對(duì)象到另一個(gè)Surface對(duì)象的變化即產(chǎn)生動(dòng)畫(huà)的變化芯义。其blit()方法可更新Surface對(duì)象妻柒。
flip:更新顯示。
update:更新屏幕一部分鬼癣,可以提高游戲性能啤贩”砸伲可以配合后文的RenderUpdates類(lèi)draw方法中返回的矩形列表使用。
set_caption:設(shè)定Pygame程序標(biāo)題暖庄,窗口化時(shí)會(huì)用做窗口標(biāo)題。
Pygame中兩個(gè)最重要的類(lèi)是Sprite類(lèi)和Group類(lèi)(Sprite類(lèi)將在后文飛機(jī)初始化部分介紹)惹悄。Group類(lèi)(和它的子類(lèi))是Sprite類(lèi)的容器泣港,RenderUpdates類(lèi)是Group類(lèi)的子類(lèi)价匠。創(chuàng)建了一個(gè)RenderUpdates類(lèi)對(duì)象之后,可以使用add()方法向其中添加Sprite類(lèi)對(duì)象坡氯,也就產(chǎn)生了一只飛機(jī)洋腮。用該容器存儲(chǔ)Sprite對(duì)象的好處是容易管理徐矩,并且可以只對(duì)需要進(jìn)行更新的顯示部分進(jìn)行更新叁幢,提高游戲性能曼玩。
初始化
pygame.init()
screen_size=400,600 #screen屏幕大小
pygame.display.set_mode(screen_size) #窗口
全屏為.set_mode(screen_size,FULLSCREEN)
pygame.mouse.set_visible(0) #隱藏鼠標(biāo)
plane_image=pygame.image.load('plane2.png')
導(dǎo)入飛機(jī)圖片(圖片大小應(yīng)做調(diào)整)
sprites=pygame.sprite.RenderUpdates()
創(chuàng)建Sprite的容器
sprites.add(plane())
向容器中添加一個(gè)plane對(duì)象
screen=pygame.display.get_surface()
可用于畫(huà)圖的Surface對(duì)象
bg=(255,255,255) #背景顏色RGB格式red-blue-green
screen.fill(bg) #屏幕填充背景顏色
pygame.display.flip() #顯示更新后的屏幕
構(gòu)造函數(shù)init(注意是兩個(gè)下劃線+)中image屬性可為其賦值.png/.jpg圖片以改變其外觀黍判,在這里我們先用plane_image代替,在前面的主函數(shù)編寫(xiě)中已為其指定具體的圖片(本文圖片均為自制美旧,隨代碼給出贬墩。大家也可以直接在網(wǎng)上找圖片素材或者PS自制,甚至可以直接用Python圖像處理處理)嗽测。rect屬性記錄位置信息肿孵,rect.top/bottom/left/right分別記錄坐標(biāo)信息,rect.height/width分別記錄長(zhǎng)寬晤愧。Pygame中的Sprite類(lèi)是所有可視游戲?qū)ο蟮幕?lèi),我們需要從它繼承創(chuàng)建子類(lèi)plane葵硕,用來(lái)表示下降的飛機(jī)贯吓,并覆蓋它的構(gòu)造函數(shù)和image悄谐,rect屬性(相當(dāng)于其數(shù)據(jù)成員)。
reset()函數(shù)隨機(jī)確定飛機(jī)產(chǎn)生的橫坐標(biāo)爬舰,并固定縱坐標(biāo)在畫(huà)面最上方们陆。
update()函數(shù)改變飛機(jī)的位置。當(dāng)飛機(jī)抵達(dá)畫(huà)面下方時(shí)情屹,調(diào)用reset()函數(shù)隨機(jī)重置其于頂端坪仇。
class plane(pygame.sprite.Sprite):
definit(self):
pygame.sprite.Sprite.init(self)
self.image=plane_image
self.rect=self.image.get_rect()
self.rect.top=-self.rect.height
self.rect.centerx=
randrange(screen_size[0]-self.rect.width)
+self.rect.width/2
控制飛機(jī)在窗口內(nèi)
def update(self):
self.rect.top+=1
if self.rect.top>screen_size[1]:
self.kill()
飛機(jī)飛出屏幕則將其移除
接下來(lái),只要在主程序中添加響應(yīng)部分垃你、更新部分和循環(huán)部分即可椅文。
其中pygame.event.get()獲取用戶(hù)操作事件列表,比如鍵盤(pán)輸入皆刺、鼠標(biāo)點(diǎn)擊等×枭悖可以通過(guò)判斷按鍵來(lái)做出響應(yīng)∠鄱辏現(xiàn)在我們只是簡(jiǎn)單地響應(yīng)“退出”事件,在之后程序完善時(shí)完全可以使其響應(yīng)“暫拖强鳎”痴怨、“重新開(kāi)始”等。
def clear_callback(surf,rect):
清除舊Sprite圖形
surf.fill(bg,rect)
while True:
for eventinpygame.event.get():
if event.type==QUIT: #關(guān)閉窗口
sys.exit()
if event.type==KEYDOWNand
event.key==K_ESCAPE:
Esc退出
sys.exit()
sprites.clear(screen,clear_callback)
sprites.update() def clear_callback(surf,rect):
清除舊Sprite圖形
surf.fill(bg,rect)
while True:
for eventinpygame.event.get():
if event.type==QUIT: #關(guān)閉窗口
sys.exit()
if event.type==KEYDOWNand
event.key==K_ESCAPE:
Esc退出
sys.exit()
sprites.clear(screen,clear_callback)
sprites.update()
調(diào)用update()方法更新Sprites對(duì)象
updates=sprites.draw(screen)
調(diào)用父類(lèi)RenderUpdates的draw方法
返回需要更新的部分
pygame.display.update(updates)
更新顯示
至此器予,我們將上述代碼保存在一個(gè).py文件中便可以運(yùn)行了浪藻。結(jié)果如下:
但是,實(shí)現(xiàn)后會(huì)發(fā)現(xiàn)飛機(jī)的速度實(shí)在是太快了劣摇,我們可以考慮在其構(gòu)造時(shí)為其加上幀率的限制珠移。
首先,在第一步初始化主框架的時(shí)候定義pygame中的Clock對(duì)象。
定義一個(gè)時(shí)間對(duì)象
clock=pygame.time.Clock()
最大幀率
speed=100
然后在最后主程序循環(huán)中钧惧,更新之前加入語(yǔ)句:
clock.tick(speed)
三暇韧、進(jìn)一步完善
現(xiàn)在我們已經(jīng)有了一個(gè)基本的框架,但是這僅僅是一只慢慢飛過(guò)的飛機(jī)浓瞪。不過(guò)不要灰心懈玻,我們只要再完成3小步,就可以制作出基本的打飛機(jī)小游戲啦乾颁。
(1)初始化我方戰(zhàn)機(jī)
(2)實(shí)現(xiàn)敵方戰(zhàn)機(jī)的產(chǎn)生
(3)實(shí)現(xiàn)敵我戰(zhàn)機(jī)的相互作用
首先涂乌,仿造之前敵方戰(zhàn)機(jī)的構(gòu)造方式,編寫(xiě)如下類(lèi):
class myplane(pygame.sprite.Sprite):
definit(self):
pygame.sprite.Sprite.init(self)
self.image=myplane_image
self.rect=self.image.get_rect()
self.rect.bottom=screen_size[1]
self.rect.centerx=screen_size[0]/2
def update(self,pressed_keys):
if pressed_keys[K_UP]:
self.rect.move_ip(0,-3)
if pressed_keys[K_DOWN]:
self.rect.move_ip(0,3)
if pressed_keys[K_LEFT]:
self.rect.move_ip(-3,0)
if pressed_keys[K_RIGHT]:
self.rect.move_ip(3,0)
限定player在屏幕中
if self.rect.bottom>screen_size[1]:
self.rect.bottom=screen_size[1]
elif self.rect.top<=0:
self.rect.top=0
if self.rect.left<=0:
self.rect.left=0
elif self.rect.right>screen_size[0]:
self.rect.right=screen_size[0]
其中與plane類(lèi)不同的地方有兩處英岭,一是其更新方式不同湾盒,它需要傳入一個(gè)pressed_keys參數(shù),代表鍵盤(pán)的按鍵诅妹,分別對(duì)上下左右四個(gè)鍵做出響應(yīng)罚勾。二是要限制myplane對(duì)象運(yùn)動(dòng)不超出屏幕。
接著吭狡,我們不妨把子彈bullets類(lèi)也編寫(xiě)了再把他們一起實(shí)例化尖殃。因?yàn)樽訌椀奈恢眯枰蒻yplane確定,因此我們可以在myplane類(lèi)中定義特定的fire()函數(shù)划煮,實(shí)現(xiàn)子彈的生成送丰。
def fire(self):
axis=[self.rect.centerx,self.rect.top]
new_bullets=bullets(axis)
mybullets.add(new_bullets)
all_sprites.add(new_bullets)
以下是bullets類(lèi),其構(gòu)造函數(shù)需坐標(biāo)參數(shù)axis弛秋。
class bullets(pygame.sprite.Sprite):
definit(self,axis):
pygame.sprite.Sprite.init(self)
self.image=bullets_image
self.rect=self.image.get_rect()
self.rect.centerx=axis[0]
self.rect.centery=axis[1]
def update(self):
self.rect.top-=3
if self.rect.bottom<=0:
self.kill()
現(xiàn)在這三種類(lèi)的實(shí)例化應(yīng)改為:
sprites=pygame.sprite.RenderUpdates()
sprites.add(plane())
mysprites=pygame.sprite.RenderUpdates()
myplane=myplane()
mysprites.add(myplane)
mybullets=pygame.sprite.RenderUpdates()
all_sprites=pygame.sprite.RenderUpdates()
all_sprites.add(mysprites)
all_sprites.add(sprites)
all_sprites.add(mybullets)
all_sprites表示所有的類(lèi)實(shí)例(或?qū)ο螅┢黪铮@是為了更新的簡(jiǎn)便起見(jiàn):
注意:其中plane()為plane類(lèi)的對(duì)象,但我們無(wú)法直接對(duì)其進(jìn)行操作铐懊;而myplane=myplane()這里簡(jiǎn)便起見(jiàn)對(duì)象與類(lèi)取相同的名字邀桑,之后才能在主程序中直接調(diào)用其方法(函數(shù))。
while True:
while True:
for eventinpygame.event.get():
if event.type==QUIT:
sys.exit()
elif event.type==KEYDOWNandevent.key==K_ESCAPE:
sys.exit()
myplane.fire()
pressed_keys=pygame.key.get_pressed()
clock.tick(speed)
all_sprites.clear(screen,clear_callback)
sprites.update()
mybullets.update()
mysprites.update(pressed_keys)
updates=all_sprites.draw(screen)
pygame.display.update(updates)
現(xiàn)在科乎,我們已經(jīng)有了可以運(yùn)動(dòng)的我方戰(zhàn)機(jī),雖然子彈速度太快以致于我們的子彈像是激光搶?zhuān)覀兘酉聛?lái)會(huì)解決這個(gè)問(wèn)題贼急,并且嘗試著產(chǎn)生更多的敵方戰(zhàn)機(jī)茅茂。
這兩個(gè)問(wèn)題都涉及到對(duì)象產(chǎn)生的速率,我們不妨先看看怎么產(chǎn)生更多敵方戰(zhàn)機(jī)太抓。首先應(yīng)該定義產(chǎn)生敵機(jī)事件:
ADDENEMY=pygame.USEREVENT+1 #為添加敵人創(chuàng)建自定義事件
pygame.time.set_timer(ADDENEMY,1200) #產(chǎn)生事件的時(shí)間(毫秒)
通過(guò)這種方式空闲,我們使產(chǎn)生敵機(jī)的速率降低,同樣的我們也可以這樣來(lái)限制子彈產(chǎn)生的速度(具體見(jiàn)源代碼)走敌。
然后再在循環(huán)過(guò)程中判斷事件event是否為ADDENEMY來(lái)決定是否產(chǎn)生敵機(jī)碴倾。
現(xiàn)在我們的游戲變成了這樣:
終于到了最后一步!我們只要為各種類(lèi)之間加上相應(yīng)的關(guān)系就完成啦。
對(duì)于敵機(jī)跌榔,只要觸碰到子彈便被kill异雁,對(duì)于我機(jī),只要觸碰到敵機(jī)便kill而且gameover了僧须。在主循環(huán)中可加入如下判斷語(yǔ)句:
for j in mybullets.sprites():
for i in antisprites.sprites():
if j.rect.colliderect(i.rect):
i.kill()
j.kill()
break
接下來(lái)判斷我機(jī)與敵機(jī)的位置:
其中mybullets.sprites(),antisprites.sprites()分別返回現(xiàn)有的子彈纲刀,敵機(jī)對(duì)象的列表。它們是按產(chǎn)生時(shí)間先后順序排列的担平。rect的colliderect()方法可以判斷兩個(gè)矩形是否相交并返回布爾值示绊。通過(guò)遍歷子彈和敵機(jī)對(duì)象,我們可以判斷子彈和敵機(jī)的位置關(guān)系暂论,但是考慮到一顆子彈應(yīng)該只能消滅一只敵機(jī)面褐,因此每當(dāng)一顆子彈消滅一只敵機(jī)時(shí),我們應(yīng)跳出內(nèi)層循環(huán)取胎,繼續(xù)遍歷剩下的子彈對(duì)象盆耽。
if pygame.sprite.spritecollideany(myplane,antisprites):
myplane.kill()
游戲結(jié)束提示
screen.fill((255,255,255)) #填充顏色
font=pygame.font.Font(None,48) #設(shè)置字體
text="YOU LOST!"
height=font.get_linesize() #獲取文字高度
設(shè)置字體顯示參數(shù)
antialias=1#光滑字體
black=0,0,0#字體顏色
text2=font.render(text,antialias,black)
r=text2.get_rect() #獲取文字圖形的矩形
center,top=screen.get_rect().center #設(shè)置文字位置
r.midtop=center,top
screen.blit(text2,r) #更新Surfacee對(duì)象
pygame.display.flip() #更新顯示
break
其中pygame.sprite.spritecollideany()方法可以用來(lái)判斷兩個(gè)Group里sprites對(duì)象間的交叉部分。
四扼菠、最后說(shuō)幾句
至此摄杂,我們已經(jīng)制作出了簡(jiǎn)單的打飛機(jī)小游戲。其實(shí)還有很多可以豐富改善的地方循榆,比如:
最后判斷不同對(duì)象是否接觸的時(shí)候僅僅用對(duì)象的矩形判斷可能不夠準(zhǔn)確析恢,為此我們可以考慮將對(duì)象的真實(shí)部分切分為幾個(gè)小矩形再進(jìn)行判斷;
我們的程序僅僅包含在一個(gè).py文件中秧饮,我們可以考慮對(duì)其進(jìn)行封裝映挂,把游戲?qū)ο蟆⒂螒蛐畔ⅲǖ梅值潦⒌燃?jí))柑船、游戲狀態(tài)(暫停、結(jié)束)等分別抽象成一個(gè)或幾個(gè)類(lèi)泼各,使程序更容易拓展鞍时;
我們可以使用py2exe包將.py文件制作為.exe可執(zhí)行文件。
反觀全文扣蜻,我們的整體思路應(yīng)該是:
類(lèi)的功能——類(lèi)的實(shí)現(xiàn)——類(lèi)間的關(guān)系逆巍。
我們從需要實(shí)現(xiàn)的功能出發(fā),定義相應(yīng)的類(lèi)及其方法莽使、屬性锐极;再到復(fù)雜的情形需要不同類(lèi)的相互聯(lián)系作用,因此再回頭豐富某些類(lèi)的方法(或者派生繼承)芳肌,使它們能產(chǎn)生聯(lián)系灵再;完成上述兩步之后肋层,又重新回到第一步,考慮類(lèi)仍可完善的功能翎迁,循環(huán)反復(fù)……
為了給大家一個(gè)好的學(xué)習(xí)機(jī)會(huì)栋猖!小編給大家準(zhǔn)備了十套Python零基礎(chǔ)的學(xué)習(xí)資料
加小編Python學(xué)習(xí)群:862703141?即可自動(dòng)獲取大量python視頻教程以及各類(lèi)PDF!