Cocos2d-x3.x塔防遊戲(保衛蘿蔔)從零開始(二)

1、前提:html

完成前一篇的內容。node

具體參考:Cocos2d-x3.x塔防遊戲(保衛蘿蔔)從零開始(一)篇android

2、本篇目標:windows

l  說說關於cocos2dx手機分辨率適配app

l  對前一篇完成的塔防遊戲原型進行屏幕適配完善iphone

3、內容:測試

l  說說關於cocos2dx手機分辨率適配this

      在上一篇的結尾咱們遺留了一個問題,在真機上運行時女主角、色狼的位置至關於道路都有點偏上了,而且好像背景地圖也沒有顯示全背景的頂部和底部有一部分沒有顯示出來,可是在windows下運行確正常,這個是什麼緣由呢,該怎麼調整呢?個人手機分辨率是:960x540 而咱們的地圖素材圖片分辨率是:960x640,兩個尺寸的不一樣致使了這個問題,這個是關於不一樣手機屏幕分辨率適配問題,Android手機品種繁多屏幕尺寸分辨率也一樣繁多,爲了讓遊戲適應不一樣的Android手機須要多作不少的工做,這個作IOS的遊戲要幸福多畢竟就這麼幾個尺寸分辨率。編碼

      影響遊戲的兩個因素屏幕大小(分辨率)、寬高比,屏幕大小從小屏的480×320手機到大屏甚至平板的2048×1536,若是用低分辨率的素材圖在高分辨率的設備上就會圖像模糊,若是用高分辨率的素材在低分辨率設備上時增長系統負擔,全部通常咱們採起多套不一樣分辨率素材進行匹配,這個問題還算容易解決。可是寬高比就麻煩的多,手機有3:2,16:9 標準寬屏等,本篇中我測試用的華爲手機爲16:9的寬屏,寬高比會形成遊戲不按比例的壓縮或者拉伸形成遊戲上的元素顯示、位置等發生變異甚至致使遊戲沒法使用,因此寬高比形成的問題比屏幕大小要嚴重的多,好比咱們的這個塔防遊戲就由於這個問題形成了遊戲人物位置偏移。那寬高比是否也能夠像分辨率同樣採用多套寬高比的素材解決?確定能夠!可是若是不一樣的寬高比結合不一樣的分辨率這樣得提供多少套的素材啊?而且新的寬高比的手機在不斷的新出來爲了每出一款手機就作一套素材這樣的成本太大。spa

在cocos2d-x-3.3/tests目錄下有一個名爲cpp-empty-test示例工程,用Microsoft Visual Studio 2012打開proj.win32下的工程,能夠看到它對於這個問題的解決方案,參考其中的AppMacros.h、AppDelegate.cpp、Resources目錄下的素材。

經過這個示例項目能夠以下總結:

一、屏幕大小(分辨率)解決方案

      按照上面的思路提供多套的素材,通常遊戲作法提供低、中、高、超高四套不一樣的分辨率的素材,低的應付通常小屏手機、中的應付高分辨率手機、高的應付平板、超高的應付高清平板或者電視之類的設備。四套素材分4個文件夾放置到項目Resources文件下好比叫iphone、iphonehd、ipad、ipadhd。在設備載入遊戲時,判斷當前設備的分辨率而後選擇不一樣文件夾下的素材進行載入以適應不一樣分辨率的設備。

二、寬高比解決方案

      爲了適應設備各類屏幕寬高比,在 Cocos2dx中,提供了相應的解決方案,以方便咱們在設計遊戲時,可以更好的適應不一樣的屏幕。Cocos2dx提供了ResolutionPolicy(分辨率策略),經過給GLView設置不一樣ResolutionPolicy來解決這個問題。

ResolutionPolicy的五種類型:

一、          EXACT_FIT

二、          NO_BORDER

三、          SHOW_ALL

四、          FIXED_HEIGHT

五、          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_HEIGHTNO_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

相關文章
相關標籤/搜索