用Python和Pygame寫遊戲-從入門到精通(實戰一:塗鴉畫板1)

NO IMAGE

用Python和Pygame寫遊戲-從入門到精通(實戰一:塗鴉畫板1)

從這次開始,我會由簡單到困難(其實也不會困難到哪裡去)講幾個例程,每一個例程都是我自己寫(或者修改,那樣的話我會提供原始出處)的,都具有一定的操作性和娛樂性。例程中匯儘量覆蓋到以前所講的pygame中方方面面,如果看到哪一步不明白,那就再回去複習複習,基本沒有人會看一遍什麼都記住什麼都掌握的,重複是學習之母,實踐是掌握一門技藝的最好手段!

這次就先從一個最簡單的程式開始,說實話有些太簡單我都不好意思拿出手了,不過從簡單的開始,容易建立自信培養興趣。興趣是學習之母嘛。我們這次做一個畫板,類似Windows裡自帶的畫板,還記不記得第一次接觸電腦用畫板時的驚歎?現在想起來其實那個真的非常簡陋,不過我們的比那個還要樸素,因為打算一篇講完,就不追加很多功能了,等你把這一次講解的都理解了,很容易可以自己給它增加新的機能。沒準,你就開發出一個非常牛X的畫圖工具擊敗了Photoshop,然後日進斗金名垂千古(眾:喂,別做夢了!)……

功能樣式

做之前總要有個數,我們的程式做出來會是個什麼樣子。所謂從頂到底或者從底到頂啥的,咱就不研究了,這個小程式隨你怎麼弄了,而且我們主要是來熟悉pygame,高階的軟體設計方法一概不談~

因為是抄襲畫圖板,也就是滑鼠按住了能在上面塗塗畫畫就是了,選區、放大鏡、滴管功能啥的就統統不要了。畫筆的話,基本的鉛筆畫筆總是要的,也可以考慮加一個刷子畫筆,這樣有一點變化;然後顏色應該是要的,否則太過單調了,不過調色盤啥的就暫時免了,提供幾個候選色就好了;然後橡皮……橡皮不就是白色的畫筆麼?免了免了!還有啥?似乎夠了。。。 OK,開始吧!

框架

pygame程式的框架都是差不多的,考慮到我們這個程式的實際作用,大概建立這樣的一個程式碼架子就可以了。

這個非常簡單,準備好畫板類,畫筆類,暫時還都是空的,其實也就是做了一些pygame的初始化工作。如果這樣還不能讀懂的話,您需要把前面22篇從頭再看看,有幾句話不懂就看幾遍:)

這裡只有一點要注意一下,我們把幀率控制在了30,沒有人希望在畫畫的時候,CPU風扇狂轉的。而且只是畫板,沒有自動運動的物體,純粹的互動驅動,我們也不需要很高的重新整理率。

第一次的繪圖程式碼

按住滑鼠然後在上面移動就畫東西,我們很容易可以想到這個流程:

立刻試一試吧:

框架中有的程式碼我就不貼了,用#*#*#*#*#代替,最後會給出完整程式碼的。

這裡主要是給Brush類增加了一些功能,也就是上面我們提到的流程想對應的功能。留下痕跡,我們是使用了在座標上畫圓的方法,這也是最容易想到的方法。這樣的效果好不好呢?我們試一試:

哦,太糟糕了,再劣質的鉛筆也不會留下這樣斷斷續續的筆跡。上面是當我們滑鼠移動的快一些的時候,點之間的間距很大;下面是移動慢一些的時候,勉勉強強顯得比較連續。從這裡我們也可以看到pygame事件響應的頻度(這個距離和上面設定的最大幀率有關)。

怎麼辦?要修改幀率讓pygame平滑的反應麼?不,那樣做得不償失,換一個角度思考,如果有間隙,我們讓pygame把這個間隙連線起來不好麼?

第二次的繪圖程式碼

思路還是很簡單,當移動的時候,Brush在上一次和這一次的點之間連一條線就好了:

在__init__和start_draw中各加了一句,用來儲存上一個點的位置,然後draw也由剛剛的話圓變成畫線,效果如何?我們來試試。嗯,好多了,如果你動作能溫柔一些的話,線條已經很圓潤了,至少沒有斷斷續續的存在了。

滿足了麼?我希望你的回答是“NO”,為什麼,如果你劃線很快的話,你就能明顯看出稜角來,就好像左圖上半部分,還是能看出是由幾個線段組合的。只有永不滿足,我們才能不停進步。

不過對我們這個例程而言,差不多了,一般人在真正畫東西的時候,也不會動那麼快的:)

那麼這個就是我們最終的繪圖機制了麼?回頭看看我們的樣式,好用還需要加一個筆刷……所謂筆刷,不僅僅是很粗,而且是由很多細小的毛組成,畫出來的線是給人一種一縷一縷的感覺,用這個方法可以實現麼?好像非常非常的困難。。。孜孜不倦的我們再次進入了沉思……

這個時候,如果沒有頭緒,就得借鑑一下前輩的經驗了。看看人家是如何實現的?

把Photoshop的筆刷尺寸改大,你會發現它會畫成這樣

如果你的Photoshop不錯,應該知道它裡面複雜的筆刷設定,而Photoshop畫出來的筆畫,並不是真正一直線的,而是由無數細小的點組成的,這些點之間的間距是如此的密,以至於我們誤會它是一直線……所以說,我們還得回到第一種方法上,把它發揚光大一下~ 這沒有什麼不好意思的,放棄第二種方法並不意味著我們是多麼的愚蠢,而是說明我們從自己身上又學到了很多!

(公元前1800年)醫生:來,試試吃點兒這種草根,感謝偉大的部落守護神賜與我們神藥!
(公元900年)醫生:別再吃那種草根,簡直是野蠻不開化不尊重上帝,這是一篇祈禱詞,每天虔誠地向上帝祈禱一次,不久就會治癒你的疾病。
(公元1650年)醫生:祈禱?!封建迷信!!!來,只要喝下這種藥水,什麼病都能治好!
(公元1960年)醫生:什麼藥水?早就不用了!別喝那騙人的”萬靈藥”,還是這種藥片的療效快!
(公元1995年)醫生:哪個庸醫給你開的處方?那種藥片吃半瓶也抵不上這一粒,來來來,試試科技新成果—抗生素
(公元2003年)醫生:據最新科學研究,抗生素副作用太強,畢竟是人造的東西呀……來,試試吃點兒這種草根!早在公元前1800年,文獻就有記載了。

返璞歸真,大抵如此了。

第三次的繪圖程式碼

這次我們考慮的更多,希望在點與點之間充滿我們的筆畫,很自然的我們就需要一個迴圈來做這樣的事情。我們的筆畫有兩種,普通的實心和刷子,實心的話,用circle來畫也不失為一個好主意;刷子的話,我們可能需要一個刷子的圖案來填充了。

下面是我們新的Brush類:

我們增加了幾個方法,_get_points()返回上一個點到現在點之間所有的點(這話聽著真彆扭),draw根據這些點填充。
同時我們把get_size()、set_size()也加上了,用來設定當前筆刷的大小。
而變化最大的,則是set_style()和get_style(),我們現在載入一個PNG圖片作為筆刷的樣式,當style==True的時候,draw不再使用circle填充,而是使用這個PNG樣式,當然,這個樣式大小也是應該可調的,所有我們在set_size()中,會根據size大小實時的調整PNG筆刷。

當然,我們得在主迴圈中呼叫set方法,才能讓這些東西工作起來~ 過一會兒再講。再回顧下我們的樣式,還有什麼?顏色……我們馬上把顏色設定程式碼也加進去吧,太簡單了!我這裡就先偷偷懶了~

控制程式碼

到現在,我們已經完成了繪圖部分的所有功能了。現在已經可以在螢幕上自由發揮了,但是筆刷的顏色和大小好像不能改啊……我們有這樣的介面你卻不呼叫,浪費了。

…… 真抱歉,我原想一次就把塗鴉畫板講完了,沒想到筆刷就講了這麼多,再把GUI說一下就文章就太長了,不好消化。所以還是分成兩部分吧,下一次我們把GUI部分加上,就能完成對筆刷的控制了,我們的第一個實戰也就宣告成功了!