PyGame中的髒矩形優化技術

2021-10-02 11:46:11 字數 3382 閱讀 1542

目錄問題

髒矩形優化 (dirty rectangle) 原理

一、獲得螢幕需要重繪的區域

二、在上一幀繪製的區域用對應區域的背景覆蓋

三、在新的位置上繪製兔子

四、**實現

半成品遊戲中的實踐

進一步優化

一些提示

這幾天在嘗試用pygame寫乙個小遊戲。當我在給遊戲加上乙個背景,遊戲最大幀率一下子降低了近一半!原因是我一直採用全屏重繪的方式來更新螢幕影象。這是因為不同於只佔螢幕的一小部分其他一些精靈影象,背景是鋪滿整個螢幕的,每次繪製這麼大乙個影象,自然要增加很大一部分繪製時間。

解決方案之一是使用髒矩形優化技術。(不要吐槽這個名字,外國人的思維方式和咱們不太一樣)

螢幕影象每一幀和上一幀通常不是完全不同的。一般我們會在遊戲中讓乙個遊戲人物移動、讓乙個炮彈橫飛,但是背景基本是每一幀都不會改變的,這樣的話我們在每一幀裡只需要重繪同上一幀相比發生改變的地方就行了,而不是把沒有改變的地方也重新畫上與上一幀一模一樣的東西。只重繪需要重繪的螢幕區域,會讓螢幕繪製時間大大縮短。

需要重繪哪些區域取決於我們向螢幕緩衝區中的哪些區域更新了影象。

幸好,pygame為我們提供了這樣的介面:

pygame.sprite.renderupdates這是乙個pygame.sprite.group的擴充套件物件,它的draw()方法能返回組內精靈影象的更新區域,那是乙個由rect物件組成的list列表,剩下的功能和pygame.sprite.group無二。

sprite_group = pygame.sprite.renderupdates()

sprite_group.add(sprite_1)

sprite_group.add(sprite_2)

while true:

# other code

# screen 為pygame.display.set_mode()返回的螢幕緩衝區物件

# 繪製精靈組內精靈 並 返回本次更新區域的列表,列表由rect物件組成

dirty_rects = sprite_group.draw(screen)

# 向update方法傳入需要重繪區域的列表,即可重繪指定區域

# 如果不傳入引數,則預設重繪整個螢幕

pygame.display.update(dirty_rects)

# other code

當然你也可以不使用pygame提供給你的這個精靈管理類來獲取需要重繪的區域,我們也可以使用自己寫的類,秩序在更新螢幕緩衝區的時候記錄下所有更新區域就好,這麼做有時候反而更方便靈活。

像上面**那樣獲取了dirty_rects直接就把這個列表傳入pygame.display.update()重繪這些區域是錯誤的做法,因為那樣會導致下面第二張圖這樣的情景:

圖中我在每次需要重繪的區域畫上了紅色框框。可以看到,上圖中遊戲一共更新了7幀,精靈組(sprite_group)中有乙個兔子外觀的精靈(sprite)。我讓兔子精靈往右移動,每一幀會在新的地方繪製兔子的影象,並且螢幕上相應的地方會重繪,但是舊的地方(兔子上一幀的位置)我們並沒有更新!在新的一幀裡,那些上一幀存在兔子影象而與新一幀兔子影象沒有交集的地方應該被繪製上背景的內容,而不是像我們現在這樣還依舊留存著上一幀剩下的內容。

然後我們就可以在新的區域上畫上兔子了,注意要儲存這個新區域,因為下一幀的時候我們要利用本幀這個區域來繪製背景影象覆蓋掉本幀的兔子

偽**如下:

screen_buffer =

rect_old =

whlie true:

rect_new =

screen_buffer.blit(area=rect_new , img=rabbit_image )    # 在緩衝區新位置上繪製這個兔子

display.update(rect_old & rect_new)      # 重繪 rect_old 和 rect_new 這兩個區域

rect_old = rect_new    # 現在的 rect_new 就要成為下一幀的 rect_old 了

這是乙個兔子接蘿蔔的遊戲。遊戲中暫時有兔子、蘿蔔這些精靈,由乙個精靈組物件管理;還有動畫特效管理器物件,負責一些動畫特效的繪製;還有房間物件,負責房間背景圖和一些背景粒子效果(圖中為簡陋的雪花)。

下面這是加上重繪提示框框的效果,可以看到每一幀實際重繪的區域。

下面是按照上面偽**思路寫的遊戲**,這是主迴圈中的一部分**,即向緩衝區繪製並彙總所有由於可繪圖物件(精靈組、動畫管理器、房間)繪製造成的「髒區域」,然後重繪這些「髒區域」。

由下圖可以看到,一些重繪區域重疊甚至小區域被大區域完全包裹了起來,有些已經重繪的地方再次重繪是不必要的,比如圖中所有被黃圈圈划起來的地方(蘿蔔、雪花、蘿蔔筐上那個龍動畫特效),這些地方只需要按照外層最大的矩形區域重繪一次就行了,而不是像現在這樣那些黃圈圈的地方再次重繪一次。

我們需要把那些黃圈圈的區域從dirty_rects(髒矩形彙總列表)中識別出來並刪去, 我們需要依次檢測dirty_rects中每個rect元素看是不是這個rect代表的區域被其他rect區域包含。

rect物件有乙個colliderect()方法,可以檢測乙個rect矩形是不是被另乙個rect矩形完全包含。

如果兩個rect矩形大部分區域相交,我們也可以考慮將這兩個矩形合併成乙個大矩形。rect物件也有對應的方法來檢測矩形相交。

髒矩形合併的核心規則如下

pygame.display.update() 和 pygame.display.flip()

前者可以傳入髒矩形列表,後者不可以;後者可以使用opengl等技術,可以使用硬體加速。

pygame中繪製弧線的方法

繪製圓形方法 pygame.draw.arc su ce,color,rect,start angle,stop angle,width pygame 官網介紹說明方法 pygame.draw.circle arc方法介紹 surfuce引數 傳入需要在該su ce物件上繪製圓形的su ce物件 c...

pygame中繪製線條的方法

繪製線條方法 pygame.draw.arc su ce,color,rect,start angle,stop angle,width pygame 官網介紹說明方法 lines su ce,color,closed,pointlist,width 1 rect lines方法介紹 surfuce...

rails中髒資料的使用

在啟用了髒資料 dirty objects 後,有時候一些不通過writer的修改是不會存到資料庫裡的。比如以下 self.org common flag common flags agent flag self.agent flag 0 1 實際上已經修改了字串中某個字元了,但是因為不通過writ...