本文來源:書圈
準備好了嗎?我們即將開始激動人心的遊戲編程之旅。
或許你之前學習過一點編程,但若是你從沒接觸過遊戲編程,那麼你仍然會對遊戲程序的運行感到不解。遊戲程序不像計算一個公式或謎題,得到答案之後程序就結束了,遊戲程序一直是處於運行中的,只要你不主動退出,那麼你可以永遠呆在遊戲之中。這就遊戲循環的神奇魔力。
下面我們嘗試用最少的代碼來編寫一個小遊戲。
準備工作
01選擇合適的開發工具
「工欲善其事必先利其器」,編寫遊戲之前得挑選一款合適的工具,這樣可以大大地簡化程序編寫工作。Python語言有很多第三方庫都提供遊戲編程功能,最有名的要屬Pygame庫,它提供了豐富的API來實現遊戲的各種效果。但是對於初學者來說Pygame庫還是顯得有些複雜,我們希望採用更加簡潔高效的工具,使得可以把注意力集中在遊戲算法的實現上,而不需花費太多精力去學習遊戲開發庫的使用。
這裡採用Pgzero庫來編寫遊戲。Pgzero的完整名稱是Pygame Zero,不難看出它是從Pygame庫衍生而來的。可以說Pgzero就是Pygame的一個精簡版本,能夠實現Pygame庫的主要功能,但是屏蔽了一些複雜的細節,使得初學者能夠快速上手。
02 設置開發環境
由於Pgzero是Python的第三方庫,它不能獨立工作,必須在Python代碼中來使用,因此我們首先需要安裝Python開發環境。可以去Python官網下載最新的安裝包進行安裝,然後便可以使用Python提供的IDLE編輯器來編寫代碼了。
且慢,你是否覺得使用IDLE編輯器來編寫程序不是那麼方便呢?對於簡單的小程序當然無所謂了,但是遊戲程序相對來說還是比較複雜的,而且遊戲中需要調用一些圖片或聲音資源,我們還要對所有的遊戲資源進行統一管理。因此我們還得尋找一個更加靈活方便的遊戲編寫工具,在這裡我採用的是Mu編輯器。Mu是一個專門為Python學習者設計的一個開發工具,它的編輯器非常友好,提供了很多的便捷操作,比如代碼自動提示、代碼縮進標示、語法檢查等等功能。更重要的是,它已經集成了Pgzero庫,而且還提供對遊戲資源的管理,這正是我們所需要的不是嗎?Mu編輯器可以在官網(https://codewith.mu/)下載安裝,現在我們直接運行Mu試一下。在初次打開Mu的時候會提示選擇運行模式,如圖1所示。
我們單擊滑鼠來選擇「Pygame Zero模式」,接下來Mu便會切換到Pgzero模式,看到的運行界面如圖2所示。
Mu編輯器中的空白區域便是我們將要編寫代碼的地方,當程序寫好之後,我們單擊界面上方的「開始」按鈕便可以運行程序了。看起來真是太棒了,還等什麼呢?趕快開工吧!
從何處開始
接下來開始編寫遊戲。可是,遊戲程序究竟什麼樣呢?或許你會在屏幕輸出「Hello World」,或者你知道如何編程計算斐波拉契數列的值,但是你真的確定遊戲程序應該如何編寫麼?
首先,遊戲運行得有一個圖形界面(當然,早期的計算機遊戲可能是文本界面的,但那已經是很古老的事了,現在我們探討的都是基於圖形界面的遊戲)。為了顯示圖形界面,我們的程序應該能夠生成一個「窗口」,在其中可以顯示各種圖形或圖像,而遊戲的內容正是由各種不同的圖形或圖像來表示的。
我們試著來創建一個程序窗口。
01 創建程序窗口
在Mu編輯器上方的工具欄中單擊「新建」按鈕,可以看到編輯器中出現了一塊空白區域,這便是新創建的Python源程序文件。
然後,單擊「運行」按鈕試試,你會看到屏幕上出現了一個窗口,如圖3所示。
感覺如何?是不是驚訝得合不攏嘴?明明連一行代碼都沒有寫,竟然就能出現一個窗口。這正是Pgzero的神奇之處。事實上,Pygzero已經幫我們做了大量的「幕後工作」,使得我們可以專注於編寫遊戲邏輯,而不用太關注顯示方面的問題。
然而眼前這個窗口黑乎乎的,並不是不太好看,而且窗口的大小也不是自己想要的。不要著急,我們一點點的來解決問題。
02改變窗口大小和顏色
首先解決窗口尺寸問題。在Pgzero中,通過定義兩個常量值來確定程序窗口的大小,代碼如下所示:
WIDTH = 500HEIGHT = 300
注意WIDTH和HEGIHT是Pgzero預設的兩個常量,分別用來表示程序窗口的寬度和高度值(單位為像素)。上面的代碼表示將程序窗口的寬度值設為600像素,高度設為400像素。我們將這兩行代碼敲入剛剛新建的源程序文件中,然後再次運行一下,可以看到窗口的大小發生了改變。
接下來我們試著改變一下窗口的背景顏色。在Pgzero中,窗口的背景顏色默認是黑色(原來如此),若要改變背景顏色,需要在程序中定義一個draw()函數。那麼這個draw()函數又是個什麼來頭呢?
draw()函數是Pgzero的「幕後主使」之一,它負責顯示遊戲中的各種圖形或圖像。我們只需在程序中定義自己的draw()函數,然後將需要繪製圖形圖像的代碼寫進draw()函數中,程序便會自動地執行draw()函數來進行顯示。
那麼,要改變窗口的顏色,究竟要在draw()函數中編寫什麼代碼呢?此時我們還需要藉助Pgzero提供的內置對象screen來完成。事實上,Pgzero為了簡化遊戲編程,在內部設置了很多的對象來協助完成遊戲中的各項操作。screen對象主要就是用來在窗口繪圖的,它提供了很多的繪圖方法,不僅能夠繪製圖形和圖像,還能繪製文字信息,在遊戲編程中我們會經常使用到它。
目前我們需要使用的是screen對象的fill()方法,它表示用某種顏色來填滿整個窗口。該方法接受一個RGB元組作為參數。那什麼是RGB元組呢?RGB元組是由三個數所組成的元組,每一個數代表一個顏色分量,比如(255,0,0)表示紅色,(0,255,0)表示綠色,(0,0,255)表示藍色,(0,0,0)代表黑色,(255,255,255)代表白色等。
於是我們可以在原始碼中加入以下兩行代碼:
def draw():screen.fill((255, 255, 255))
保存並運行程序,可以看到如圖4所示的界面。沒錯,我們的窗口背景變成了白色。
03 顯示圖像
現在我們擁有了一個程序窗口,但它似乎空空如也,並沒有什麼內容。我們希望在窗口裡面顯示點什麼。比如我們準備了一幅精美的圖片,想將它顯示在窗口中,如何做到呢?
首先我們要將圖片文件放到指定的位置,即「images」文件夾中。單擊Mu編輯器上方的「圖片」按鈕,會自動打開「images」文件夾。我們將圖片文件複製到該文件夾中即可。
將圖片文件準備好並放入「images」文件夾後,我們便可以將其顯示在窗口中,這需要調用screen對象的blit()方法。比如要顯示一個名為「breakout_ball」的小球圖片,我們只需要在程序中加入一行代碼:
screen.blit("breakout_ball", (200, 100))
blit()方法的第一個參數是要顯示的圖片文件名,以字符串來表示(不要帶後綴),第二個參數為圖像顯示的坐標。該坐標是由兩個數所組成的元組,第一個數表示圖像的橫坐標,第二個數則為圖像的縱坐標。由於Pgzero中窗口的坐標原點位於左上角,向右橫坐標值增加,向下縱坐標值增加,因此坐標(200,100)表示圖像從窗口左邊界向右偏移200個像素,從窗口上邊界向下偏移100個像素。
到目前為止我們已經編寫了5行代碼,如下所示:
WIDTH = 500HEIGHT = 300def draw():screen.fill((255, 255, 255)) screen.blit("breakout_ball", (200, 100))
現在運行程序,可以看到圖5所示的程序界面,其中標示了圖像的坐標值所代表的含義。
現在我們不僅擁有了一個程序窗口,而且還在裡面顯示了一幅圖像,真是太了不起了。但別高興太早,現在這個程序還不能稱為遊戲。我們都知道,遊戲中的圖形或圖像是會「活動」的,也就是說它們可以不斷地改變位置進行顯示,而我們的程序目前只能在某個固定的位置顯示一幅圖像,它根本就不能動。不要灰心,接下來我們就想辦法讓它動起來。
建立遊戲世界
在採取行動之前,我們有必要來了解一下遊戲的基本概念。在遊戲的世界中有兩個基本要素:場景和角色。遊戲場景是指遊戲發生的場所,或者說遊戲的一個特定情景。通常我們會為遊戲製作一些尺寸比較大的圖片,以此作為遊戲場景的背景圖像;遊戲角色是指遊戲場景中的各種物體,它們不僅有特定的圖像,更重要的是它們能夠活動(通常是在場景範圍內活動),而且彼此之間還能發生相互作用。
若是我們想設計遊戲,則必須要為遊戲創建場景和角色。那麼如何操作呢?
01 創建遊戲場景
首先來創建遊戲場景。其實遊戲場景我們之前已經做好了。沒聽錯吧?我們好像什麼都沒做啊,僅僅是建立了一個程序窗口,然後用白色將它填充了一下。沒錯,這就算是一個遊戲場景。遊戲場景可以很複雜,也可以很簡單,就如同我們所做的,僅僅是用單一色彩來填充窗口,也可以作為遊戲的場景。因為場景的主要作用是為各個遊戲角色提供一個活動的場所,只要能夠保證角色能夠正確地顯示其中就可以了。
02創建遊戲角色
接下來創建遊戲角色。角色的創建似乎沒那麼簡單,因為角色是需要活動的,而我們之前在窗口中顯示的小球根本無法活動,所以它還不能算作遊戲角色,僅僅只是一幅圖像而已。怎麼辦呢?好在Pgzero事先已經為我們準備好了,它通過提供一個叫做Actor的類來幫助我們創建遊戲角色。比如要創建一個小球角色,可以這樣編寫代碼:
ball = Actor("breakout_ball", (200, 100))
上面這行代碼調用Actor類的構造方法來生成小球角色對象,並將其保存在一個變量ball中,今後若要操作小球則只需訪問ball變量即可。Actor類的構造方法有兩個基本參數,第一個是角色的圖片文件名,第二個是角色的初始位置。這和之前顯示圖像的參數是一樣的。
小球角色是創建好了,那麼如何將它顯示在窗口中呢?是不是還和之前一樣,需要調用screen的blit()方法呢?當然不需要了。現在的小球已經不再是一幅圖像了,而是一個真正的角色對象,它擁有很多的屬性和方法可以使用。其中有一個叫做draw()的方法,可以用來將自身顯示在窗口中。
我們將之前的代碼改寫如下:
WIDTH = 500HEIGHT = 300ball = Actor("breakout_ball", (200, 100))def draw():screen.fill((255, 255, 255)) ball.draw()
運行一下你會發現,程序的結果和圖5所顯示的效果是一模一樣的。可是現在小球還是不會動呀?不要著急,我們已經做好了一切準備工作,現在是時候讓它動起來了。
移動小球
倘若想要移動小球,必須改變它在窗口中的位置,即小球顯示的坐標。在Pgzero中,角色對象擁有兩個屬性:x和y,前者表示角色在窗口中的橫坐標,後者表示角色在窗口中的縱坐標。由於小球目前已經被定義為角色對象,我們就可以直接修改它的x和y屬性來改變其坐標值。
還有一件事需要注意,Pgzero規定所有對角色操作的代碼都要放置在一個叫做update()函數中。因此我們首先定義一個update()函數,然後將改變小球坐標的代碼放入其中,如下所示:
def update():ball.x += 1
運行一下程序,你會發現小球開始緩緩地向右移動。真是棒極了!可這到底是怎麼回事呢?明明只寫一行代碼啊,小球的x坐標應該只增加1一個單位才對,怎麼它會一直朝著右邊移動呢?
嘿嘿,這就是遊戲循環的神奇魔力!
02 遊戲循環
究竟什麼是遊戲循環呢?如果你有一點編程經驗,你一定編寫過循環程序。所謂循環程序,就是程序在滿足指定的條件下,重複不斷地執行某些操作。遊戲循環也是類似的原理,即把遊戲操作的程序代碼放置在一個循環語句中,讓其自動地重複執行。那麼遊戲循環的執行條件是什麼呢?循環體中又該執行什麼樣的語句呢?
先來看看遊戲循環的條件。想一想你玩遊戲的經歷,當你玩遊戲的時候,除非你主動選擇退出,否則你是一直處於遊戲之中的。難道不是嗎?從程序角度來看,自從你進入遊戲開始玩的那一刻開始,你就已經處於遊戲循環之中了,而且是一直處於其中的。因此,遊戲循環的執行是無條件的,它本質上就是個死循環!天哪沒聽錯吧,編程課時老師可特別強調過,「編寫循環程序時要檢查循環條件,千萬別寫成了死循環」,沒想到遊戲程序竟然是個死循環。沒錯,遊戲就是個死循環,或者稱為無限循環。
我們可以用while語句來表示遊戲循環,代碼如下所示:
while True:執行遊戲操作
可以看到,while語句的循環條件設為了True,而True是個布爾類型的常數,表達的含義就是「真」。因此,while循環會一直重複地執行下去。
接著看看遊戲循環中的操作語句應該如何編寫。作為一個遊戲,它要執行兩個最基本的操作:一個是更新遊戲邏輯,包括改變角色位置或圖像,處理角色之間的相互作用,切換遊戲場景,等等;另一個是繪製遊戲圖像,包括繪製遊戲的背景,繪製角色的圖像,繪製文字信息,等等。如圖6所示
在之前的程序中,我們編寫了update()函數來改變小球坐標,也編寫了draw()函數來繪製小球圖像,而這兩個函數就恰好分別對應了遊戲循環中的兩個基本操作:update()函數用來更新遊戲邏輯,而draw()函數則用來繪製遊戲圖像。由於遊戲是不斷地在運行著的,需要不斷地更新遊戲邏輯,同時將更新後的內容重新顯示出來,因此要將update()函數和draw()函數放入遊戲循環中重複執行。程序看起來應該像這樣:
while True:update() draw()
然而,我們編寫代碼的時候可並不是這樣寫的,我們只是在程序中定義了update()和draw()函數,卻並沒有通過類似的無限循環語句來調用它們。確實是這樣,因為Pgzero不需要我們這樣做,它已經在內部預先設定好了一個遊戲循環,我們只負責定義update()和draw()函數,並將更新遊戲邏輯和顯示遊戲圖像的代碼分別寫入其中即可,Pgzero內部的遊戲循環會自動調用這兩個函數。
當單擊Mu編輯器上的「開始」按鈕時,程序會啟動遊戲循環來開始遊戲;當單擊「停止」按鈕時,程序便會終止遊戲循環來退出遊戲。
現在終於明白遊戲程序竟然是這樣運作的,有一點小小的滿足感,原來遊戲並沒有想像的那麼神秘嘛。既然Pgzero已經幕後安排好了一切,那我們只需要集中精力為update()和draw()這兩個函數編寫代碼就好啦。沒錯,就是這麼簡單!
完善遊戲規則
目前還存在一個問題,那就是當小球移動到窗口之外後,它便消失得無影無蹤了。作為遊戲角色的小球竟然跑到了場景外面!玩過遊戲的朋友都知道,遊戲角色是不能置於場景之外的,可怎樣將小球的活動範圍限定到窗口之內呢?
我們需要做兩件事情:一是檢查小球是否跑到了場景外面;二是讓小球重新回到場景之中。
01 檢測小球的位置
若要知道小球是否跑到場景之外,我們可以將它的位置與窗口進行比較,比如,如果小球的右邊界超過了窗口的右邊界,則可判定小球即將從右方跑出場景。那麼如何用程序來表達這個意思呢?
目前我們只知道小球的x屬性表示橫坐標,y屬性表示縱坐標。而不論是x還是y的值,都是根據角色中心點的位置來計算的,所以準確來說,小球的x屬性其實是小球中心點的橫坐標,而y屬性是小球中心點的縱坐標。那麼如何表示小球右邊界的坐標呢?
Pgzero為角色對象提供了4個屬性left、right、top、bottom,分別表示角色的左、右、上、下各個邊界的位置。具體來說,left和right分別表示角色左邊界和右邊界與窗口左邊界的距離;top和bottom分別表示角色上邊界和下邊界與窗口上邊界的距離。於是可以通過ball對象的right屬性來獲取小球右邊界的位置。而要想知道小球的右邊界是否超過了窗口右邊界,則需要判斷小球的right屬性是否大於窗口的寬度WIDTH,這可以藉助條件語句if來實現,代碼類似如下形式:
if ball.right > WIDTH:讓小球重新回到窗口內
倘若條件成立,那如何讓小球回到窗口之內呢?這就看你的意思了。換句話就是說,你想怎樣讓小球回到窗口之內都可以,比如可以讓小球從窗口右邊跑出去,然後從窗口左邊重新跑進來;或者當小球跑到場景之外後,讓它直接回到窗口中的某個指定位置,等等。你完全可以按照自己的想法來規定小球的動作,然後去編寫代碼實現,遊戲便會忠實地按照你的想法來執行。其實這就是所謂的遊戲規則設計,也是遊戲設計的最大樂趣所在,因為此刻你就是造物主,遊戲世界將會按照你制定的規則來運轉。超有成就感是不是?!
02 讓小球回到窗口內
下面考慮這樣一種規則,那就是當小球超出窗口邊界後,讓它重新回到窗口的另一側。比如,小球如果向右移動時超出了窗口右邊界,則讓它從窗口左邊界出現。
我們將update()修改為如下代碼:
def update():ball.x += 1 if ball.right > WIDTH: ball.x = 0
我們把這段代碼「翻譯」一下,它表示:小球的橫坐標先是增加1個單位,如果它的右邊超出了窗口右邊界,則將它的橫坐標設為0。運行看看是什麼效果,是不是發現小球樂此不疲地在窗口中跑起來了呢?
好了,我們的小遊戲至此就編寫完成了,想必你已經了解遊戲程序是怎麼一回事了。是不是覺得很簡單?想不想自己動手試一試呢?
下面給出小遊戲的完整源程序。不多不少,剛好十行代碼!
WIDTH = 800 HEIGHT = 600 ball = Actor("breakout_ball")def update():ball.x += 1 if ball.right > WIDTH: ball.x = 0def draw(): screen.fill((255, 255, 255)) ball.draw()
本文節選自《趣學Python遊戲編程》一書
版權歸原作者所有
- 相關推薦 -
《 趣學Python遊戲編程 》
作者:何青
清華大學出版社出版
內容簡介
本書是高校教師多年開發經驗的結晶之作,深入淺出地講解使用 Python語言進行遊戲開發,幫助讀者快速掌握遊戲設計的基本原理和方法,同時提高應用 Python語言的編程能力。全書精選十個遊戲案例,涵蓋不同的遊戲類型,每一章圍繞一個經典遊戲案例展開,並突出一個遊戲編程的主題。本書涉及的主要知識點有遊戲循環的原理、滑鼠及鍵盤事件處理、碰撞檢測及處理、隨機數的運用、定時器的使用、遊戲場景的滾動、角色動畫的播放、音效及音樂的播放、緩動效果、遊戲關卡設計、遊戲人工智慧的原理及運用等。本書將 Python語法知識及常用的編程技巧糅合在各個遊戲案例中介紹,為讀者展示 Python語言的實際運用場景。本書內容安排合理,架構清晰,注重理論與實踐相結合,適合作為零基礎學習 Python開發初學者的教程,也可作為本科院校及大專院校的教材,還可供職業技術學校和各類遊戲培訓機構使用。