Python游戲開發(fā)之精靈和精靈組
1. 基本概念
接下來介紹兩個pygame中提供的高級類, 精靈和精靈組.
在介紹這兩個類之前, 先來共同回顧一下到目前為止掌握的游戲開發(fā)套路.
在游戲初始化,需要加載一下游戲中所有的圖像, 然后呢,在游戲循環(huán)中,需要針對每張圖像來編寫代碼、修改圖像的位置,并且需要用screen對象來調用一下blit 方法,把所有變化位置的圖像重新做一個繪制.
那現(xiàn)在假設開發(fā)的游戲,需要處理100張圖像,意味著游戲循環(huán)內部的代碼就會變得非常的繁瑣.
因為需要在游戲循環(huán)中針對每張圖像的位置變化,編寫代碼,然后再讓screen對象調用blit 方法依次繪制每個圖像,這樣的代碼呢,就顯得太繁瑣了.
那怎么樣有效的解決這個問題呢?pygame就提供了兩個高級類,一個是精靈,一個是精靈組.

精靈這個詞匯聽起來有些奇怪,可以把精靈理解成一個對象,在一個精靈對象中有兩個重要的屬性,一個是精靈要顯示的圖像數(shù)據(jù),一個是精靈要把這個圖像顯示在屏幕上的位置.
一句話講,包含了圖像數(shù)據(jù)和顯示位置的對象,就可以把它叫做精靈,同時呢,在精靈類中啊,還提供了一個非常重要的方法update,

在開發(fā)時, 可以根據(jù)不同的游戲角色派生出不同的游戲子類, 然后在每個子類中根據(jù)各自的需求分別重寫各自的update 方法, 在update 方法中, 專門來處理一下當前這個游戲角色位置變化的代碼, 這個就是update方法的作用.
之所以要派生子類, 是因為不同的游戲角色在屏幕上的運動方式是不一樣的,.
一個精靈類, 提供了一個image屬性, 提供了一個rect 屬性,并且提供了一個重要的update方法.

這個就是精靈類最重要的兩個屬性和一個方法.
那接下來看一下精靈組,從字面上來看,精靈組是一個包含了多個精靈的對象,在創(chuàng)建精靈組的時候可以使用多值參數(shù)的方式,一次性把精靈組中包含的所有精靈傳入到精靈組內部.
當有了精靈組之后,當創(chuàng)建了一個精靈組之后,在編寫游戲循環(huán)代碼時,只需要在游戲循環(huán)中,讓精靈組調用兩個方法,第一個方法update,第二個方法draw.

這兩個方法有什么作用呢?當讓精靈組 調用update方法時,精靈組,就會讓組中所有的精靈,各自調用各自的update方法,精靈組調用一次update 方法,精靈組中所有的精靈就會各自調用各自的update方法.
各自都用各自的update方法,就可以根據(jù)游戲的需求,各自修改各自不同的位置,當所有的精靈更新完位置之后,再讓精靈組調一下draw 方法,同時呢,把屏幕對象當作參數(shù)傳遞給 draw 方法, draw 方法會把精靈組中所有精靈的image繪制到屏幕上每一個精靈對應的rect 位置。

在精靈中包含有兩個重要的屬性,一個是精靈要顯示的圖像數(shù)據(jù),一個是精靈在屏幕上的位置,而在游戲循環(huán)中,讓精靈組調用一下draw 方法,就可以一次性的把精靈組所有的精靈圖像,各自繪制到不同的位置上.
這個就是使用精靈組之后,游戲循環(huán)的代碼能夠得到大大的改善.

但是務必要注意哦,當讓精靈組調用了draw 方法之后,如果希望在屏幕上看到最終的繪制結果,同樣是需要調用一下display模塊兒提供的update的方法,因為啊,只有調用了update的方法,才能夠在屏幕上看到最終的繪制結果。
簡單介紹了一下pygame中提供的兩個高級類,一個是精靈,一個是精靈組.
在精靈類中封裝了兩個重要的屬性,一個是精靈顯示的圖像數(shù)據(jù)image ,一個是精靈在屏幕上對應的顯示位置rect ,同時,每個精靈都各自有自己不同的update 方法,在開發(fā)時可以派生一個精靈的子類,根據(jù)游戲角色不同,重寫update的方法,在update 方法內部,針對當前這個精靈角色編寫代碼、修改精靈的位置就可以,當精靈建立完成,就可以建立一個精靈組,一個精靈組是可以包含多個精靈的,當有了精靈組之后再編寫游戲循環(huán)的代碼,只需要讓精靈組調用update的方法,update 方法就可以讓所有的精靈同時更新位置,然后呢,再調一下draw 方法,draw 方法就會把更新位置之后的精靈繪制到屏幕對應的位置,這個就是精靈和精靈組的作用.
2. 自定義精靈子類需求分析
接下來就做一下派生精靈子類的演練,先共同來明確一下派生精靈子類這個演練的需求與步驟,第一步啊,要在項目中建立一個plane_sprites.py 的python 文件.

從這一小節(jié)開始,就要正式的進入到面向對象程序開發(fā)了,而在使用面向對象程序開發(fā)時,給每個模塊起名都需要有一個意識,就是起一個稍微正式一些的名字,而在這一小節(jié)派生出來的精靈子類會用在后續(xù)的飛機大戰(zhàn)實戰(zhàn)中, 所以啊,給他起一個稍微正式一些的名字,飛機精靈,然后呢,在這個模塊中建立一個Game_Sprites類,讓這個游戲精靈類繼承自pygame提供的精靈類pygame.sprites.Sprites .
在這里提示一下,在使用pygame時,第一個點后面通常是模塊的名稱,而第二個點后面才是類的名稱,回顧一下,在給類起名時,首字母應該大寫,所以啊,pygame提供的精靈類準確的名稱是點兒sprite點兒Sprite.
第一個sprite是模塊的名稱,第二個Sprite才是類的名稱.
現(xiàn)在要重點強調一個話題,在使用面向對象開發(fā)時,如果某一個類的父類不是object這個基類,也就是開發(fā)的這個類,不是繼承自object基類,那么,在這個類的初始化方法中, 一定要先調用一下父類的初始化方法,現(xiàn)在要開發(fā)的游戲精靈類是繼承自pygame的精靈類,那么試想一下,在這個父類的初始化方法中,是不是很有可能已經提前封裝了一部分代碼,那么,如果在子類的初始化方法中不調用父類的初始化方法,就不能夠享受到父類中原本封裝的代碼.
所以啊,為了保證父類原本封裝的初始化代碼能夠正常的被執(zhí)行,在開發(fā)時一定要記住,如果開發(fā)的子類不是繼承自object這個基類,那么,在初始化方法中一定都要主動調用一下父類的初始化方法.

來看一下游戲精靈類的需求,在游戲精靈類中啊,封裝三個屬性,分別是image, rect 和speed ,speed這個單詞翻譯過來是速度的意思,現(xiàn)在要做的是飛機大戰(zhàn)的游戲.
飛機大戰(zhàn)意味著屏幕上的每一個精靈都各自擁有不同的飛行速度,所以在這一小節(jié)的演練中,就在游戲精靈這個類中封裝一個速度的屬性.
要給對象定義屬性應該在初始化方法中定義,那現(xiàn)在再強調一下,這一小節(jié)要編寫的精靈子類是繼承自pygame的精靈類的,所以啊,必須要在初始化方法中主動調用一下父類的初始化方法,只有這樣才能夠保證父類已經封裝好的初始化代碼能夠被正常的執(zhí)行,那現(xiàn)在來看一下初始化方法的參數(shù).
第一個參數(shù)是image_name,第二個參數(shù)是speed ,有了image_name這個參數(shù),就可以在初始化方法中通過image.load來加載出圖像,當圖像加載出來之后,怎么樣指定圖像的rect 屬性呢?在這里分享的小技巧,在pygame的image對象,提供了一個get_rect()方法,

讓圖像調用一下這個方法就會返回一個矩形對象, 矩形對象的x, y都是0,但是矩形對象的寬和高就是剛剛加載出來的圖像的寬和高.
通過get_rect() 這個方法,就能夠在初始化方法內部非常方便的指令一下圖像精靈的初始位置,然后呢,再使用speed 這個參數(shù)來指定一下游戲精靈的初始速度,如果在調用時不指定速度,就使用默認的1 來設置一下游戲精靈的速度.

這個就是游戲精靈中,要封裝的三個屬性,圖像,位置以及速度,那接下來再來看一下update這個方法,在這一小節(jié)演練中,Update這個方法,讓圖像精靈的y值跟速度進行相加,要修改y值會讓游戲精靈在屏幕的垂直方向進行運動,這個就是接下來要派生的精靈子類的需求.

一句話講封裝三個屬性,圖像, 位置以及速度, 重寫一個update的方法,在update方法中,讓游戲精靈在屏幕上做垂直方向的運動.
3. 派生精靈子類代碼實現(xiàn)
接下來就參照這張類圖共同實現(xiàn)一下游戲精靈的子類開發(fā),已經提前準備好了plane_sprites 這個模塊文件.
在這一小節(jié)要開發(fā)的游戲精靈是繼承自pygame的精靈子類,所以啊,應該先在模塊中使用import關鍵字導入一下pygame這個模塊兒, 導入之后,就使用class關鍵字來給類起個名字,GameSprite,然后在小括號中指令一下游戲精靈的父類,寫下pygame.sprite,

第一個sprite是模塊的名稱,需要再敲一個點兒,然后輸入大寫的Sprite , 大寫的Sprite才是類的名稱.

那現(xiàn)在就在類名下方增加一個文檔注釋, 寫一下飛機大戰(zhàn)游戲精靈, 文檔注釋增加完成, 再來看一下類圖,

在游戲精靈類GameSprite中, 需要封裝三個屬性, 既然要定義對象的屬性, 就應該先實現(xiàn)一下初始化方法.

在初始化方法的參數(shù)中, 需要傳入圖像的名稱image_name, 以及精靈的初始速度speed.
先使用def 關鍵字找到__init__ 這個初始化方法, 然后呢, 然后增加兩個形參. 初始化方法準備完成, 先敲一個pass 占位, 現(xiàn)在一個簡單的初始化方法就準備完成了.
pycharm 以深黃色的背景提示初始化方法沒有調用父類的初始化方法,之前提到過, 當在開發(fā)時某一個子類的父類不是object基類,一定要在初始化方法中主動調用一下父類的初始化方法.

那現(xiàn)在就增加兩個注釋, 第一步呢,應該調用父類的初始化方法,調用完成之后,再來定義對象的屬性,兩個步驟寫完, 要想調用父類的方法,應該有一個特殊的對象,可以讓super這個對象來調用一下父類的初始化方法.

當父類的初始化方法調用完成, 就可以在下方定義一下游戲精靈的三個屬性, 分別是圖形, 位置和速度這三個屬性.
先使用self點 來定義第一個屬性image, 要想從一個圖像文件中加載數(shù)據(jù),可以使用pygame點image,然后調用load 方法,把傳入的image_name 當做參數(shù),傳遞給這個方法,這樣就可以把指定名稱的圖像加載到圖像屬性中了.

圖像屬性定義完成,緊接著定義一個rect 屬性,這個rect 屬性, 默認大小,如果要是圖像的大小, 可以讓image 調用一下get_rect 方法,get_rect()方法返回的大小就是剛剛加載出來的圖像大小,同時x和y都是零.

現(xiàn)在第2個屬性定義完成,再來定義第3個屬性速度,
self.speed 就直接把形參的速度傳遞過來,現(xiàn)在三個屬性定義完成.

就把pass這個占位符刪除一下,

三個屬性定義完成,再來看一下類圖, 在這一小節(jié)中,還需要重寫一下父類的update方法,在update方法中,讓游戲的精靈在垂直方向上運動,那現(xiàn)在就使用def 關鍵字, 然后輸入一個update小括號.

update方法找到之后, 游戲精靈需要在垂直方向上移動,那現(xiàn)在增加一個注釋,在屏幕的垂直方向上移動,要在屏幕的垂直方向上移動,就可以修改一下self.rect屬性的y 屬性,讓y屬性來加上當前的速度屬性,這樣相加之后意味著在創(chuàng)建對象時指定的速度不同,那么游戲精靈在屏幕上移動的速度也會不同.

代碼演進到這里,就實現(xiàn)了一下游戲精靈這個子類的代碼,讓游戲精靈繼承自pygame 的精靈類,同時在初始化方法中,先調用了一下父類的初始化方法,然后呢,按照類圖的需求依次定義了三個屬性,圖像, 位置以及速度,并且重寫了一下父類的update方法,在update的方法中,對rect 的y值做了一個修改,讓y值加上速度,這樣呢就能夠實現(xiàn)讓游戲精靈按照垂直方向進行移動了.
再強調一下,因為在開發(fā)子類的時候,如果子類的父類不是object這個基類, 一定要記住,在初始化方法中需要主動的調用父類的初始化方法,因為不主動調用父類的初始化方法就沒有辦法享受到父類中已經封裝好的初始化代碼了.
4. 創(chuàng)建敵機并且實現(xiàn)敵機動畫
接下來就使用剛剛派生的游戲精靈這個類來創(chuàng)建一個低級精靈對象,并且實現(xiàn)一下敵機的動畫效果。在開始動手之前,先讓來明確一下這一小節(jié)的演練步驟.

首先使用from這個關鍵字,把plane_sprites這個模塊導入一下,然后呢,在游戲初始化位置來創(chuàng)建一個敵機的精靈對象,并且創(chuàng)建一個敵機的精靈組對象.
當精靈對象和精靈組對象創(chuàng)建完成,就在游戲循環(huán)中,讓精靈組對象分別調用一下update方法和draw 方法.

在開始演練之前,再強調一下精靈和精靈組這兩個對象的職責,精靈對象是負責封裝精靈要顯示的圖像以及精靈在屏幕上的位置,并且封裝一下精靈的運動速度, 同時精靈這個對象還要提供一個update方法,在update方法中根據(jù)游戲的需求來更改自己的位置變化, 這個是精靈對象的職責.
那么再看一下精靈組對象的職責,精靈組對象可以包含多個精靈對象,當精靈組對象建立完成,就可以在游戲循環(huán)中讓精靈組對象來調用update 方法和draw 方法,update 方法會讓精靈組中所有的精靈各自調用各自的update方法.
調用之后,精靈組中所有的精靈位置都會發(fā)生變化, 當所有精靈的位置調整完畢,就讓精靈組再調用一下draw 方法,調用draw 方法之后就會把精靈組中所有的精靈全部繪制在screen這個屏幕對象上, 這個就是精靈和精靈組的職責.

明確了演練步驟,并且強調了一下精靈和精靈組的職責之后,現(xiàn)在就做一下代碼演練,因為現(xiàn)在看到的代碼是之前一個小節(jié)完成的監(jiān)聽退出事件的代碼,先運行一下程序驗證一下現(xiàn)在的代碼執(zhí)行效果.

游戲啟動了,

現(xiàn)在點擊叉叉,可以退出游戲.

這個是之前一個小節(jié)完成的代碼.
那在這一小結中,就在之前一個小節(jié)完成的代碼基礎上, 來實現(xiàn)一下敵機精靈的創(chuàng)建,并且實現(xiàn)一下敵機的動畫效果.
首先把光標放在第2行使用from這個關鍵字,從plane_sprites這個模塊來導入一下所有的內容,

導入之后, 在開發(fā)時使用import 導入模塊和from 導入模塊有什么區(qū)別呀?使用import 導入模塊,在使用這個模塊時,必須要使用模塊點的方式來使用.
而使用from來導入模塊,在使用時可以直接使用模塊提供的工具,而不再需要輸入模塊的名稱.
現(xiàn)在飛機精靈的模塊導入之后,就把代碼向下方滾動,來找到游戲循環(huán),為了看清楚這一小節(jié)完成的代碼,在游戲循環(huán)上方多增加幾個空行.

現(xiàn)在先增加一個單行注釋來明確一下這一小節(jié)要演練的重點. 在游戲初始化位置應該創(chuàng)建敵機的精靈,創(chuàng)建完精靈之后, 還需要創(chuàng)建敵機的精靈組,精靈要在初始化位置創(chuàng)建, 精靈組同樣也要在初始化位置創(chuàng)建.
那現(xiàn)在就把光標放在31行,先給敵機的精靈起個名字叫做enemy ,然后使用上一小節(jié)派生的游戲精靈類來創(chuàng)建一個精靈對象.
在創(chuàng)建精靈對象時, 第1個參數(shù)需要指定圖像的名稱,那現(xiàn)在先寫一個點表示當前目錄,然后寫一個image,那要加載哪一張圖像呢?就把images這個目錄展開,敵機的圖像是enemy1,那么就在image 后面寫上enemy1.png ,圖像指定完成,一個敵機的精靈也創(chuàng)建完成.

那現(xiàn)在就來創(chuàng)建一下敵機的精靈組,同樣先給精靈組起個名字enemy_group,然后呢,使用pygame提供的sprite模塊中提供的Group類來創(chuàng)建一個精靈組,
在創(chuàng)建精靈組的時候,可以把多個精靈以多值參數(shù)的方式傳遞給精靈組, 那么就把剛剛創(chuàng)建的敵機精靈傳遞給這個精靈組,現(xiàn)在敵機的精靈組也創(chuàng)建完成了.

那現(xiàn)在有了精靈,也有了精靈組, 但現(xiàn)在運行程序不能夠看到敵機的畫面.
因為要看到敵機的畫面,需要在游戲循環(huán)中讓精靈組調用draw 方法.
來先運行驗證一下,游戲啟動了,但是只有英雄的飛機孤孤單單,并沒有敵機的身影,那現(xiàn)在先停止一下程序.

現(xiàn)在已經在游戲初始化位置創(chuàng)建出來了敵機的精靈, 創(chuàng)建出來了敵機的精靈組,那接下來應該把代碼移動到游戲循環(huán)內部,在游戲循環(huán)中讓敵機的精靈組來調用兩個方法,那現(xiàn)在就找到update這個方法,然后多增加幾個空行,在這里先增加一個注釋,讓精靈組調用兩個方法.
讓精靈組需要調用哪兩個方法,第1個方法是update,第2個方法是draw.

update 方法可以讓精靈組中所有的精靈對象都執(zhí)行一下update 方法,而draw 方法呢,會把精靈組中所有精靈的圖像繪制在屏幕上,那現(xiàn)在就使用enemy_group來調用一下update方法,enemy_group.update(), 調用完成,再讓enemy_group來調用一下draw 方法, enemy_group.draw(screen).

在調用draw 方法的時候,需要把屏幕對象傳遞給方法,因為精靈組需要知道把精靈組內部的精靈繪制到誰的身上,現(xiàn)在就選中draw 方法,并且把之前創(chuàng)建好的screen 對象傳遞給這個方法, 又增加了兩行代碼,運行一下程序,看看這一次能不能看到敵機的身影.
程序啟動了, 一個敵人的小飛機,從上向下再運動了,現(xiàn)在英雄已經不再孤單了.

代碼寫到這里, 創(chuàng)建了一個精靈,創(chuàng)建了一個精靈組,就已經實現(xiàn)了敵機的精靈動畫.
那么先來看一下游戲循環(huán)內部的代碼,在游戲循環(huán)中,只是讓精靈組調用了一下update方法.

對比一下之前的英雄飛機,之前的英雄飛機,需要在游戲循環(huán)中修改飛機的位置,并且判斷飛機的位置,

而有了精靈組之后,直接讓精靈組調用一個update 方法,調用之后精靈組就能夠讓所有的精靈更新位置.
寫一下注釋,讓組中的所有精靈更新位置,這個是update方法的好處.

那么再來看一下draw 方法,之前在繪制圖像時都是讓screen 對象調用blit 方法,然后要指定一下,把這個圖像繪制到哪里,

而使用精靈組只需要調用一下draw 方法,并且把屏幕對象傳遞給draw 方法, 精靈組就會把內部的所有精靈全部繪制在屏幕上.
這個就是draw 方法的好處,再寫下注釋,在screen上繪制所有的精靈,這就是使用精靈和精靈組在開發(fā)游戲時能夠大大簡化游戲循環(huán)內部的代碼.

那現(xiàn)在再看一下之前創(chuàng)建精靈和精靈組的代碼,之前創(chuàng)建了一個敵機的精靈,把敵機的精靈添加到了精靈組中,添加之后, 現(xiàn)在運行程序, 屏幕上就有了一個敵人的飛機.
精靈組中可以包含多少個精靈?精靈組中可以包含多個精靈.
那既然可以包含多個精靈,就對這個代碼進行一個擴展,再來定一個敵機的對象,給他起個名字叫做enemy1,然后同樣使用GameSprite()來創(chuàng)建一個敵人的飛機,再指定一下圖像位置,image下的enemy1.png,指定完成,在創(chuàng)建游戲精靈時,還可以指定一下精靈的速度.
那現(xiàn)在把第2架飛機的速度值乘2,這樣呢,可以跟第1架敵機加以區(qū)分,現(xiàn)在創(chuàng)建了兩架敵機的精靈,就可以把第2架敵機同樣也添加到敵機的精靈組中.
現(xiàn)在就以多值參數(shù)的方式再添加一個敵機, 代碼改造完成.

現(xiàn)在運行一下程序,看一下屏幕上是有幾個敵機.
游戲啟動了,兩個敵人的小飛機從上向下持續(xù)飛行,并且飛行的速度各不相同.

再來對比一下現(xiàn)在的代碼,通過一個簡單的改造再創(chuàng)建一個敵機的精靈,把敵機的精靈添加到精靈組中, 不需要在對游戲循環(huán)的代碼進行任何的修改, 輕輕松松就可以在游戲中又增加了一個敵機的對象.
在這一小節(jié),就使用之前一個小節(jié)也派生出來的游戲精靈為創(chuàng)建了兩架敵機,并且實現(xiàn)了一下敵機的動畫效果.
在這一小節(jié)中,重點是要體會一下精靈和精靈組的職責,精靈這個對象負責封裝圖像, 位置以及速度并且提供一個update的方法,update 方法,會根據(jù)游戲的需求修改精靈自己應該更新的位置,這個是精靈的職責,而精靈組負責包含多個精靈對象, 精靈組只需要在游戲循環(huán)中,分別調用一下update方法和draw 方法就可以了,update 方法可以更新組中所有精靈的位置,而draw 方法呢會把組中所有的精靈全部繪制在屏幕對象上.
總結
到此這篇關于Python游戲開發(fā)之精靈和精靈組的文章就介紹到這了,更多相關Python精靈和精靈組內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python循環(huán)神經網絡RNN函數(shù)tf.nn.dynamic_rnn使用
這篇文章主要為大家介紹了python循環(huán)神經網絡RNN的tf.nn.dynamic_rnn使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
Keras搭建Mask?R-CNN實例分割平臺實現(xiàn)源碼
這篇文章主要為大家介紹了Keras搭建Mask?R-CNN實例分割平臺實現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
深度學習環(huán)境配置之Anaconda安裝和pip源方式
這篇文章主要介紹了深度學習環(huán)境配置之Anaconda安裝和pip源方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
Windows下Python使用Pandas模塊操作Excel文件的教程
Pandas是一個強大的Python數(shù)據(jù)分析模塊,這里我們先使用ANACONDA來幫助獲取Pandas所以來的一些環(huán)境,然后來初步學習Windows下Python使用Pandas模塊操作Excel文件的教程2016-05-05
一文帶你掌握Python內置reversed函數(shù)的使用
Python作為一門強大的編程語言,提供了許多內置函數(shù)來處理各種數(shù)據(jù)結構和對象,本文將詳細探討reversed函數(shù)的用法、示例代碼以及實際應用場景,需要的可以參考下2024-01-01

