您的位置:首页 > 移动开发 > Cocos引擎

Cocos2D-X屏幕适配新解-兼容与扩展

2013-05-20 08:54 375 查看
转自 http://www.cocoachina.com/applenews/devnews/2013/0513/6186.html
为了适应移动终端的各种分辨率大小,各种屏幕宽高比,在 Cocos2D-X(当前稳定版:2.0.4) 中,提供了相应的解决方案,以方便我们在设计游戏时,能够更好的适应不同的环境。

而在设计游戏之初,决定着我们屏幕适配的因素有哪些?简而言之只有两点:屏幕大小和宽高比。这两个因素是如何影响游戏的呢?

屏幕大小: 从小分辨率 480×320 1280×800 分辨率,再到全高清 1080p,从手机到平板,还有苹果设备的 Retina屏,这么多不同的分辨率,而且大小差距甚大,不可能做到一套资源走天下,资源往小了设计,在大屏幕会显示模糊,图片往大了设计,在小屏幕设备又太浪费,而且小屏幕的手机硬件资源也会相对的紧缺,所以 根据屏幕大小使用不同的资源 是有必要的,而
Cocos2D-X 也帮我们解决了这一点。

宽高比: 什么是宽高比,就是你的屏幕是方的还是长的,靠近方形的分辨率如 480×320,比例为 3:2,还有 960×540 的16:9 标准宽屏,这也算是两种总极端情况了,如果能在这两种比例情况做好适配基本就可以了,如果比 3:2 “更方”如
4:3,比 16:9 “更长”,那么不论如何布局,显示效果差距甚大,最好对固定比例优化吧。当在宽高比在一定范围内,可以通过灵活编写程序去适应,而在显示效果上,Cocos2D-X为我们提供了三种模式,这些 模式更多的是帮我们解决比例不一的情况而存在 的,如果只是屏幕大小(比例一样),那通过简单的放大缩小即可完成。

三种模式

说是三种模式,其实还有一种“无模式”,也就是 Cocos2D-X默认的适配方案,现在我们就来认识一下这些模式,并且通过这些模式去认识其中一些概念
FrameSize、WinSize、VisibleSize、VisibleOrigin
,以及它们存在的意义,并且最后灵活运行这些概念
创建出一个不属于这些模式而超越这些模式的新适配解决方案
,这是最终目的。

kResolutionUnKnown 认识 FrameSize

这是 Cocos2D-X 编写的默认模式,没有做任何处理,在这种情况下,游戏画面的大小与比例都是不可控的,在程序运行之初,由各个平台入口函数定义画面大小:
1.// proj.linux/main.cpp linux 平台手动指定画面大小
2.CCEGLView* eglView = CCEGLView::sharedOpenGLView();
3.eglView->setFrameSize(720, 480);
4.
5.// proj.android/jni/hellocpp/main.cpp android 平台由 jni 调用传入设备分辨率参数
6.void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h)
7.{
8. if (!CCDirector::sharedDirector()->getOpenGLView())
9. {
10. CCEGLView *view = CCEGLView::sharedOpenGLView();
11. view->setFrameSize(w, h);
12.
13. AppDelegate *pAppDelegate = new AppDelegate();
14. CCApplication::sharedApplication()->run();
15. }
16. else
17. {
18. // other
19. ...
20. }
21.}

1.

在此我们首先认识了 FrameSize 参数,在游戏运行时,我们可以通过 CCEGLView::sharedOpenGLView()->getFrameSize();获得此值。如果在手机上运行,那么不同分辨率将会得到不同的值,既然这个值不可控,那么在写游戏中也就没有参考价值了,比如我们写一个精灵的位置距离底部 320 高度,在 480×320 分辨率,能看到其在屏幕上方,如果换一台手机分辨率 960×540 那么只能显示在中间靠上的位置,如果设置精灵位置为距离屏幕上方(高度)320,反之依然,显示效果不一。

此时可行的方案是使用百分比,如精灵位置在屏幕横向距离左边 1/3 宽度,在 1/2 正中间处,而类似这样的设置也不用依赖 FrameSize 的具体数值。而这样的做法,使得内部元素像弹簧一样,随着 FrameSize 的大小改变而改变,伸缩或者挤压,对于图片资源大小也是完全不可控,如果根据屏幕大小放大缩小,那我们可以考虑用下面要说的模式,在此不推荐使用 Cocos2D-X 的无模式方案。

kResolutionExactFit and kResolutionShowAll 认识 WinSize

在 AppDelegate.cpp 处可以通过设置:
1.CCEGLView::sharedOpenGLView()->setDesignResolutionSize(720, 480, kResolutionShowAll);
2.// 或者
3.CCEGLView::sharedOpenGLView()->setDesignResolutionSize(720, 480, kResolutionExactFit);

1.

DesignResolutionSize!顾名思义,也就是逻辑上的游戏屏幕大小,在这里我们设置了其分辨率为 720×480 为例,那么在游戏中,我么设置精灵的位置便可以参照此值,如 左下角 ccp(0,0),右上角 ccp(720, 480),而不论 FrameSize 的大小为多少,是 720×480 也是,是 480×320 也罢,总能正确显示其位置,左下角和右上角。能够实现这一点的原因是,固定了设计分辨率大小,从而确定了其固定的宽高比,它的 优势 是可以使用具体的数值摆放精灵位置,不会因为实际屏幕大小宽高比而是内部元素相对位置关系出现混乱。

而为了保持画面的宽高比,Cocos2D-X 做了些牺牲,牺牲了什么呢?kResolutionExactFit 牺牲了画质而保持了全屏显示,对画面进行了拉伸,这意味着什么?意味着相对极端情况下,本来精灵是方形的,显示出来变成长方形,本来圆形的变成了椭圆,固此模式不推荐使用。kResolutionShowAll 为了保持设计画面比例对四周进行留黑边处理,使得不同比例下画面不能全屏。鱼和熊掌不能兼得也。

我们可以通过如下方法获取到 setDesignResolutionSize 所设置的值:
1.CCSize winSize = CCDirector::sharedDirector()->getWinSize();

我们可以用 Cocos2D-X 程序是如何开始运行与结束的 一文的方法,跟踪 WinSize 的初始化,获取过程,在这里简单提一下,如下步骤:
1.// 获得 winSize
2.CCSize winSize = CCDirector::sharedDirector()->getWinSize();
3.
4.// 查看其 getWinSize(); 方法实现
5.[cocos2dx-path]/cocos2dx/CCDirector.cpp
6.
7.CCSize CCDirector::getWinSize(void)
8.{
9. return m_obWinSizeInPoints;
10.}
11.
12.// 而 m_obWinSizeInPoints 是何时被赋值的
13.[cocos2dx-path]/cocos2dx/platform/CCEGLViewProtocol.cpp
14.
15.void CCEGLViewProtocol::setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy)
16.{
17. ...
18. ...
19. m_obDesignResolutionSize.setSize(width, height);
20.
21. ...
22. ...
23. CCDirector::sharedDirector()->m_obWinSizeInPoints = getDesignResolutionSize();
24.}
25.
26.const CCSize& CCEGLViewProtocol::getDesignResolutionSize() const
27.{
28. return m_obDesignResolutionSize;
29.}

1.

具体的优势:通过设置逻辑分辨率大小,相比无模式,可以帮我们解决了屏幕自动放大缩小问题,并且保持屏幕宽高比,使得游戏更好设计,可以将设计画面大小作为默认背景图片大小等,唯一点遗憾就是那点前面所提到的一点点牺牲。

kResolutionShowAll 方案可以作为我们的默认解决方案,使得游戏的设计更为简化,但为了补填拉伸或留黑边这点缺憾,进入下一个模式!

kResolutionNoBorder了解 VisibleSize 与 VisibleOrigin

此模式可以解决两个问题,其一:游戏画面全屏;其二:保持设置游戏时的宽高比例,相比 kResolutionShowAll 有所区别的是,为了填补留下的黑边,将画面稍微放大,以至于能够正好补齐黑边,而这样做的后果可想而知,补齐黑边的同时,另一个方向上将会有一部分画面露出屏幕之外,如下示意图:



黑色边框标示实际的屏幕分辨率,紫色区域标示游戏设计大小,而通过放大缩小,保持宽高比固定, 可以看到 Show All 之中的黑色阴影部分为留边,而 No Border
的紫色阴影部分则不能显示,而这紫色区域的大小是游戏设计之时是不可控的。那么原设计的画面大小就失去了 一定的 参考价值了,因为这可能让你的画面显示残缺。这时仅仅通过 WinSize 满足不了我们的设计需求,所以引入了 VisibleSize 与 VisibleOrigin
概念。



如上所示,紫色区域是被屏幕截去的部分,不可显示的,根据实际情况,可能出现横向截取和竖向截取,这取决于实际分辨率的宽高比。而 A、B、C、D所标示的是设计分辨率,固定大小。如果我们想让一个精灵元素显示在屏幕上方靠边,那么如果使用 WinSize 的高度设置其位置,可能出现的情况就是显示到屏幕之外了。FrameSize 和 WinSize
我们已经知道其概念,而 VisibleSize 和 VisibleOrigin 所代表的是什么呢,又时如何为我们解决靠边的问题!注意上图下方的定义, VisibleSize = H I J K 是用紫色标注的。 而在上图是 黑色 标注,标示屏幕实际分辨率,虽然 FrameSize 和 VisibleSize 都是 H I J K,但其意义不同,紫色表明它是与设计分辨率相关的。

FrameSize 是实际的屏幕分辨率,而 VisibleSize 是在
WinSize
之内,保持 FrameSize 的宽高比所能占用的最大区域,实际屏幕分辨率 H I J K (黑色) 可以大于 WinSize ,但VisibleSize 一定会小于或者等于 WinSize,这两者相同的是宽高比。

VisibleSize 有着 WinSize 大小(随WinSize 的大小改变而改变),还有着 FrameSize
的宽高比,它标示 在设计分辨率(WinSize)下,在屏幕中的可见区域大小。 而 VisibleOrigin 则标示在设计分辨率下被截取的区域大小,用点 K 标示,有了这些数据,我们想让游戏元素始终在屏幕显示的区域之内不成难事。下面通过几个数值带入,加深这些概念的印象。
1.// 组[1] :
2.FrameSize: width = 720, height = 420
3.WinSize: width = 720, height = 480
4.VisibleSize: width = 720, height = 420
5.VisibleOrigin: x = 0, y = 30
6.
7.// 组[2] :相比 组 [1] FrameSize 不变 VisibleSize 和 VisibleOrigin 随着 WinSize 的变小而变小
8.FrameSize: width = 720, height = 420
9.WinSize: width = 480, height = 320
10.VisibleSize: width = 480, height = 280
11.VisibleOrigin: x = 0, y = 20
12.
13.// 组[3] : 相比组 [1] WinSize 不变,VisibleSize 随着 FrameSize 的比例改变而改变
14.FrameSize: width = 720, height = 540
15.WinSize: width = 720, height = 480
16.VisibleSize: width = 640, height = 480
17.VisibleOrigin: x = 40, y = 0
18.
19.// WinSize VisibleSize VisibleOrigin 与都设计的分辨率相关,满足如下关系
20.WinSize.width = (VisibleOrigin.x * 2) + VisibleSize.width
21.WinSize.height = (VisibleOrigin.y * 2) + VisibleSize.height

1.

NoBorder 具体的使用方法可以参考 Cocos2D-X 自带例程 TestCpp
,有详细的使用方法,并且封装了 VisibleRect 类,可以获取设计分辨率,不同比例屏幕之时的主要参考点,屏幕四个拐角,和边的中点等,让我们设置元素位置时,使其总能显示在屏幕之内,这里就不详细介绍了。

基于这几种模式的程序使用方法, Cocos2D-X 自带例程或者网上有很多教程,这里只详细解释了其中各种概念,而知道了这些概念,当然用起来就没有多大问题了。

kResolutionLeafsoar

!!!这是什么模式!好吧,Leafsoar 是 一叶 的 ID ,或者是本博客的一级域名而已 :P 在 cocos2d-x 中并没有这种模式。除却 UnKnown ExactFit 不说,ShowAll 的优势是,只需要一个设计分辨率,然后通过 WinSize 设置相对对位即可,而且位置的最大长宽都是确定,方便了开发,但屏幕不能填满, NoBorder 模式的优势是在画面不变形的情况下,实现全屏,显示效果更好,但 WinSize
一定程度失效,需要通过运行时计算 VisibleSize 和 VisibleOrigin 来设置位置,由于是运行时计算,所以也就会出现,各种屏幕显示效果不一样的情况。

ShowAll 和 NoBorder 各有所长,各有所短,而这里提出的新适配解决方案正是取两者之长,舍两者之短的组合模式。简单说来就是用 NoBorder 去实现 ShowAll 的思想。NoBorder 可以保证全屏利用,ShowAll 可以更好的使用实际设计坐标固定位置,而且相对位置不会随宽高比的改变而改变,这在编写游戏的时候能方便不少。先上一个示意图,一目了然 (两个图,两个方向):



在原来 NoBorder 模式示意图上添加了新的概念,LsSize = X Y M N (leafsoar 简写了,为了不跟 Cocos2D-X 的一些概念混淆,什么名字不重要,只要了解其含义即可),在 NoBorder 模式下的 LsSize 相对于 FrameSize 而言,正如 在
ShowAll 模式下的 WinSize 相对于 FrameSize,所以说这是 ShowAll NoBorder 的组合概念,而这里的 LsSize 与 WinSize 的宽高比是一致的

猛地一看,似乎把问题复杂化了,仔细一看,还不如猛地一看 ~~

在 ShowAll 中,WinSize 作为最高的宽高,以此参照设置位置,因为在此范围内都能在屏幕上显示,用了 NoBorder 使得四周可能被截去一块区域,而这个区域大小不可控制,所以不能再使用 WinSize 作为参考点来设置位置,而这里的 LsSize 同样,因为 LsSzie 不论在什么情况下,总能显示在屏幕之内,我们可以方便的使用 LsSize 作为坐标系参考,并且可以全屏显示,在配合 VisibleSize ,相比纯的 NoBorder 加强了不少。它可以怎么用?

可以把 LsSize 当作 ShowAll 中的 WinSize 来用,而黑边可以使用稍大的图片填充,或者使用其它图片修饰边框,修饰的边框图案可大可小,可长可短,填充屏幕,保持全屏。

开始基于 LsSize 的游戏设计实现

为了能够准确实现基于 LsSize 的设计,初步计划将 LsSize 设定在 480×320 的分辨率方案,为此做了些准备,首先不使用任何模式情况下,在场景内调用如下:
1.CCSize size = CCDirector::sharedDirector()->getWinSize();
2.
3.CCPoint center = ccp(size.width/2, size.height/2);
4.
5.// 大小 600x500 为了 NoBorder 看到效果,使用稍大的背景图
6.CCSprite* pb = CCSprite::create("Back.jpg");
7.pb->setPosition(center);
8.this->addChild(pb, 0);
9.
10.// 480x320 此图为使用于设计分辨率 LsSize 的图片
11.CCSprite* pSprite = CCSprite::create("HelloWorld.png");
12.pSprite->setPosition(center);
13.this->addChild(pSprite, 0);
14.
15.// 37x37 在 480x320 画面的四个拐角处,添加参照
16.CCSprite* p1 = CCSprite::create("Peas.png");
17.p1->setPosition(ccpAdd(center, ccp(-240, -160)));
18.this->addChild(p1);
19.
20.CCSprite* p2 = CCSprite::create("Peas.png");
21.p2->setPosition(ccpAdd(center, ccp(240, 160)));
22.this->addChild(p2);
23.
24.CCSprite* p3 = CCSprite::create("Peas.png");
25.p3->setPosition(ccpAdd(center, ccp(-240, 160)));
26.this->addChild(p3);
27.
28.CCSprite* p4 = CCSprite::create("Peas.png");
29.p4->setPosition(ccpAdd(center, ccp(240, -160)));
30.this->addChild(p4);

1.

显示效果:(FrameSize = 640×540)



显示效果:(ShowAll; FrameSize = 520×320; WinSize = 480×320)



显示效果:(NoBorder; FrameSize = 520×320; WinSize = 480×320)



通过效果我们可以看到,在相同 FrameSize 下 NoBorder 时,画面由于填充了黑边,将画面放大,以至于上下有部分显示不全,通过拐角四个精灵可以看出。

好!既然我们知道是由于放大所致,那么我们将画面缩小呢?cocos2d-x 提供了一个方法,我们调用如下代码:

1.CCDirector *pDirector = CCDirector::sharedDirector();
2.pDirector->setContentScaleFactor(
3. CCEGLView::sharedOpenGLView()->getScaleY() );

为了弥补画面因需要不填空白出现的方法,我们将画面缩小,放大系数可以通过 CCEGLView::sharedOpenGLView()->getScaleY()
取得。其实 setContentScaleFactor 方法是为了适配不同资源而设计的,可以用此方法对不同资源适配,缩放等。效果如下:



我们看到 480×320 的图片显示完全正确了,也正是我们想要的效果,但唯一的缺点是 ~~ 拐角处四个精灵的位置依然不是我们想要的,我们设计的位置是以 480×320 设置位置的,而 WinSize 也是 480×320 ,而此时基于 480×320 的设计必然会显示到屏幕之外,而要想不修改精灵位置,而让其显示正确的位置,那么为了保证 LsSize 的固定,我们需要一个方法,那就是动态设置 WinSize

什么意思?我们知道一般这些模式设计游戏时,是通过 setDesignResolutionSize 设置 WinSize 的,这个值在游戏运行其间是定植,动态改变的是 VisibleSize 等,而这里提出了 LsSize 的概念,可想而知,如果 WinSize 固定,那么 LsSize 会随着屏幕宽高比的改变而改变,那么我们反其道而行,固定 LsSize 值,那么在运行时可以通过实际的宽高比来算得 WinSize 的值,这样动态算得的 WinSize 值就能够保证我们的
LsSize 是一个定值了。

相对论,WinSize 与 LsSize 的值是相对的,与其通过固定 WinSize 在运行时动态获得 LsSize
(这也是 NoBorder 的默认方式,而导致的结果是 WinSize 没有参考价值),不如我们固定 LsSize 而在运行时算得 WinSize 设置来的要更妙一些。

现在不使用 setContentScaleFactor 方法,而修改 setDesignResolutionSize
这里的值,我们知道 WinSize 是 480×320 时,LsSize 必然会小于此值,而 NoBorder 的放大系数我们可以通过如下方式算得(可以参考setDesignResolutionSize方法内部实现),并在 AppDelegate 里执行:
1.CCSize frameSize = CCEGLView::sharedOpenGLView()->getFrameSize();
2.// 设置 LsSize 固定值
3.CCSize lsSize = CCSizeMake(480, 320);
4.
5.float scaleX = (float) frameSize.width / lsSize.width;
6.float scaleY = (float) frameSize.height / lsSize.height;
7.
8.// 定义 scale 变量
9.float scale = 0.0f; // MAX(scaleX, scaleY);
10.if (scaleX > scaleY) {
11. // 如果是 X 方向偏大,那么 scaleX 需要除以一个放大系数,放大系数可以由枞方向获取,
12. // 因为此时 FrameSize 和 LsSize 的上下边是重叠的
13. scale = scaleX / (frameSize.height / (float) lsSize.height);
14.} else {
15. scale = scaleY / (frameSize.width / (float) lsSize.width);
16.}
17.
18.CCLog("x: %f; y: %f; scale: %f", scaleX, scaleY, scale);
19.
20.// 根据 LsSize 和屏幕宽高比动态设定 WinSize
21.CCEGLView::sharedOpenGLView()->setDesignResolutionSize(lsSize.width * scale,
22. lsSize.height * scale, kResolutionNoBorder);

1.

显示效果:(NoBorder 模式 ;FrameSize = 520×320; LsSize = 480×320; WinSize = 动态获取)



我们看到在没有修改源代码,并且在设计中使用 480×320 的参考系,也既是基于 LsSize 的设计显示效果如我们预期,那么我们换一个 FrameSize 来看看是否能够自动适应呢?如下:

显示效果:(NoBorder 模式 ;FrameSize = 600×480; LsSize = 480×320; WinSize = 动态获取)



到此,基于 LsSize 参考系的游戏设计已经完成了,这样做的好处是很明显的,集 ShowAll 和 NoBorder 的优点于一处,这里的图片元素是为了好定位,实现的需要而写的,具体场景可以使用背景地图,或一张大的图片显示,而没有任何影响,也可以继续使用 VisibleSize 得到 LsSize 之外的部分区域大小,在 LsSize 之外可以使用背景图片作为装饰,即保证了游戏的全屏,又保证了游戏设计时的方便,如果使用完全基于 LsSize 的设计实现,除了显示背景装饰之外,我们不想让 LsSize
的内部元素显示到 LsSize 之外如何做呢?我们只需要设定 LsSize 层的的显示区域即可,我们可以修改场景的实现:

1.// 这里先简单实现思路
2.
3.CCScene* HelloWorld::scene() {
4.
5. CCScene *scene = CCScene::create();
6. // 创建背景层
7. CCLayer* b = CCLayer::create();
8. scene->addChild(b);
9.
10. // 添加背景图片和设置位置,可以使用其它装饰,或者小图片屏幕都行
11. CCSize size = CCDirector::sharedDirector()->getWinSize();
12. CCPoint center = ccp(size.width/2, size.height/2);
13. CCSprite* pb = CCSprite::create("Back.jpg");
14. pb->setPosition(center);
15. b->addChild(pb, 0);
16.
17. // 创建 LsLayer 层
18. HelloWorld *lsLayer = HelloWorld::create();
19. scene->addChild(lsLayer);
20.
21. return scene;
22.}
23.
24.// 在 HelloWorld 中重写 visit() 函数 设定显示区域
25.void HelloWorld::visit() {
26. glEnable(GL_SCISSOR_TEST); // 开启显示指定区域
27. // 在这里只写上固定值,在特性环境下,以便快速看效果,实际的值,需要根据实际情况算得
28. glScissor(20, 0, 480, 320); // 只显示当前窗口的区域
29. CCLayer::visit(); // 调用下面的方法
30. glDisable(GL_SCISSOR_TEST); // 禁用
31.}

1.

显示效果:(NoBorder 模式 ;FrameSize = 520×320; LsSize = 480×320; WinSize = 动态获取)



屏幕适配新解

看完这篇文章想必对Cocos2D-X的屏幕适配方案及其原理有了相当的认识,从内部提供的三种模式,再到我们自定义基于 LsSize 的 Leafsoar
模式 (好吧,因该叫做 ShowAllNoBorder)。这里已经给出了完全的实现原理以及实现方法,并配有效果图,当然这其中还有些细节需要注意,比如我们基于 LsSize 的大小设计,那么实际的图片肯定需要比 LsSize 的要大,大多少,太小了不够适应,太大了又浪费,如何取舍等问题,这一点取决的因素是什么,留给读者思考。

一叶将在 GitHub 处建立一个ScreenSolutions 项目,读者可以从这里参考实现的方案。(也许此时在 GitHub 所看到的实现并不完全,但已经有了简单的实现方法,并且能够运行,如有必要,将会新写一篇博客,去实现 ScreenSolutions 并且解说)。

在读这篇文章之前,先读前一篇文章
《Cocos2D-X屏幕适配新解》 是必要的。

如果说前一篇文章文章在 LsSize 提出之前是基础,LsSize 是应用,那么对于这篇文章来说,LsSize 是基础,而这里是其的综合应用,我的初衷是其扩展性和兼容性,激发读者思维。也许你并没有体会出 LsSize 的强大,而实际上,它能做的比你想象的要多的多,这是前话 ~

ShoAll 模式的兼容

首先 LsSize 能满足 ShowAll 模式的需要,因为开始就是把 LsSize 当作 ShowAll 中的 WinSize 来设计的。 并且可以为背景做装饰,而在游戏设计之时并没有什么区别,LsSize 可以设定显示区域的大小,使背景层与 LsSize 分离(这一点在上一篇文章最后已经提到),从而保证了游戏的元素不会超出 LsSize 而露出到 VisibleSize 的区域内。

NoBorder 模式兼容

为什么说 NoBorder 兼容模式,它本身不就是 NoBorder 么,它与实际的 NoBorder 区别又在何处,有何优势?首先说说兼容,使用此模式,并不影响 你继续使用 VisibleSize 和 VisibleOrigin(以后简写 Visible),你可以不使用 LsSize 的参考点,而使用 Visible 的相关值获取屏幕的拐点,游戏元素按照 Visible 来设置也可。下面详细介绍基于 LsSize 的 NoBorder 和原油 NoBorder 的区别以及其优势。

我们设想这样一个 实际情况 。我们需要一套资源图片,做为在适合分辨率的资源展示,当屏幕的大小分辨率在 854×480,800×480,728×480 (横屏下:为什么高度同样是 480 而宽度有这么多值 : P 这也是屏幕适配万恶之一了吧) 时,我们使用一套资源,当高度小于 480 时,我们使用另一套小的资源是合理的设计。而这里我们的资源宽姑且先不论,高一定是 480 最为合适了,最接近此分辨率的图片。那我们使用 NoBorder 的时候该 设置 WinSize 为多少 了呢?基于 854×480
设计!好,那么当程序跑在 854×480 的屏幕上,正好满屏显示,而图片资源并没有放大或者缩小,或者说基于像素点点对点显示的。但是当这样设计的 WinSize 跑在 800×480 和 728×480 分辨率会如何?也许已经知道了,为了保证小于 854 那一小块区域的显示,画面将会缩小那么一点点,也许在如今屏幕的 ppi 日益渐高的情况下,并不十分明显,但画面一定是有那么一点模糊了。同理可以遇见,不论 WinSize 如何设置,在 三种少许不同分辨率下,显示的效果肯定略有不同。 而分辨率差别越大,这种效果就越明显。

读到这里也许已经发现了,LsSize 已经完美的解决了这个问题,动态 WinSize ,一个合理的设计,我梦将 LsSize 设定为 720×480,并且使用 高度为 480 的图片资源,而宽度可以往大了设计,比如 854×480 的图片资源,读过前文 LsSize 的实现原理,我们可以知道,在这三种情况下,屏幕的画面并没有缩放,因为实际的宽度总是大于 720 ,从而达不到缩放的条件。480 高度的图片,能够正好填充 480 高度的屏幕,而图片的宽度往大了设计,在宽度稍微小的屏幕下,会被截取一部分,但就显示效果来说,并没有什么损失,而游戏的元素位置可以用原来的方法基于
Visible 来设计。

为什么可以做到如此!原本的 NoBorder 通过固定 WinSize 根据屏幕宽高比缩放,所以会有不同程度的缩放,而 LsSizeNoBorder 的设计实现,通过屏幕宽高比来获得 WinSize 的值,以保证 LsSize 总能在屏幕上正好全部显示。

LsSizeNoBorder 比 NoBorder 好,好多少,这就仁者见仁,智者见智了 ~

kResolutionFixedHeight,kResolutionFixedWidth 扩展兼容模式

FixedHeight 和 FixedWidth 是什么模式,如果你试用了最新版的 cocos2D-X (2.1.3)就能发现这两种模式,一种是固定设计时的高,一种是固定设计时的宽。而在当前的 2.0.4 并没有这两种模式,而现在,你可以通过 LsSize 来实现这两种模式。存在既是合理,FixedHeight 和 FixedWidth 的存在是合理的,比如我们写一个横版过关游戏,同样是三种分辨率 854×480,800×480,728×480, 使用固定设计高度的方法,可以避免在
NoBorder 中会根据宽度而做的缩放。

而在 LsSizeNoBorder 中好似也实现了相同的功能!但注意有一点区别,在 LsSizeNoBorder 中,实际屏幕高度为 480 ,如果宽度小于 720 时,那么画面会缩放,而这里新模式固定高度不会。如果我们想让 LsSize 实现这种功能怎么做,我们将对 LsSize 的设计稍作扩展,上篇文章的代码:
1.CCSize frameSize = CCEGLView::sharedOpenGLView()->getFrameSize();
2.// 设置 LsSize 固定值
3.CCSize lsSize = CCSizeMake(480, 320);
4.
5.float scaleX = (float) frameSize.width / lsSize.width;
6.float scaleY = (float) frameSize.height / lsSize.height;
7.
8.// 定义 scale 变量
9.float scale = 0.0f; // MAX(scaleX, scaleY);
10.if (scaleX > scaleY) {
11. // 如果是 X 方向偏大,那么 scaleX 需要除以一个放大系数,放大系数可以由枞方向获取,
12. // 因为此时 FrameSize 和 LsSize 的上下边是重叠的
13. scale = scaleX / (frameSize.height / (float) lsSize.height);
14.} else {
15. scale = scaleY / (frameSize.width / (float) lsSize.width);
16.}
17.
18.CCLog("x: %f; y: %f; scale: %f", scaleX, scaleY, scale);
19.
20.// 根据 LsSize 和屏幕宽高比动态设定 WinSize
21.CCEGLView::sharedOpenGLView()->setDesignResolutionSize(lsSize.width * scale,
22. lsSize.height * scale, kResolutionNoBorder);

我们的实际缩放系数,是根据 scaleX 和 scaleY 的大小来判断,依据哪个方向缩放,从而在显示效果上是高对齐还是宽对齐。而想要固定是高度对其还是宽度对其,那只要换用如下方法即可:
1.// 要实现这种功能,我们需要做的就是算得 缩放系数,缩放系数由 原来的设计稍作演变即可
2.// 由于 NoBorder 的缩放是根据 scaleX 和 scaleY 的熟大熟小来判断缩放系数是参照横向还是竖向
3.// 固我们需要两个先决条件,固定的方向 和 缩放的参照方向,而得到如下算法
4.
5.// 固定高度
6.if (scaleX > scaleY)
7. scale = scaleX / (frameSize.height / (float) lsSize.height);
8.else
9. scale = scaleX / (frameSize.width / (float) lsSize.width);
10.
11.// 固定宽度
12.if (scaleX > scaleY)
13. scale = scaleY / (frameSize.height / (float) lsSize.height);
14.else
15. scale = scaleY / (frameSize.width / (float) lsSize.width);

1.

下面通过几张效果图展示 固定高度 和 固定宽度 效果:

显示效果:(NoBorder 固定高度模式 ;FrameSize = 520×320; LsSize = 480×320; WinSize = 动态获取)



显示效果:(NoBorder 固定高度模式 ;FrameSize = 520×360; LsSize = 480×320; WinSize = 动态获取)



显示效果:(NoBorder 固定宽度模式 ;FrameSize = 480×360; LsSize = 480×320; WinSize = 动态获取)



显示效果:(NoBorder 固定宽度模式 ;FrameSize = 480×300; LsSize = 480×320; WinSize = 动态获取)



如图所示,我们固定了一个方向,使得这个方向上的设计长度正好填充屏幕,而另一个方向上会有所延伸或截取,而此时如果想或者屏幕拐点,可以配合 Visible 的显示区域算得。而这也正式 cocos2d-x 2.1.3 所实现的功能,而如果你此时为了稳定而使用 2.0.4 stable 版本,那么就可以通过这种基于 LsSize 的设计方法实现 FixedHeight 与 FixedWidth。 而在将来后续版本稳定,也可以很平滑的升级到使用自带的方式替换,其显示效果一样,只是后续版本
cocos2d-x 在内部将它封装了而已。

kResolutionLeafsoar 模式的核心思想

透过现象看本质!基于固定 LsSize 的动态 WinSize 设计。之所以能够兼容这么多模式并且有所加强,在于 LsSize 在 FrameSize、WinSize、VisibleSize、VisibleOrigin等概念之外的存在,并且通过动态计算 scale 而游走于此等之间。它的存在并不依赖于 这些已有概念,而反过来,让已有的概念去依赖 LsSize 。从而保持设计上的灵活性与扩展性。

原文:无间落叶的博客
来源:泰然网
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: