一直以来,在 cocos2d-x 平台中如何用一套资源适配全屏幕都是每个程序的梦想。随手百度都可以搜索到很多关于 cocos2dx 适配的文章,但是看完之后的感觉仍然是不知道如何具体操作,所以自己参考网上其他人的帖子,并结合 cocos2dx 源码,自己验证了一番在此做个总结。如有不正确的地方请大家提出讨论。
首先是做 Android 的同学们先暂时忘掉 dpi 的概念,无论是几寸屏多少 dpi,在cocos2dx 里面只关心像素就可以了。
cocos2dx里面提供几种概念,FrameSize/WinSize/WinPixel/VisibleSize/VisibleOrigin,那么就一次性都输出出来看看这些值都是代表什么。
测试环境
iPhone6 (640x1136)
iPhone6 plus (1242x2208)
Nexus4 (768x1184)
在 iPhone6的模拟器上运行获得如下结果
cocos2d: FrameSize width = 640.000000 height = 1136.000000
cocos2d: WinSize width = 640.000000 height = 1136.000000
cocos2d: WinPixel width = 640.000000 height = 1136.000000
cocos2d: VisibleSize width = 640.000000 height = 1136.000000
cocos2d: VisibleOrigin x = 0.000000 y = 0.000000
在 Nexus4上输出结果如下
然后,我设置了一下
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
glview->setDesignResolutionSize(1242.0f, 2208.0f, ResolutionPolicy::FIXED_HEIGHT);
再次输出上面那些值
cocos2d: frameSize width = 640.000000 height = 1136.000000
cocos2d: WinSize width = 1244.000000 height = 2208.000000
cocos2d: winPixel width = 1244.000000 height = 2208.000000
cocos2d: visibleSize width = 1244.000000 height = 2208.000000
cocos2d: visibleOrigin x = 0.000000 y = 0.000000
所以我们得出一个结论 FrameSize 是屏幕的真实尺寸,WinSize = WinPixel = VisibleSize, 受我们设置的 DesignResolutionSize影响。这里没有对 WinSize 及 VisibleSize 做更多的测试,通过设置可能会存在不一致的情况,这里先不对这些情况讨论。
接下来我们看一下 ResolutionPolicy::FIXED_HEIGHT 相关的设置。
找到源码:void GLView::updateDesignResolutionSize()函数
_scaleX = (float)_screenSize.width / _designResolutionSize.width;
_scaleY = (float)_screenSize.height / _designResolutionSize.height;
if (_resolutionPolicy == ResolutionPolicy::NO_BORDER)
{
_scaleX = _scaleY = MAX(_scaleX, _scaleY);
}
else if (_resolutionPolicy == ResolutionPolicy::SHOW_ALL)
{
_scaleX = _scaleY = MIN(_scaleX, _scaleY);
}
else if ( _resolutionPolicy == ResolutionPolicy::FIXED_HEIGHT) {
_scaleX = _scaleY;
_designResolutionSize.width = ceilf(_screenSize.width/_scaleX);
}
else if ( _resolutionPolicy == ResolutionPolicy::FIXED_WIDTH) {
_scaleY = _scaleX;
_designResolutionSize.height = ceilf(_screenSize.height/_scaleY);
}
ResolutionPolicy::EXACT_FIT--全屏,如果屏幕比例跟设计尺寸的比例不相等,则图片会被变形拉伸。
ResolutionPolicy::NO_BORDER--无黑边模式,如果屏幕比例跟设计尺寸的比例不相等,则一定有一个方向被裁剪。
ResolutionPolicy::SHOW_ALL--全屏模式,如果屏幕比例跟设计尺寸的比例不相等,则一定有一个方向显示黑边。
ResolutionPolicy::FIXED_HEIGHT--按高度适配,如果屏幕比例跟设计尺寸的比例不相等,则横向要么被裁剪,要么出黑边。
ResolutionPolicy::FIXED_WIDTH--按宽度适配,如果屏幕比例跟设计尺寸的比例不相等,则纵向要么被裁剪,要么出黑边。
以上都是废话,人家注释里写的清清楚楚。
说了这么多最想知道的问题还是没有得到解答,我该用哪个模式?设计分辨率应该设置多大?美术应该切多大的图?
下面根据我个人的理解,对这几个问题一一解答。
1,模式的选择。
如果能忍受黑边,推荐 SHOW_ALL模式。这样美术最省力,严格按照DesignResolutionSize画就可以了。
如果能忍受变形拉伸,就直接EXACT_FIT,但是我个人是不接受变形拉伸的。朋友之前的一款游戏采用这个模式,素材在设计的时候比例介于16:9跟4:3之间,所以拉伸的时候变形也不是很严重,大家可以参考。
如果你采用剩下的3种模式一定要提前跟美术沟通,因为可能会产生裁剪。
NO_BORDER模式由于不确定哪个方向产生裁剪,美术同学可能会疯掉。需要两个方向都预留出来一部分内容,即便被裁剪掉也不影响整体效果即可。
如果采用 FIEXD_HEIGHT模式,请美术同学在横向上预留出一部分内容以防被裁剪。
如果采用 FIEXD_WIDTH 模式,请美术同学在纵向上预留出一部分内容以防被裁剪。
2,DesignResolutionSize。
这个设计分辨率直接影响包的尺寸,所以要慎重。
苹果还是很良心的,6跟6p 的横纵比是相等的,所以只要DesignResolutionSize的横纵比是0.563,选任何上面一个模式都是全屏不变形无黑边无裁剪。
这次提供的素材是1242x2208,显示到 Nexus4的时候两侧出现了黑边(setDesignResolutionSize(1242.0f, 2208.0f, ResolutionPolicy::FIXED_HEIGHT);)
原因是这个模式下 WinSize是1433,2208。
关于如何切图,
先根据游戏的类型及包尺寸的要求,决定适配模式。程序同学请事先同美术沟通好,再决定一个DesignResolutionSize,核心的元素需要画在这个尺寸之内。另外根据模式再在需要的方向上留一定的被裁剪空间。
FIXED_HEIGHT 模式素材提供的横纵比略大一些,两侧留一些被裁剪的空间,就可比较好的适配各种屏幕。setDesignResolutionSize 函数的第一个参数只要不为0即可,计算的时候会被无视掉。
FIXED_WIDTH 模式素材提供的横纵比略大一些,上下预留被裁剪的空间。setDesignResolutionSize 函数的第二个参数只要不为0即可,计算的时候会被无视掉。
SHOW_ALL最简单,美术不需要预留,严格按DesignResolutionSize切图,请容忍黑边。
NO_BORDER,美术两个方向都要预留被裁剪的空间。
EXACT_FIT,横纵比取折衷大小,容忍变形。
采用以上的方法之后,图片应该不需要在代码中二次缩放,因为 cocos2dx 已经替你进行缩放了。
那么程序还需要解决一个问题,界面元素与元素之间的距离。
FIXED_HEIGHT 模式下纵向位置固定为设置的 DesignResolutionSize,所以请美术直接给出高度就可以直接使用,不论上边距还是下边距不需要适配。但是横向上像素数量不固定,请以屏幕中轴做参考向两侧距离适配。FIXED_WIDTH 同理横向不需要特别适配,纵向以中轴做参考向两侧适配。
SHOW_ALL/NO_BORDER/EXACT_FIT 三个模式横向纵向最好都以中轴做参考适配。当然有些时候按钮跟背景图片位置不关联,这个时候只要保证不出屏幕就可以了。
由于时间关系没有进行更多机型的适配测试,难免有遗漏或者错误的地方,欢迎大家把自己实际测试的情况也分享下。