這幾天偶爾翻翻Python Crash Course
,前面的內容還是比較淺顯的,在第十三章上看到了一個用pygame開發的打飛機的小游戲。之前沒有接觸過pygame,有點興趣,于是跟著作者的思路一步步琢磨著下去,大概做了60%,最后打飛機分數與開始按鈕都沒有做,代碼量雖然不多,但是坐著寫得很詳細,導致所占篇幅有點多,而且到了后面,估計作者也寫得有點暈,漏了些許代碼,看得我糊涂就沒有繼續下去,只有大概雛形,但是就這么個百來行代碼,還是學到了很多東西:對于一些較大型項目的就夠規劃以及對“動態語言-python”的理解。
1. Pygame中的游戲元素
依照書范例中的打飛機(外星人入侵)游戲。游戲元素:alien,bullet,ship,以及這些元素的settings。每個元素單獨的一個py文件,一個文件中一個class,結構很清晰,不會在同一個文件中放兩個不同的類,這讓我學到了,因為我最近也在偷偷搞一個類以解決公司項目亂攤子的狀況。本想一個文件解決,現在讓我改變注意了。
2. 游戲畫面
Pygame中有一個類,名為pygame.Surface
,文檔中解釋為 pygame object for representing images。比如,游戲的主窗口稱為display Surface
。Surface能代表任意的圖片對象,比如飛船(ship)。Surface類有一個方法pygame.Surface.get_rect
會得到一個pygame.Rect
實例(不知道這里有沒有說錯,我怕是這么理解的)。這個Rect類代表的是什么?其實就是游戲中的每一個元素都是一個矩形。而整個游戲畫面也是個矩形,可以以矩形左上角為坐標原點(0,0)在上面劃坐標軸,往右往下為正。Rect object
有許多屬:x,y
top, left, bottom, right ....
這里屬性很多,但是轉換為矩形了,就會好處理的多。有一個例子,alien(外星人飛船)要往左或者往右偏移,可以設置direction =1為往右,-1往左,偏移量x *-1
就是負數,在坐標軸上就是往左。這里涉及了很多基本數學的應用,有很多意想不到的解決方法,這方面還是值得好好研究一番的。
3. 游戲流程
def run_game():
# Initialize game and create a screen object.
pygame.init()
...
...
while True:
# Watch for keyboard and mouse events.
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
精簡下來主流程就這么多。main函數一直在一個while的循環中,用于一直在那刷新游戲頁面。所謂游戲畫面更新:人眼看到的例如游戲人物的走動其實就是某一時間段刷新著某一部分畫面,想想小時候快速翻看連環畫冊的效果,就好理解了。這里的gf.check_events(ai_settings, screen, ship, bullets)
用來檢測一些event,用戶的某個按鍵,鼠標的點擊....檢測到了之后久坐動作,比如用戶按了Q鍵,那么我們可以講游戲畫面退出,方便用戶不用鼠標點擊右上角的 X;ship.update()
,刷新飛船的位置;下面兩行類似,但要注意,bullets是復數,在此之前線創建一個bullet組,bullets = Group()
,因為一個游戲畫面有很多一樣的游戲元素(在這里的是子彈與外星人),添加一個組之后能較為方便的管理所以的相同元素;gf.update_screen(ai_settings, screen, ship, aliens, bullets)
用來刷新游戲畫面,更新之前的游戲元素所變化的的位置,達到之前所說的“動態”的效果。
4. 鴨子類型
python是一門動態語言,有“鴨子類型”特性:
當看到一只鳥走起來像鴨子、游泳像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。
鴨子類型給予Python這樣的動態語言以多態。但是這種多態的實現完全由程序員來約束強制實現(文檔、清晰的代碼和測試),并沒有語言上的約束(如C++繼承和虛函數)。因此這種方法既靈活,又提高了對程序員的要求。
而在這個項目中的一些定義函數或者類可以窺探出:為了增加邏輯結構的清晰度,凡是一個有循環以上的代碼量,就必須重新定義一個額外的函數、舉個簡單例子:
def print(a,b,c,d):
hello (a,b,c,d)
world (b,c,d)
def hello(a,b,c,d):
....
def world(b,c,d):
....
剛開始我覺得這樣寫有點變扭,因為都是括號里都是參數,看得好亂。雖然之后發現它邏輯比較清晰但是我還是有點無法接受,只能慢慢在習慣中向它靠攏。
在這個project中鴨子類型的也體現了很多,這里就不一一說了,之前我也沒有這樣的習慣。因為: ......... 這樣寫pycharm不提示語法了、⊙﹏⊙b汗
補充 ,我覺得我上面的想法有所欠缺:
最開始接觸 Python 的時候,覺得這一特性很好用啊,例如我要處理一大串對象,只要里面的對象實現了這個方法,就可以都扔到同一個隊列里面進行處理,但這個東西很容易濫用。如果你在處理之后需要相應的回調,這又必須跑去實現回調函數,但因為函數調用的檢查是在處理該對象的時候,如果忘了某些對象的相應方法,可能最后上線才能發現問題。
接觸了一陣子的 golang,使用 interface 之后簡直如沐春風,運行時的錯誤在編譯階段就能避免了,減少了很多查 bug 的時間。在這一點上,如果需要使用類似的特性,python 對程序員的能力要求反而更高了。
我個人比較推崇先去判斷這個對象是什么類型,如判斷它是不是可以迭代的,可調用的,這樣子,你就知道了該對象支持什么樣的方法。如果不行再去判斷是否實現了具體的方法。當然,運行時去判斷這些東西是有消耗的,具體的需要就要看業務場景了。
靈活是拋棄了一些約束的結果,沒有了約束效率就會低下,哪樣較好沒有絕對的定論。
以上內容來自[https://lycheng.github.io/2017/06/17/python-abstact-class.html "虛擬基類")
5. 小結
Pygame
與matplotlib
一樣都是大型庫(當然,相比較pygame要小很多很多),具體的方法文檔以及用法可以去官網查看,我了解的很少,這篇文章只是對我初看pygame的一個小總結。以后接觸的也應該不多,若要真正學起來,需要很多的時間去慢慢學習。這里推薦一個博客http://eyehere.net/2011/python-pygame-novice-professional-1/。留著以后自己去看一看,還會學到更多。