一個仿windows泡泡屏保的實現

一個仿windows泡泡屏保的實現算法

有天看到有人在百度知道上問windows 泡泡屏保該怎麼用C#作,一時有趣,就作了一個出來,對於其中幾個要點總結以下:windows

一,屏保程序的製做要求

屏保程序的擴展名是.scr, 但其實仍是一個exe文件,只要把編譯好的exe文件擴展名改成.scr,就變成了一個屏保了。工具

但作爲屏保程序,也對之有必定的要求以下:測試

1.應該是一個全屏的、無邊框的程序。優化

2.退出機制應該符合屏保的操做習慣,如動鼠標就退等。(我在這個例子裏是用esc作退出。).net

3.支持如下命令行參數:命令行

/c , 顯示選項對話框。設計

/p, 顯示預覽。指針

/s, 或不加參數:正常運行。orm

 

提醒注意一點: 當程序擴展名修改以後,.config配置文件也須要同時更名。好比原來叫popo.exe, 配置文件就是popo.exe.config, 當你把popo.exe改成popo.scr時,也要把配置文件改成popo.scr.config。

二,程序運行原理

 接下來,就是要作一個全屏的泡泡程序了。原理以下:

1.程序啓動時,先把當前的屏幕位圖保存下來備用。

2.設計泡泡對象,記錄本身的位置、大小、速度、顏色等。在設計時,我認爲泡泡怎麼動,怎麼畫都是泡泡本身該知道的,因此,把運行計算方法與繪製方法都放在泡泡類裏了。另外,在計算時,泡泡須要參考環境因素來決定本身的運動狀態,所以還帶進去了一些環境參數。我這裏就把屏幕大小和整個泡泡集合作爲兩個參數放進去了,若是須要的環境變量太多,能夠作個環境類,把這些都包到一個環境對象裏傳遞,或是作爲全局變量來用。

3.作一個無框無內容、最大化、自繪製的form。

4.設置一個定時器。在觸發時,逐一更新泡泡的運動狀態。而後通知form進行重繪。

5.在form重繪時,先把保存下來的屏幕位圖畫到屏幕上,再調用泡泡本身的繪製方法,用gdi+做圖。

 

3、gdi+繪圖

 繪圖部分很簡單,我就只畫了個圓作泡泡,並把圓裏面用線性漸變畫刷刷了個從有顏色到徹底透明色的效果,看起來也挺漂亮的。請看截屏。

 

若是用路徑漸變畫刷繪圖,還能夠作到園心透明的效果。但我沒有繼續改進這塊兒。

關於透明色,能夠用Color.FromArgb()方法來獲得,第一個參數就是不透明度,只要設爲0就是徹底透明瞭,255爲徹底不透明。

 

四,兩種計時器的不一樣

爲了簡化時間處理,我決定採用定時器的方法來作主循環。 一開始,我使用了一個system.form.timer, 也就是從工具欄裏直接拖出來的那個timer,結果發現不管我怎麼把interval搞多小,每秒都只會產生大約18次多一點的tick事件,屏幕更新率上也就最多18fps. 後來我想起來以前有看到過說這個定時器最大精度只有55ms,設再短都是55ms.

因而改用system.timer.timer作定時器。這個定時器不能從工具框里拉出來,只能在form裏代碼定義,在form_load裏設置屬性、委託事件,並啓動。測試後發現它的最小精度彷佛是15ms,不過這樣也能達到每秒Elapsed上60屢次,遠超過人眼須要的24fps刷新率了。

五,winform的重繪效率

1.Form的OnPaint()調用機制

我是在定時器Elapsed時,對全部泡泡進行一次狀態計算,而後調用form.Invalidate(),要求重繪整屏。進行測試後發現,60次計算是真實發生了,但OnPaint只重繪了40屢次,也就是40fps,其中有20次Invalidate並無產生重繪消息。這應該是windows對wm_paint消息的重複出現時的一種處理方式——上次paint沒搞完,此次paint就又來了時,系統就放棄此次的paint消息了。

 

2.double buffered比位圖複製的效率高

在用VC作繪圖程序時,若是你直接在Paint事件裏給你的Graphics句柄上繪圖,這個繪圖的過程就會被直接顯示到屏幕上,一是形成閃耀,二是因爲要向硬件發送繪製信息,繪圖的效率也會變低(低得多!),因此習慣上會建立一個與所給的Graphics設備兼容的「內部」Bitmap對象,在這個內部Bitmap上進行繪製,完成以後再整個複製到Graphics句柄上去。因爲是全覆蓋,因此對原Graphics的句柄都不須要Clear()。

所以,在用C#的winform繪圖時,我也習慣地用了這個方法。在最後優化幀率時卻發現,這個方法並非最快的!

在form對象裏有個屬性作doublebuffered,就是是否採用雙顯示緩衝區。若是設爲True,.net就會給form維護兩個顯示緩衝區,當請你響應wm_Paint繪圖時,給你一個後臺的緩衝請你畫,畫完了,把這個畫好的屏幕切換到前臺來顯示,把原來的前臺切到後臺備用於下一次paint。

有了這個機制,其實就不用本身來建立那個內部bitmap了,只要啓用雙緩衝方式,你就能夠直管向onPaint方法裏送來的那個Graphics上繪製就好,繪製的過程不會被實時顯示到屏幕上。當你結束了OnPaint時,.net會把你畫好的這個屏幕「翻」到前面去。也不會有閃爍。

實際的測試發現,使用單緩衝 + 本身維護內部bitmap繪圖與複製的辦法,要比啓用雙緩衝 + 直接在Graphics上繪圖的效率要低上差很少30%。這應該是由於本身的內部bitmap最後要Draw到Graphics的過程是個位圖複製過程,整屏內容數據量仍是很大的。而雙緩衝時,.net只是交換一個指針值就能夠了。

 

六,碰撞算法

泡泡的運動狀態參數,我用了Vx, Vy兩個值來表示在水平方向和豎直方向上的運行速度,速度分爲正負。這個設計在一些狀況下簡化了運動狀態的計算處理。

1.撞邊

撞邊的檢測很簡單,就是看邊緣座標是否超過屏幕大小,或是小於0。撞上下邊時,修改Vy的值爲-Vy; 撞左右邊時,Vx = -Vx;

 

2.泡泡互撞

這個很頭大,但終於自行搞定了。

我在一個泡泡計算運動時,逐個計算與其餘泡泡兩兩碰撞的狀況,原理就是計算兩個園心的距離(d =( (x1 -x2) ^ 2 + (y1 - y2)^2) ^ 0.5)是否大於兩個泡泡半徑之和,若是不大於,d <= r1 + r2, 那麼說明與這個泡泡發生碰撞。

碰撞以後,若是發現兩個泡泡已經相交了,d < r1 + r2, 第一件事情就是先把兩個泡泡移開到誰不挨誰的位置上,爲了看起來正確一些,我決定按兩圓心的延長線,把兩個泡泡各向後移動重疊部分( r1 + r2 - d) 的一半距離。這種狀況在兩個時候很容易有,一是剛開始,泡泡的位置是隨機的,有大量擠一塊兒的,二是在運動中,一個泡泡若是速度太快,在一次計算週期裏它移動的位置極可能已經讓它超過另外一泡泡的邊界了。我如今想到只是移回到邊界處也不是很對,應該彈回對應的距離才符合實際。

後來發現,各自回退一半距離時,有時會有某個圓的回退方向上會碰到其餘圓,其餘圓又會把它擠回原位置,這個循環引發了一些泡泡間的奇怪行爲,還形成一些偏差積累的問題。最後,我把規則改成只移動其中一個泡泡,這樣就消除了這種問題。

而後,要計算兩個泡泡的碰撞對兩方產生的影響。

我先規定這些泡泡的重量都是相同的,以簡化問題。而後,我推測天然界裏的碰撞過程應該是一個動量(速度矢量 * 質量)交換的過程,交換的法則應該符合平行四邊形法則,結合我Vx,Vy速度份量的設計,我分析了在兩個正交方向上向對方輸出動量的計算方法,好比下圖是B圓的Vy動量向A圓Vy和Vx輸出的分析:

 

如圖:Vy向A圓的輸出動量大小,應該是Vy在法線上的投影ObP, 這個投影是個矢量,正指向A圓圓心,也就是法線的方向。這個矢量被分解到兩個正交方向上,就是Py和Px。

一樣,Vx也要如此分解爲兩個方向上的份量(Px2, Py2)。

Px+Px2, Py + Py2, 須要都從Vx和Vy裏減出去,加到圓A的Vx和Vy上。

以此類推,圓A的動量也要如此計算出要分給圓B的部分,並從本身的兩個速度與動量中減出,加到圓B的兩個動量裏去。

最後的效果,看起來和現實狀況比較符合。爲了測試,我作了一個下面的佈置,把5個球放一排,而後讓兩個球從左邊撞過去,看看碰撞的效果如何,結果是右邊也彈出了兩個球,與現實至關符合:

 

3.多個泡泡同時撞到一塊兒

兩兩相撞計算後,看起來也差很少了。沒作更多處理。

4.效率問題

因爲要兩兩計算,每一輪的計算量就是n*n次。一開始比較擔憂,但設置了30個泡,運行起來以後,發現大量的cpu時間是在繪圖部分的,900次的碰撞檢查幾乎不佔什麼CPU比例,因而就算了。

5.難以完全處理的問題

程序運行中,發現有一些偏差被積累,或是在不少球球時,它們之間的相互做用就變得很複雜,行爲有時有點怪異。仔細分析後,歸結爲兩大緣由:

1、       現實中,物體的運行狀態是依時間連續發生的,實時的。而在計算機裏,只能在離散的時間點上計算狀態,這就會錯過去不少事件,不得不加了一些糾正。這種事件錯失與糾正行爲會讓物體表現得不太天然。

2、       現實中,物體間的相互做用是並行發生的,但在計算裏,只能兩兩考慮,逐個處理,這也形成計算結果與現實環境不太一致。

這兩大緣由都是目前不太好徹底解決的,最多隻是精度修正,不可能徹底消除。

七,如何實現不妨礙工做的屏幕泡泡?

最後,拋出一個問題:在這個程序裏,背景部分是抓屏後繪出來的假屏幕。有沒有可能讓泡泡們在真正的屏幕上飄動,並且不影響當前的工做?

 

我想到把每一個泡泡都做爲一個真正的窗體來處理,以Top most方式運行,並設置window的剪裁區域爲本身的形狀,可是這樣的話,這些窗口泡泡就會載到在它們上面發生的windows消息,彷佛會影響下面的程序工做,好比點擊到它們時,就讓下面的窗口失去了焦點。

 

用系統鉤子如何呢?

 

下載源碼在這裏: http://files.cnblogs.com/haoxiaobo/PopScreenSaver.rar

相關文章
相關標籤/搜索