- 精通Cocos2d-x游戲開發(進階卷)
- 王永寶
- 2873字
- 2020-11-28 22:37:01
5.1 Cocos2d-x適配策略
可以在Cocos2d-x中調用CCEGLView的setDesignResolutionSize方法設置游戲的分辨率策略,以及我們的設計分辨率。
setDesignResolutionSize()方法包含3個參數,分別是設計分辨率的寬和高,以及分辨率的適配策略。下面這行代碼設置了960×640的設計分辨率,并使用了SHOW_ALL分辨率適配策略。
Director::getInstance()->getOpenGLView()->setDesignResolutionSize(960, 640, ResolutionPolicy::SHOW_ALL);
5.1.1 分辨率適配策略
Cocos2d-x的分辨率適配一般不是為每一種分辨率設計一種布局方案,而是在一種分辨率下進行設計(也就是設計分辨率),然后通過分辨率適配策略,讓程序能夠適應不同的分辨率。Cocos2d-x提供以下5種分辨率適配策略。
? EXACT_FIT以設置的分辨率為標準,按照該分辨率對x和y進行拉伸。
? NO_BORDER不留黑邊,不拉伸,等比縮放,有一個方向(上下或左右)可能超出屏幕。
? SHOW_ALL設置的分辨率區域內全部可見,但上下左右都可能出現黑邊。
? FIXED_HEIGHT鎖定分辨率的高度,寬度不管,可能出現黑邊也可能超出屏幕。
? FIXED_WIDTH鎖定分辨率的寬度,高度不管,可能出現黑邊也可能超出屏幕。
通過圖5-1~圖5-3可以直觀地了解到在不同分辨率下,各個分辨率適配策略的表現。以960×640為設計分辨率,然后通過調整窗口的實際分辨率,選擇不同的適配模式進行觀察。在PC上調用Director的setFrameSize()方法可以自定義窗口的尺寸,但不要在移動設備上設置FrameSize。

圖5-1 EXACT_FIT模式

圖5-2 NO_BORDER模式

圖5-3 SHOW_ALL模式
首先是EXACT_FIT模式,當在不同的分辨率下運行時,界面的寬和高都會根據我們的設計分辨率進行縮放,例如,當設計分辨率是100×200,在200×300的分辨率下運行時,寬會放大2.0,高會放大1.5,當實際分辨率小于設計分辨率時,Cocos2d-x又會相應地縮小界面使其適配,如圖5-1所示。
NO_BORDER模式下會根據實際分辨率進行等比縮放,不留黑邊。首先按照EXACT_FIT模式的縮放規則計算出寬和高的縮放值,按照最高的縮放值進行等比縮放。當實際分辨率無法完整放下縮放后的界面時,會有一部分內容顯示在屏幕外,如圖5-1所示,當界面以NO_BORDER模式進行適配時,紅色邊框為界面的完整內容,紅色邊框左下角的紅色原點為OpenGL窗口的原點坐標,如圖5-2所示。
SHOW_ALL模式下會根據實際分辨率進行等比縮放,完全顯示界面的完整內容,與NO_BORDER模式相反,其會先按照EXACT_FIT模式的縮放規則計算出寬和高的縮放值,按照最低的縮放值進行等比縮放。由于是按照最小的分辨率進行縮放,所以左右和上下都有可能出現黑邊,圖5-3右側圖片中的紅點處為OpenGL窗口的原點坐標,如圖5-3所示。
FIXED_HEIGHT和FIXED_WIDTH模式比較類似,它們會將高度或寬度鎖定,按照高度或寬度進行等比縮放,另外一個方向既可能超出,也有可能留下黑邊。這兩種模式會先按照EXACT_FIT模式的縮放規則計算出寬和高的縮放值,FIXED_HEIGHT取高度縮放值進行等比縮放,保證設計分辨率的高度剛好鋪滿設計分辨率,FIXED_WIDTH取寬度進行等比縮放,保證設計分辨率的寬度剛好鋪滿設計分辨率。
5.1.2 坐標編碼
當我們的程序在不同的分辨率下運行時,setDesignResolutionSize()方法會對整個程序按照適配策略根據設計分辨率和實際分辨率進行縮放。在對坐標進行編碼時,需要使用相對坐標編碼,而根據窗口尺寸可以進行相對坐標的編碼,如希望將一個節點放置在屏幕的正中間,就需要將其坐標的x和y分別設置為窗口尺寸的寬和高的1/2。相對左下角原點的坐標則可以直接使用絕對坐標,設置相對位置可以使得程序在不同的分辨率下運行,我們的對象都能夠顯示在正確的位置上。
在使用相對坐標編碼時,Director單例中有幾個方法可以獲取尺寸,下面了解一下這幾個獲取尺寸相關的方法。
? getWinSize,獲取OpenGL窗口的單位尺寸。
? getWinSizeInPixels,獲取OpenGL窗口的實際像素尺寸。
? getVisibleSize,獲取可視窗口的尺寸。
? getVisibleOrigin,獲取可視窗口左下角坐標的位置。
另外GLView對象還提供了以下兩個接口來獲取其他的尺寸。
? getFrameSize,獲取設備或窗口的尺寸。
? getDesignResolutionSize,獲取設置的設計分辨率。
如圖5-4直觀地演示了上面描述的各種尺寸,WinSize和WinSizeInPixels分別是當前整個OpenGL窗口的單位尺寸和像素尺寸。VisibleSize和VisibleOrigin可以共同構成當前窗口中實際可見部分內容的矩形范圍,FrameSize為當前窗口或設備的真實尺寸。

圖5-4 WinSize與VisibleSize
? WinSize分別為圖5-4中左右兩圖的紅色框范圍,雖然看上去范圍不同,但這是一個單位尺寸,所以值并沒有變化,也就是原圖尺寸960×640,一般等同于設計分辨率的尺寸,也是OpenGL窗口的單位尺寸。
? WinSizeInPixels也對應圖5-4兩圖中的紅色框范圍,但這個尺寸為實際占用的像素尺寸,所以在不同分辨率下有不同的值(程序邏輯中使用的坐標是單位尺寸,而非像素尺寸)。
? VisibleSize表示可視內容的尺寸,在圖5-4左圖中為紅色框范圍,右圖則為黃色框范圍,也就是可以看到的有內容的顯示區域尺寸。
? VisibleOrigin表示可視內容的左下角坐標,分別是左右圖中左下角的紅點的位置,左圖中OpenGL窗口原點的坐標與紅點重疊,而右圖中OpenGL窗口的原點為紅色框的左下角,VisibleOrigin的Y軸比原點高了64個像素。
? FrameSize為窗口或設備的實際尺寸,也就是圖5-4中兩個窗口的窗口大小1200× 640。
Cocos2d-x推薦使用VisibleSize和VisibleOrigin進行相對位置的計算,就是因為根據它們來計算可以保證我們的對象能夠處于可視范圍中。
WinSize和(0,0)坐標構成了OpenGL窗口,VisibleSize和VisibleOrigin構成了可視窗口,可視窗口不會大于OpenGL窗口,因為OpenGL窗口以外的內容都是不可見的!但OpenGL窗口范圍內的對象并不一定可見,如當屏幕窗口容不下OpenGL窗口時。可視窗口可以理解為OpenGL窗口和設備實際分辨率窗口相交的矩形區域。
5.1.3 OpenGL窗口與可視化窗口
絕大部分的游戲都可以使用FIXED_HEIGHT或FIXED_WIDTH模式來實現簡單的分辨率適配,只需要在背景上將可能有黑邊的內容進行填充即可。這兩種模式與SHOW_ALL有些類似,就是都可能導致黑邊或超出,但有一種本質區別,即它們的OpenGL窗口不同,這對于坐標編碼是有巨大影響的!OpenGL窗口不同,說的是原點位置不同,WinSize、VisibleSize不同。
在圖5-5中,使用FIXED_HEIGHT和SHOW_ALL模式都是同樣的表現,左右都會有同樣的黑邊,但FIXED_HEIGHT模式下的OpenGL窗口和可視化窗口對應的是圖5-5中的黃色矩形區域(包括左右的黑邊),而SHOW_ALL模式下的OpenGL窗口和可視化窗口對應的是圖5-5中的紅色矩形區域(不包括黑邊)。

圖5-5 OpenGL窗口和可視化窗口
最直觀的表現就是,在(0,0)的位置創建一個對象,FIXED_HEIGHT模式下會出現在黃色矩形區域的左下角,而SHOW_ALL模式下會出現在紅色矩形區域的左下角。SHOW_ALL模式下的黑邊部分是不會出現任何顯示對象的,因為不在OpenGL窗口中。而FIXED_HEIGHT模式則可以正常顯示,所以只要背景圖片大一些,將左右的黑邊區域遮住,即可簡單地解決適配黑邊的問題。正是由于這種實現方式,FIXED_HEIGHT和FIXED_WIDTH模式才可以在背景上對可能有黑邊的內容進行填充來解決黑邊的問題。
5.1.4 setDesignResolutionSize詳解
在了解了適配策略和Cocos2d-x的各種尺寸之后,下面來進一步了解setDesign-ResolutionSize()方法,setDesignResolutionSize()方法中會簡單判斷傳入的設計分辨率的寬度和高度,以及分辨率適配策略,將這些參數保存并調用updateDesignResolutionSize()方法更新分辨率。
void GLView::setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy) { CCASSERT(resolutionPolicy ! = ResolutionPolicy::UNKNOWN, "should set resolutionPolicy"); if (width == 0.0f || height == 0.0f) { return; } _designResolutionSize.setSize(width, height); _resolutionPolicy = resolutionPolicy; updateDesignResolutionSize(); }
在updateDesignResolutionSize()方法中,首先根據屏幕尺寸和設計分辨率計算出x和y方向的縮放值,然后根據分辨率適配模式選擇最終的縮放值,計算完縮放值之后,再計算視口的大小。
void GLView::updateDesignResolutionSize() { if (_screenSize.width > 0 && _screenSize.height > 0 && _designResolutionSize.width > 0 && _designResolutionSize.height > 0) { _scaleX = (float)_screenSize.width / _designResolutionSize.width; _scaleY =(float)_screenSize.height/ _designResolutionSize.height; //NO_BORDER模式下取最大的縮放值等比縮放 if (_resolutionPolicy == ResolutionPolicy::NO_BORDER) { _scaleX = _scaleY = MAX(_scaleX, _scaleY); } //SHOW_ALL模式下取最小的縮放值等比縮放 else if (_resolutionPolicy == ResolutionPolicy::SHOW_ALL) { _scaleX = _scaleY = MIN(_scaleX, _scaleY); } //FIXED_HEIGHT模式下取y軸縮放值等比縮放,并將設計分辨率的寬度調整為全屏的 寬度 else if ( _resolutionPolicy == ResolutionPolicy::FIXED_HEIGHT) { _scaleX = _scaleY; _designResolutionSize.width = ceilf(_screenSize.width/_scaleX); } //FIXED_WIDTH模式下取x軸縮放值等比縮放,并將設計分辨率的高度調整為全屏的 高度 else if ( _resolutionPolicy == ResolutionPolicy::FIXED_WIDTH) { _scaleY = _scaleX; _designResolutionSize.height = ceilf(_screenSize.height/_ scaleY); } //計算視口的尺寸,并設置視口的矩形區域 float viewPortW = _designResolutionSize.width * _scaleX; float viewPortH = _designResolutionSize.height * _scaleY; _viewPortRect.setRect((_screenSize.width - viewPortW) / 2, (_screenSize.height - viewPortH) / 2, viewPortW, viewPortH); //重置Director的成員變量來適應可視化矩形 auto director = Director::getInstance(); director->_winSizeInPoints = getDesignResolutionSize(); director->_isStatusLabelUpdated = true; director->setGLDefaultValues(); } }