一、前提:
完成前一篇的內容。
具體參考:Cocos2d-x3.x塔防游戲(保衛蘿卜)從零開始(一)篇
二、本篇目標:
l 說說關於cocos2dx手機分辨率適配
l 對前一篇完成的塔防游戲原型進行屏幕適配完善
三、內容:
l 說說關於cocos2dx手機分辨率適配
在上一篇的結尾我們遺留了一個問題,在真機上運行時女主角、色狼的位置相當於道路都有點偏上了,並且好像背景地圖也沒有顯示全背景的頂部和底部有一部分沒有顯示出來,但是在windows下運行確正常,這個是什么原因呢,該怎么調整呢?我的手機分辨率是:960x540 而我們的地圖素材圖片分辨率是:960x640,兩個尺寸的不同導致了這個問題,這個是關於不同手機屏幕分辨率適配問題,Android手機品種繁多屏幕尺寸分辨率也同樣繁多,為了讓游戲適應不同的Android手機需要多做很多的工作,這個做IOS的游戲要幸福多畢竟就這么幾個尺寸分辨率。
影響游戲的兩個因素屏幕大小(分辨率)、寬高比,屏幕大小從小屏的480×320手機到大屏甚至平板的2048×1536,如果用低分辨率的素材圖在高分辨率的設備上就會圖像模糊,如果用高分辨率的素材在低分辨率設備上時增加系統負擔,所有一般我們采取多套不同分辨率素材進行匹配,這個問題還算容易解決。但是寬高比就麻煩的多,手機有3:2,16:9 標准寬屏等,本篇中我測試用的華為手機為16:9的寬屏,寬高比會造成游戲不按比例的壓縮或者拉伸造成游戲上的元素顯示、位置等發生變異甚至導致游戲無法使用,所以寬高比造成的問題比屏幕大小要嚴重的多,比如我們的這個塔防游戲就因為這個問題造成了游戲人物位置偏移。那寬高比是否也可以像分辨率一樣采用多套寬高比的素材解決?肯定可以!但是如果不同的寬高比結合不同的分辨率這樣得提供多少套的素材啊?並且新的寬高比的手機在不斷的新出來為了每出一款手機就做一套素材這樣的成本太大。
在cocos2d-x-3.3/tests目錄下有一個名為cpp-empty-test示例工程,用Microsoft Visual Studio 2012打開proj.win32下的工程,可以看到它對於這個問題的解決方案,參考其中的AppMacros.h、AppDelegate.cpp、Resources目錄下的素材。
通過這個示例項目可以如下總結:
1、屏幕大小(分辨率)解決方案
按照上面的思路提供多套的素材,一般游戲做法提供低、中、高、超高四套不同的分辨率的素材,低的應付一般小屏手機、中的應付高分辨率手機、高的應付平板、超高的應付高清平板或者電視之類的設備。四套素材分4個文件夾放置到項目Resources文件下比如叫iphone、iphonehd、ipad、ipadhd。在設備載入游戲時,判斷當前設備的分辨率然后選擇不同文件夾下的素材進行載入以適應不同分辨率的設備。
2、寬高比解決方案
為了適應設備各種屏幕寬高比,在 Cocos2dx中,提供了相應的解決方案,以方便我們在設計游戲時,能夠更好的適應不同的屏幕。Cocos2dx提供了ResolutionPolicy(分辨率策略),通過給GLView設置不同ResolutionPolicy來解決這個問題。
ResolutionPolicy的五種類型:
1、 EXACT_FIT
2、 NO_BORDER
3、 SHOW_ALL
4、 FIXED_HEIGHT
5、 FIXED_WIDTH
l 對前一篇完成的塔防游戲原型進行屏幕適配完善
1、屏幕大小(分辨率)適配
第一步:
按照上面解決方案的做法,先制作iphone、iphonehd兩套素材,然后拷貝到項目的Resources文件下,我們這款游戲的目標是手機類的設備,所以就只提供了兩套分辨率的素材,如果需要對更高分辨率的設備進行支持那么需要提供更多套的素材。個人建議如果你的游戲需要支持平板,那么建議單獨出個HD版,雖然通過代碼和素材的適配能同時支持手機和平板設備,但是這樣的實現還是有一定的限制,會一定程度上降低某類設備的可玩性。
第二步:
新建AppMacros.h文件,把cpp-empty-test示例工程下同名的文件代碼直接拷貝過來進行修改,只需要保留兩種不同的分辨率代碼即可:
#define DESIGN_RESOLUTION_480X320 0 #define DESIGN_RESOLUTION_960X640 1 /* If you want to switch design resolution, change next line */ #define TARGET_DESIGN_RESOLUTION_SIZE DESIGN_RESOLUTION_960X640 typedef struct tagResource { cocos2d::Size size; char directory[100]; }Resource; static Resource smallResource = { cocos2d::Size(480, 320), "iphone" }; static Resource mediumResource = { cocos2d::Size(960, 640), "iphonehd" }; #if (TARGET_DESIGN_RESOLUTION_SIZE == DESIGN_RESOLUTION_480X320) static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320); #elif (TARGET_DESIGN_RESOLUTION_SIZE == DESIGN_RESOLUTION_960X640) static cocos2d::Size designResolutionSize = cocos2d::Size(960, 640); #else #error unknown target design resolution! #endif // The font size 24 is designed for small resolution, so we should change it to fit for current design resolution #define TITLE_FONT_SIZE (cocos2d::Director::getInstance()->getOpenGLView()->getDesignResolutionSize().width / smallResource.size.width * 24)
第三步:
打開AppDelegate.cpp文件,添加對AppMacros.h的引用,然后applicationDidFinishLaunching方法里添加對當前設備屏幕分辨率進行判斷然后設置不同的圖片素材:
#include <vector> #include <string> #include "AppMacros.h" …… //獲取當前設備屏幕尺寸 Size frameSize = glview->getFrameSize(); vector<string> searchPath; //如果屏幕尺寸寬>smallResource素材尺寸寬 if (frameSize.width > smallResource.size.width) { //使用mediumResource目錄下的素材 searchPath.push_back(mediumResource.directory); float scale=mediumResource.size.width/designResolutionSize.width; director->setContentScaleFactor(scale); } else { //使用smallResource目錄下的素材 searchPath.push_back(smallResource.directory); float scale=smallResource.size.width/designResolutionSize.width; director->setContentScaleFactor(scale); } // 設置素材路徑目錄 FileUtils::getInstance()->setSearchPaths(searchPath); ……
通過這段代碼,我們解決了低分辨率手機和高分辨率手機的圖片素材適配問題。如何測試效果呢?分別找個低分辨率的的手機和高分辨率的手機?這樣太麻煩了,其實只需要一行代碼就可以直接在調試時模擬不同分辨率的手機效果,在applicationDidFinishLaunching方法中添加一行glview->setFrameSize(960,440);代碼即可實現,當游戲開發完成時刪除本行代碼。
if(!glview) { glview = GLViewImpl::create("DefendTheGirl"); //設置模擬器分辨率大小 glview->setFrameSize(960,440); director->setOpenGLView(glview); }
2、寬高比適配
第一步:
按照上面解決方案的做法,在AppDelegate.cpp的applicationDidFinishLaunching方法中,添加如下代碼:
/設置游戲的設計尺寸以及分辨率策略
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height,ResolutionPolicy::FIXED_HEIGHT);
ResolutionPolicy有五種類型,我們先分別對這5種類型進行測試,看看有什么區別,並且決定我們這個游戲應該采用哪種類型效果最佳,為了能比較明顯看出區別我們通過設置模擬器不同分辨率進行效果測試。
EXACT_FIT: 會拉伸素材進行顯示,充滿整個屏幕,最簡單最粗暴,但是可能會出現圖像變形。
glview->setFrameSize(960,440);
結論:看游戲截圖效果確實像描述的一樣出現了圖像變形,無論手機屏幕是何種寬高比的,里面是素材一律按照填滿手機屏幕的寬高比對素材進行拉伸變形,我想這個方法肯定不可取。
NO_BORDER:短邊占滿屏幕,另外一側超出屏幕,一部分畫面在屏幕外,無法顯示。
glview->setFrameSize(960,440);
對應的分析圖:
glview->setFrameSize(460,640);
對應的分析圖:
結論:我們分別用960,440和460,640兩種手機尺寸進行測試。通過分析圖可以知道第一種尺寸的時候是素材寬(960px)占滿了整個手機屏幕,而素材高(640px)只顯示了中間的440px,素材的上部分被手機屏幕遮住了100px下部分被遮住了100px;第二種尺寸的時候是素材高(640px)占滿了整個手機屏幕,而素材的寬(960px)只顯示了中間的460px, 素材的左邊部分被手機屏幕遮住了250px右邊部分被遮住了250px。對於NO_BORDER解釋:短邊占滿屏幕,另外一側超出屏幕,一部分畫面在屏幕外,無法顯示。這里說的短邊並不是指素材本身寬和高中短的那邊,而是素材寬和手機寬進行比較,素材高和手機高進行比較,那個差值小就算是短邊,比如第一種尺寸的時候,寬差值=|素材的寬960px-手機屏幕寬960px|=0,高差值=|素材的高640px-手機屏幕寬440px|=200,所以短邊是素材的寬。我想這個方法寬高適應和設備的寬高比有關,也就是哪個是短邊不一定由設備決定。
SHOW_ALL:保持原比例,讓一邊占滿屏幕,另外一側黑邊。
glview->setFrameSize(960,540);
結論:這個方式好像看起來相對合理點,畫面肯定保持原來素材的比例不會拉伸變形等,但是比較遺憾的是手機的左右兩邊或者上下兩邊會出現黑色的空白區除非素材的寬高比恰好和手機的寬高比相同。我想這個方式對我們這個游戲來說也可選,但是有黑邊不是最佳的用戶體驗,但是用這個方式最簡單可以在任何設備中保持游戲真實的畫面。
FIXED_HEIGHT:NO_BORDER類似,但是指定高占滿屏幕,寬部分超出屏幕外,無法顯示。
glview->setFrameSize(960,440);
對應的分析圖:
glview->setFrameSize(460,640);
對應的分析圖:
結論:這個方式好像和NO_BORDER有點像,上面兩種尺寸屏幕測試結果均表現為素材高占滿手機屏幕的高,而寬要不超出屏幕要不小於屏幕,素材仍舊維持本身的寬高比,同時游戲背景上的人物位置有一定的偏移,這個方式對我們這個游戲來說也不是很合適。
FIXED_WIDTH:NO_BORDER類似,但是指定寬占滿屏幕,高部分超出屏幕外,無法顯示。
glview->setFrameSize(960,440);
對應的分析圖:
glview->setFrameSize(460,640);
對應的分析圖:
結論:這個方式好像和NO_BORDER有點像,上面兩種尺寸屏幕測試結果均表現為素材寬占滿手機屏幕的寬,而高要不超出屏幕要不小於屏幕,素材仍舊維持本身的寬高比,同時游戲背景上的人物位置有一定的偏移。
我們游戲的選擇:
5種類型的測試結果和分析結果均已經完成了,現在要為我們的這個塔防游戲找一個最合適的類型,EXACT_FIT這個肯定不可取直接否定,SHOW_ALL這個倒是個最簡單的方案雖然有點小缺陷,但是這個最簡單效果也能接受,並且很多知名游戲前期的一些版本均是這個模式,但是個人覺得我們的游戲得有點高追求,所以准備 NO_BORDER、FIXED_HEIGHT、FIXED_WIDTH這3個中選擇一個,雖然會增加編碼和素材設計的難度,但是通過合理素材設計和代碼配合能完全實現全屏並且游戲比例不扭曲的效果,這3個其實差不多屬於一個類型,NO_BORDER其實就包括了FIXED_HEIGHT、FIXED_WIDTH兩種類型,只不過具體表現為哪種類型由實際設備的寬高比確定,也就是帶有一定的不確定性,而FIXED_HEIGHT、FIXED_WIDTH就是由開發者直接指定要高適應要么寬適應,這樣至少確定了一樣不確定因素可以一定程度的降低編碼和素材設計的難度。但是FIXED_HEIGHT、FIXED_WIDTH這2個中應該選哪個這應該跟要實際開發的游戲有關系,比如橫屏游戲比較合適FIXED_WIDTH而豎屏游戲比較合適FIXED_HEIGHT,同時有可能和游戲素材設計也有一定的關系,比如我們的這個游戲地圖背景,至少要保證地圖中道路部分應該完整的呈現在手機屏幕的可視區域,其他部分可以允許被一定的遮蓋。由此可見我們的游戲應該選擇FIXED_WIDTH。
對前一篇完成的塔防游戲原型進行屏幕適配完善
上面的測試和分析決定了我們的游戲采用FIXED_WIDTH類型解決問題,現在展開具體的編碼工作。
第一步:
在AppDelegate.cpp的applicationDidFinishLaunching方法中,添加如下代碼:
//設置游戲的設計尺寸以及分辨率策略 glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height,ResolutionPolicy::FIXED_WIDTH);
第二步:設置模擬器屏幕尺寸glview->setFrameSize(960,540);然后運行游戲
會發現游戲道路完整顯示,背景圖上部分和下部分有一定區域被遮蓋沒有顯示出來,這樣我們在設計素材的時候可以刻意的把背景圖的上下區域加高,盡量讓素材高度很高道路等有效部分盡量居中,比如我們要適應960x960的正方形屏幕,只需要通過把地圖的上部分和下部分同時加高直至960或者更高,並且加高部分只需要平鋪綠色的底紋就可以了,這樣游戲就能和諧的滿屏幕顯示。
第三步:上面這個游戲還有個問題,看下圖由於背景素材底部有部分被擋住了,導致了設計時候的坐標原點和實際游戲坐標原點在高度上發生了偏移。
但是我們在前一篇時候,道路的路點坐標是按照相對設計原點進行計算的,現在原點坐標發生了偏移導致了這些路點坐標不准,這樣就需要在代碼上對這個偏移高度進行修正。
dw:素材寬 dh:素材高 sw:屏幕寬 sh:屏幕高 偏移高度值:x
x=( sh *0.5- (sw /dw)* dh *0.5) /( sw /dw)
偏移高度通過這個公式進行計算,我們在applicationDidFinishLaunching的時候用這個公式把偏移高度計算出來,然后保存到一個靜態變量中,方便后續可以直接使用。
進行GameMediator.h、GameMediator.cpp類,用來保存一些游戲中經常使用的變量,比如我們的偏移高度和素材縮放比例就保存在這個類中,這個類實現一個單實例的模式,功能很簡單以單實例靜態變量的方式存放變量。
GameMediator.h:
class GameMediator : public cocos2d::CCObject { public: GameMediator(void); ~GameMediator(void); bool init(); //獲取單實例 static GameMediator* sharedMediator(); //偏移高度 CC_SYNTHESIZE(float,_offsetHeight,OffsetHeight); //縮放比例 CC_SYNTHESIZE(float,_scaleHeight,ScaleHeight) }; GameMediator.cpp: //靜態實例 static GameMediator _sharedContext; GameMediator* GameMediator::sharedMediator(){ static bool s_bFirstUse =true; if (s_bFirstUse) { _sharedContext.init(); s_bFirstUse=false; } return &_sharedContext; } GameMediator::GameMediator(void) { } GameMediator::~GameMediator(void) { } bool GameMediator::init(){ bool bRet=false; do { _offsetHeight=0; _scaleHeight=1; bRet = true; } while (0); return bRet; }
第四步:在AppDelegate類中引入GameMediator的頭文件,然后在applicationDidFinishLaunching方法中加入如下代碼:
//以寬為標准計算素材縮放比例 float scaleHeight=frameSize.width/designResolutionSize.width; //高度偏移值計算 //x=( sh *0.5- (sw /dw)* dh *0.5) /( sw /dw) float offsetHeight=(frameSize.height*0.5f-scaleHeight*designResolutionSize.height*0.5f)/scaleHeight; //保存縮放比例 GameMediator::sharedMediator()->setOffsetHeight(offsetHeight); //保存高度偏移值 GameMediator::sharedMediator()->setScaleHeight(scaleHeight);
第五步:找到MainScene.cpp中init方法聲明12個路點坐標的地方做如下修改:
…… //獲得保存的偏移高度 float offsetHeight=GameMediator::sharedMediator()->getOffsetHeight(); //獲得保存的縮放比例 float scaleHeight=GameMediator::sharedMediator()->getScaleHeight(); …… //添加地圖1號路徑點到集合中 Waypoint *waypoint1=Waypoint::nodeWithTheLocation(Point(920, 435+offsetHeight)); …… //添加地圖12號路徑點到集合中 Waypoint *waypoint12=Waypoint::nodeWithTheLocation(Point(50, 350+offsetHeight)); ……
第六步:這樣路點坐標均已經校正完畢,現在我們還需要對色狼和女主角做一下提高半個身位的校正使得他們的腳底部剛剛在道路的中央。找到MainScene.cpp中init方法中初始化色狼和女主角部分的代碼修改如下:
…… //獲得色狼大叔的高 float dsh=dsSprite->getTextureRect().size.height; …… //女主高 float nzh=nhSprite->getTextureRect().size.height; …… //獲取集合中的最后一個點,12號點 Waypoint *waypoint0=wayPositions.back(); //設置運動的開始點 beginningWaypoint=waypoint0; //設置運動的目標點為12號點的下一個點,11號點 destinationWaypoint=waypoint0->getNextWaypoint(); //設置色狼當前位置值 myPosition=waypoint0->getMyPosition(); //提高半個色狼身位 myPosition.add(Vec2(0,dsh/2.0f)); //設置色狼在地圖的初始位置 dsSprite->setPosition(myPosition); //設置女主角在地圖的初始位置,為集合中的1號點 Point pos=wayPositions.front()->getMyPosition(); //提高半個女主角身位 pos.add(Vec2(0,nzh/2.0f)); nhSprite->setPosition(pos); ……
找到MainScene.cpp中update方法中色狼沿着道路移動部分的代碼修改如下:
//獲得色狼大叔的高 float dsh=dsSprite->getTextureRect().size.height; Point destinationPos=destinationWaypoint->getMyPosition(); //提升色狼半個身位 destinationPos.add(Vec2(0,dsh/2.0f)); //判斷色狼大叔是否和目標點碰到 if (this->collisionWithCircle(myPosition,1,destinationPos,1) ) { //是否還有下一個目標點 if (destinationWaypoint->getNextWaypoint()) { //重新設定開始點和目標點 beginningWaypoint=destinationWaypoint; destinationWaypoint=destinationWaypoint->getNextWaypoint(); } } //獲取目標點的坐標 Point targetPoint=destinationWaypoint->getMyPosition(); //提升色狼半個身位 targetPoint.add(Vec2(0,dsh/2.0f));
第七步:開始測試修改的效果,通過glview->setFrameSize(960,540);不斷修改各種尺寸的屏幕看看這些尺寸屏幕下的游戲效果。並且編譯打包一下so文件在android真機上看看之前的問題解決沒有,打包前記得把所有新加的cpp文件添加到Android.mk的編譯列表里。
修正后真機上的效果:
結束語:
本篇花了很長的篇幅來解決上一篇遺留的一個問題,但是我覺得這個非常值得因為這個屏幕適配問題是一個很重要的問題,盡早合理的選擇方案能減少后續很多的返工量,現在我們解決了這個問題,后續在開發寫代碼的時候就會讓編碼適應這個方案。本篇到此結束請期待:Cocos2d-x3.x塔防游戲(保衛蘿卜)從零開始(三)篇
作者交流QQ:2303452599
郵箱:mymoney1001@126.com