cocos2d-x是一款眾所周知的跨平台的游戲開發引擎。由於其跨平台的特性,多分辨率支持也自然就有其需求。
因此,在某一次更新中(抱歉,筆者已經忘了是哪次更新了),cocos2d-x添加了一個新的方法,可以非常簡便地讓cocos2d根據屏幕尺寸的大小做自適配。
方法名:
virtual void setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy);
該方法所傳的參數中,前兩個參數width、height,指的是開發者在設計界面時默認設計的尺寸。例如:開發者在設計界面時以iphone5為准,則width和height就是568*320;若是以iphone4為准,則width和height就是480*320;以此類推。
第三個參數resolutionPolicy指的是cocos2d在縮放時需要遵守的規則。ResolutionPolicy這個自定義結構如下:
enum ResolutionPolicy { // The entire application is visible in the specified area without trying to preserve the original aspect ratio. // Distortion can occur, and the application may appear stretched or compressed. kResolutionExactFit, // The entire application fills the specified area, without distortion but possibly with some cropping, // while maintaining the original aspect ratio of the application. kResolutionNoBorder, // The entire application is visible in the specified area without distortion while maintaining the original // aspect ratio of the application. Borders can appear on two sides of the application. kResolutionShowAll, kResolutionUnKnown, };
通過注釋可以看出:
kResolutionExactFit是指cocos2d縮放的時候不考慮畫面失真,直接用畫面填充滿整個屏幕
kResolutionNoBorder是指cocos2d縮放的時候盡量頂滿畫面,如果因此有超出畫面的部分,就會把超出的部分切除掉(舉例來說:如果設計大小是568*320,在480*320大小的屏幕上自適配的時候,就會把寬度超出的部分切除)
kResolutionShowAll是指cocos2d縮放的時候顯示所有的畫面,如果有填不滿屏幕的地方就會顯示黑條。
這三種規則各有優缺點,具體使用哪種規則需要考量項目的需要進行合理的選擇。
然而,需要特別注意的一點是:setDesignResolutionSize和enableRetina這兩個方法是不可以同時使用的。這即意味着使用自適配的時候是不可以做高清版的。
如果查看cocos2d-x源代碼,可以看到在setDesignResolutionSize方法中有一句代碼:
CCAssert(m_bIsRetinaEnabled == false, "can not enable retina while set design resolution size!");
在下認為這是因為高清模式下圖片資源都是進行了縮小的,所有的坐標都是定義的point,而不是pixel。因此高清模式和自適配可能有某些沖突存在。(以上只是筆者的猜測,不靠譜勿怪)
總之,如果想要使用自適配,就必須在項目一開始就放棄高清。如果要做高清版,目前看來就只能自己根據屏幕尺寸的比例變化設置setContentScaleFactor,這一部分的設置方法這里就不展開了。
另外,筆者在剛開始使用自適配功能時,誤以為開發時要把UI元素的位置坐標和大小都寫成相對於屏幕大小的一定比例。例如:
CCSprite* avatarSprite = CCSprite::create("avatar.png"); avatarSprite->setPosition(ccp(winSize.width * 0.000878, winSize.height * 0.653125)); addChild(avatarSprite);
但實際上這是沒有必要的。自適配功能是直接調整整個CCDirector的畫布大小,因此任何添加到CCDirector中的元素都會被縮放相應的比例,沒有必要再特意將坐標寫成相對屏幕比例的模式。
下面筆者將分享一下在使用自適配功能的時候遇到的一個問題,即CCListView在多分辨率模式下可能出現的被過多地切除一部分內容的問題。
首先先介紹下筆者發現問題時的背景:當時筆者是在實現一款跨平台的游戲的其中一個界面,該游戲的畫面是按照iphone5的低清版設計,DesignResolutionSize設置的是568*320。在安裝到iphone5上時一切正常,但是隨后,在安裝到iphone4機器上進行調試時,發現畫面上所有的CCListView都被切除了左邊的一部分內容。
由於在iphone5上一切正常,在iphone4上畫面被切除了一部分,因此筆者立即懷疑是CCListView與setDesignResolutionSize之間有某些沖突。
通過debug和log,最后將問題鎖定到了CCListView類的visit方法。方法的源代碼如下:
void CCListView::visit(void) { if (!m_pListViewParent) { CCRect rectSelf; float factor = CC_CONTENT_SCALE_FACTOR(); rectSelf.origin = convertToWorldSpace(CCPoint(0,0)); rectSelf.origin.x *= factor; rectSelf.origin.y *= factor; rectSelf.size = this->getContentSize(); rectSelf.size.width *= factor; rectSelf.size.height *= factor; glScissor((GLsizei)rectSelf.origin.x, (GLsizei)rectSelf.origin.y, (GLsizei)rectSelf.size.width , (GLsizei)rectSelf.size.height); glEnable(GL_SCISSOR_TEST); } CCLayerColor::visit(); if (!m_pListViewParent) { glDisable(GL_SCISSOR_TEST); } }
這段代碼中調用了一個方法:glScissor。這個方法是框定了CCListView將會顯示在屏幕上的范圍。而由於在使用自適配模式,對CC_CONTENT_SCALE_FACTOR沒有做任何修改,因此這個框出來的范圍是對應於DesignResolutionSize設定的原始設計畫面大小的,而在屏幕尺寸發生了變化,整個CCDirector被縮放的情況下,這個rectSelf卻沒有做任何改變,所以就導致了上述的被切除一部分的問題。
隨后,筆者通過搜索,發現已經有人遇到了同一個問題。而且還提供了修復的方法:http://www.cocos2d-x.org/boards/18/topics/14464
簡單講就是,用CCEGLView::sharedOpenGLView()->setScissorInPoints方法替換glScissor方法。其它任何地方都不用變。
修改好以后在iphone4和5上調試都沒問題,因此這個bug本身可以算是修復了。
PS:由於對這個方法能夠奏效感到好奇,筆者查看了setScissorInPoints方法的源文件,代碼如下:
void CCEGLViewProtocol::setScissorInPoints(float x , float y , float w , float h) { glScissor((GLint)(x * m_fScaleX + m_obViewPortRect.origin.x), (GLint)(y * m_fScaleY + m_obViewPortRect.origin.y), (GLsizei)(w * m_fScaleX), (GLsizei)(h * m_fScaleY)); }
可以看出這個方法內部仍然是調用了glScissor方法,但是傳遞的參數則考慮到了屏幕尺寸的變化,因此可以保證通過這個方法不會切除過多的CCListView的內容。
另外,在工程中搜索glScissor時發現CCScrollView也調用了這個方法,因此如果使用了CCScrollView推薦也替換掉源代碼中glScissor方法。