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

相关文章
相关标签/搜索