cocos2d-x屏幕適配原理分析

cocos2d-x作爲著名的cocos2d遊戲開發框架的C++實現者,最近一年發展迅猛。越來越多的app使用它實現快速多平臺部署,從最初的ios,android,win32等到新近的html5,實現移動,客戶端到瀏覽器全覆蓋,不得了~~

 


開發移動應用的屏幕適配和愛情一樣是一個從洪荒時代就存在的永恆命題,根本目的是實現在不同設備上用戶體驗的統一。
cocos2d-x在cocos2d-2.0-x-2.0.4版本之前沒有提供解決的方案,開發者只能自己解決,其中一些方法見 這裏

從2.0-x-2.0.4開始,勤勞的cocos2d-x團隊終於着手解決這個問題了~贊! 下面用一個實例分析一下其實現原理,官方說明文檔見 這裏 

==============================  大家好,我是正文開始之前華麗的分隔線   ===========================

新建一個帶helloworld例子的工程,跑起來是這樣的:

 

窗口尺寸爲480x320,背景火藍精靈的圖片原始大小就是480x320,正好鋪滿屏幕。

 


我們就從這裏開始變化。。。
首先,模擬分辨率爲960x640的屏幕,看看程序會是什麼樣子。
會修改win32下窗口尺寸麼?如果你是從2.0-x-2.0.4之前的版本直接跳過來的,比如我(源自1.0.1-x-0.11.0。。。“源自xxxx”形容酒很有檔次,對於編程麼。。還是要與時俱進,改進工作作風密切聯繫羣衆~),第一反應就是到AppDelegate.cpp裏去找設置win32窗口大小的代碼。
很遺憾,你找不到熟悉的"CC_TARGET_PLATFORM == CC_PLATFORM_WIN32",新版本把設置win32窗口尺寸移到了main.cpp裏面,
 
從代碼結構來講,這樣更合理,因爲AppDelegate.cpp是與平臺無關的程序邏輯入口,裏面不應該還包含大量不同平臺的適配代碼。
交給win32自己的main.app去設置,讓上帝的歸於上帝,凱撒的歸於凱撒!


接上述,把eglView->setFrameSize(480,320);  改爲eglView->setFrameSize(960, 640);  模擬分辨率爲960x640的屏幕,
ok,背景圖尺寸是480x320,在960x640屏幕下自然就填不滿,很合理~

好,需求來了:我們想要的結果是不論在什麼分辨率下,用戶都應該看到背景圖填滿了屏幕!

so,解決方案:
1,使用不同的資源適配不同的屏幕,可以通過判斷屏幕的分辨率來設置加載不同目錄的資源。
2,使用一套資源,在不同的分辨率下匹配屏幕進行縮放。

一個個來,先說方案一,既然要爲不同分辨率使用不同資源,那就再做一張圖案一樣的火藍精靈背景圖,尺寸爲960x640,
用於分辨率爲960x640的屏幕。在工程Resources目錄下新建兩個目錄:


"iphone"目錄存放480x320的背景圖和按鈕圖標,"iphone-retina"存放960x640的背景圖和按鈕圖標。
這兩個目錄名字隨意,再看到AppDelegate.cpp,  

再運行程序

 腫麼樣,框架就按照設置找到了960x640的背景圖,屏幕又恢復了不空虛不寂寞的狀態
這裏要注意:想獲取設備屏幕實際分辨率需要使用CCSize szFrame = pEGLView->getFrameSize();
而不是之前版本的CCDirector::sharedDirector()->getWinSizeInPixels(); 這個函數另有含義,後文會介紹。


but,面對全世界那麼多的移動設備,那麼多的分辨率,你hold住麼?就算你hold住,你的美術妹紙們hold住麼?
一種分辨率就出一套匹配的美術資源。。“希望你能陪我到地牢到天晃,希望你能陪我到海叫到天啞”。
就算真有以做地老天荒牌遊戲爲榮的團隊,出來的安裝包之大,也會讓用戶下載到地老到天荒~
so,這不是個好辦法。

那看看第二種方案,對資源進行縮放,還原Resources目錄的結構:


現在還是隻有一張480x320的背景圖,怎麼改呢?AppDelegate.cpp裏修改:

 

再次運行,amazing!屏幕再次不空虛不寂寞~


分析:
designResolutionSize是一個新的概念,它讓一切與座標,尺寸相關的數據徹底擺脫了屏幕分辨率的羈絆,或者說
由框架層來幫開發者完成轉換,開發者需要的只是設置designResolutionSize。告訴框架你在什麼樣尺寸的場景下
做的資源,比如此例,背景圖原始尺寸480x320,需求是剛好填滿屏幕,那麼就應該告訴框架“嗨,我設計時是以
480x320的屏幕爲標準的,你幫我轉轉”,框架就會回答你“放心吧!” 那麼框架究竟如何實現的呢? 跟蹤
pEGLView->setDesignResolutionSize(480, 320, kResolutionNoBorder);


可以發現,框架是獲取了實際分辨率和開發者designResolutionSize的比例,渲染的時候把圖片按照這個比例來縮放繪製。
拿本例來說,屏幕960x640,designResolutionSize爲480x320,縮放比例爲2,那麼原始大小480x320的背景圖,在繪製
時就會x2來繪製,也就是實際繪製成了960x640的大小,這樣就填滿窗口了!
請注意這裏對不同ResolutionPolicy的處理,原理後文會分析。

ok,現在面對480x320和960x640的屏幕,你已經能寫出自動適配的代碼了(其實只有一句,框架的好處啊~~)
又來了新的需求,“那誰誰,你改一下代碼,程序要能適應ipad2 ”,WTF!好吧,問候歸問候,問題總要解決。
於是你開始想“ ipad2分辨率1024x768,就按剛纔帥帥凡教我的到main.cpp裏修改”,好,修改爲1024x768,
ms填滿了,你很滿意,但是不對,右下角的按鈕腫麼快到屏幕外面去了?!仔細一看,不是填滿是填太滿,背景圖都超出屏幕了。
這是腫麼回事呢? 根源在於pEGLView->setDesignResolutionSize(480, 320, kResolutionNoBorder);第三個參數,找到定義:

講得很清楚了:
kResolutionExactFit:會靠拉伸來填滿屏幕,本例來說背景圖會變形來填充屏幕,因爲1024:768=1.3, 480:320=1.5,寬高比不同,圖片也就無法等比縮放來填滿屏幕,只能變形了。
kResolutionNoBorder: 看不到黑邊,實際就是寬高等比縮放,但縮放比例取寬比和高比之中大的那一個。
kResolutionShowAll:全部顯示,可以理解爲保證內容都顯示在屏幕之內,實際也是寬高等比縮放,但縮放比例取寬比和高比之中小的那一個。

一般來說,我們希望設計時一屏的內容,用戶在實際設備上也能在一屏內看到,拿本例來說,1024x768分辨率時,右下角的按鈕卻跑到屏幕外去了。看完上面的分析,你應該知道如何解決了: 對了,改變pEGLView->setDesignResolutionSize(480, 320, kResolutionNoBorder);第三個參數爲kResolutionShowAll。 ok,看看效果:


bingo!背景圖填滿了屏幕(水平方向),按鈕緊貼到右下角,可以滿足基本的風格統一要求。
不過縱向上出現黑邊,這個是實際分辨率的寬高比和設計分辨率寬高比不同造成的,無法通過框架層來解決。
只能交給開發者自己了,比如在代碼里根據分辨率計算會出現黑邊時,在黑邊填充相應的圖片等。一些解決方法見 這裏 

 

setPosition()的變化
之前版本可能已經習慣了CCNode::setPosition(const CCPoint &position);和CCNode::setPositionInPixel(const CCPoint &position);
但在新的版本里,只有setPosition(const CCPoint &position);
這裏傳入的參數不是像素,也和傳統的point有不同,它指的是在designResolutionSize參照下的座標。
驗證交給你自己來:比如設計分辨率爲480x320,設置一個sprite的位置爲240,160,在480x320分辨率下會發現它在屏幕正中,
模擬其他分辨率,960x640,1024x768,會發現它依然在屏幕中心,這就可瞭解240,160這個值跟實際屏幕分辨率已經無關了,
只和designResolutionSize有關,理解這一點至關重要,是後續開發正確空間感的基礎!


CC_CONTENT_SCALE_FACTOR()的變化
首先enableRetinaDisplay()在新版本里取消了,將不會有什麼retina設備上scalefactor爲2的說法了,
因爲designResolutionSize已經解決了按不同屏幕縮放的問題,所以CC_CONTENT_SCALE_FACTOR();始終返回1。
 
 
所以新版本里的定義已經過時了,無須理會。

當然,開發者可以通過CCDirector::sharedDirector()->setContentScaleFactor()來設置contentScaleFactor,這個係數可以理解爲
圖片原始尺寸和designResolutionSize的比值,這個比值將用來繪製圖片。
如果只是一套資源按照不同屏幕分辨率縮放,可以不用理會。


getWinSize()的變化

CCDirector::sharedDirector()->getWinSize();   獲取的是designResolutionSize
CCDirector::sharedDirector()->getWinSizeInPixels();  獲取的是getWinSize*contentScaleFactor之後的值,和老版本一樣。
如果contentScaleFactor爲1,則這兩個函數返回的值一樣。

 

使用kResolutionNoBorder策略時要注意的
CCSize szVisible = CCDirector::sharedDirector()->getVisibleSize();
CCPoint posVisible = CCDirector::sharedDirector()->getVisibleOrigin();
使用該策略時,因爲標準背景圖可能會超出屏幕,所以設置位置時需要已一個可視矩形爲基準。
可以這樣理解,szVisible就是你在實際設備上能看到的有效區域的寬高,posVisible就是這個有效區域的起始座標,和szVisible構成一個可視矩形,一般來說這個可視矩形是設計分辨率下可視矩形的子集。

 

總結一下,cocos2d-x新的版本(對於從1.0.1-x-0.11.0上來的人)的確添加了不少新的功能,代碼結構也更加合理,這些都是開發者之福,希望2dxteam繼續加油!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章