cocos2d-x 游戏实战经验(三) 多分辨率的自适应(上)
前言:网上的cocos2d-x教程多为知识点的讲解,但我们学习cocos2d-x的目的是为了什么?为了做出游戏来! cocos2d-x 游戏实战经验 系列文章将分享我在开发游戏过程中遇到的问题及解决方案!
多分辨率的自适应应该是大家比较蛋疼的问题了吧,我之前去找工作面试的时候几乎每一家公司都会问我这个问题!那么什么是自适应呢?我认为就是使用1套或者2套(SD,HD)分辨率的资源自动去适应任何分辨率的设备,画面完整清晰!
一.前辈们的文章
关于多分辨率的自适应,网上你会搜到一大篇的文章,但其实都是大家转来转去的文章,有好些转载的文章甚至没有说明出处,真的很无耻唉!尤其是使用百度搜索的时候,可能得翻好几页才能找到原文,我整理了下讲的好的就这么5篇文章:
1.官网上的这篇:http://www.cocos2d-x.org/projects/cocos2d-x/wiki/Multi_resolution_support
英文文章,用心看的话应该都能看懂,讲的是cocos2d 2.x版本自带的3种适配方案,推荐看下!
2.Alex Zhou的程序世界上的这篇:http://codingnow.cn/cocos2d-x/975.html
这篇文章主要讲的也是cocos2d 2.x版本自带的3种适配方案,讲的还是蛮清晰,图文并茂还是很值得一看的!
3.我是妖怪大大的这篇:http://dualface.github.io/blog/2012/08/17/cocos2d-x-2-dot-0-multi-resolution/
我是妖怪大大的这篇文章是主要讲的是多分辨率的原理,写的非常棒,我的很多想法都是从这篇文章中学习到的,就是源码中的实现我不是很赞同,毕竟现在手动去编写UI真的不多了!
4.无间落叶大大的这篇:http://blog.leafsoar.com/archives/2013/05-10-19.html
大家看到的最多的应该就是无间落叶大大的这篇文章了,泰然上曾经顶置了一段时间的,文章讲的十分详细,从理论知识到具体实现都有讲到,如果你能耐心看完的话,受益匪浅!
5.还有K.C大大的讲cocosbuider的这篇:http://i.kimiazhu.info/?p=119
网上讲ccb多分辨率的文章真的好少啊(几乎就这一篇文章),大部分文章就一句话:按照屏幕的四个角去设置位置或者按百分比去设置,也不知道他们到底试过了没有…….这篇文章算是稀品中的战斗机了!推荐大家看一下!
哈哈哈,前面唠叨了这么多,并不是说我的文章比他们好,鲁班门前岂容我等小辈班门弄斧?
小弟只是试着从一颗屌丝的角度出发,看能否写出一篇更通俗,全面一些的文章!如果有什么错误,欢迎前辈们不吝赐教!
二.宽高比与分辨率的知识
分辨率:一般来说我们是指设备横向和纵向的像素点的熟练,如:480x320,即横向有480个像素点,纵向有320个像素点!
宽高比:一般iphone我们是用分辨率的除它们的最大公约数得出的!如:480x320的最大公约数是160,这样它们的宽高比就是3:2! android的分辨率太多太过诡异,只能单独google了!
这样,我整理了下常用设备信息,如下:
设备 | 分辨率 | 宽高比 | ||||
iphone4/4s | 960x640 | 3:2 | ||||
iphone5 | 1136x640 | 16:9 | ||||
ipad1/ipad2/ipadmini | 1024x768 | 4:3 | ||||
ipad3/ipad4 | 2048x1536 | android 1 | 800x480 | 5:3 | ||
android 2 | 854x480 | android 3 | 1280x720 | android 4 | 960x540 | 16:9 |
表1
三.cocos2d-x 2.x版本自带的自适应方法分析
请看下图,这是在DesignResolutionSize为480x320,设备尺寸480x320正常效果下的HelloCpp:
1.开启自适应
大家打开HelloCpp工程的AppDelegate.cpp文件中的applicationDidFinishLaunching()函数,会发现下面这行代码:
1 2 |
// Set the design resolution pEGLView->setDesignResolutionSize(designResolutionSize.width,designResolutionSize.height,kResolutionNoBorder); |
这个函数是用来设置我们的设计尺寸和适配模式的:
1).什么是设计尺寸(DesignResolutionSize)呢?
不同的设备拥有不同的分辨率,我们不可能为每一个分辨率都去设计一种界面,准备一套素材,所以我们会有一个设计尺寸,然后将设计尺寸以某种规则投影到实际尺寸上去!更详细解释请移步上文中的第一篇官方的文章!
2).适配方案(ResolutionPolicy)有哪些?
kResolutionExactFit 强制拉伸到全屏幕显示,如果设计宽高比和实际宽高比不同时画面会变形扭曲!如下图:
kResolutionShowAll按照宽和高的缩放系数的较小值去缩放画面,因为是等比缩放,所以不会失真,但是较大缩放系数的方向会出现黑边!如下图:
kResolutionNoBorder 按照宽和高的缩放系数的较大值去缩放画面,这样也不会失真,但是较小缩放系数的方向因为放大的过多会导致超出屏幕边界!如下图:
这三种适配策略是最简单的,如果必须在这3中方案中选择一个的话,我会选择kResolutionShowAll,它的效果相对来说是可以理解的,而且也不用做任何额外的工作!如果想要达到更好的效果的话呢,我们就需要费点心思了!
2.默认自适应详解
好吧,让我们来分析下上面的这几种适配方式,先看下面这行代码(CCEGLViewProtocol::setDesignResolutionSize函数):
m_fScaleX = (float)m_obScreenSize.width / m_obDesignResolutionSize.width;
m_fScaleY = (float)m_obScreenSize.height / m_obDesignResolutionSize.height;
从这个片段中可以看出cocos2d-x会用屏幕的实际尺寸(m_obScreenSize)除设计尺寸(m_obDesignResolutionSize)得到x(横)方向和y(纵)方向上的缩放系数!
1 2 3 |
// calculate the rect of viewport float viewPortW = m_obDesignResolutionSize.width * m_fScaleX; float viewPortH = m_obDesignResolutionSize.height * m_fScaleY; |
这段代码是用来计算视口大小(可视区域外的东西是看不到的)的,不难得出如果m_fScaleX,m_fScaleY不做处理的话视口大小(viewPort)会等与屏幕的尺寸(m_obScreenSize).
1).为什么kResolutionExactFit会出现变形失真呢?
如果我们算出的m_fScaleX不等于m_fScaleY的话,将画面映射时会给宽高乘不同的缩放系数,这样就不是等比缩放了,自然会出现变形的问题!
2).为什么kResolutionShowAll会有黑边呢?
1 2 3 4 |
if (resolutionPolicy == kResolutionShowAll) { m_fScaleX = m_fScaleY = MIN(m_fScaleX,m_fScaleY); } |
从代码中可以看到kResolutionShowAll选择了较小的缩放系数作为最终的缩放因子,这样m_fScaleX恒等于m_fScaleY,不会出现变形扭曲的问题,但是原本缩放系数较大的方向因为使用了较小的缩放系数导致算出的视口尺寸的宽或高(viewPortW or viewPortH)小于屏幕的尺寸的宽或高(m_obScreenSize.width or height),于是我们的画面无法映射到全屏幕,因此会出现黑边!
3).为什么kResolutionNoBorder绘制的东西为什么会超出边界呢?
if (resolutionPolicy == kResolutionShowAll) { m_fScaleX = m_fScaleY = MAX(m_fScaleX,m_fScaleY); } |
从代码中可以看到kResolutionNoBorder选择了较大的缩放系数作为最终的缩放因子,所以较小缩放系数方向因为使用了较大的缩放系数从而导致算出的视口尺寸的宽或高(viewPortW or viewPortH)大于屏幕的尺寸的宽或高(m_obScreenSize.width or height),于是我们的画面的某个方向就映射到了屏幕外面,因此我们看到的效果就是画面上的有些东西一半在屏幕中,一半在屏幕外!
四.思路分析1.去除黑边
让我们看下下面这段代码(节选自2.14版本CCEGLViewProtocol::setDesignResolutionSize函数):
1 2 3 4 5 6 7 8 9 10 |
//这是新增加的以高度为基准的适配策略,将x方向取的缩放系数设置为y方向的缩放系数 if ( resolutionPolicy == kResolutionFixedHeight) { m_fScaleX = m_fScaleY; m_obDesignResolutionSize.width = ceilf(m_obScreenSize.width/m_fScaleX); } //这是新增加的以宽度为基准的适配策略,将y方向取的缩放系数设置为x方向的缩放系数 if ( resolutionPolicy == kResolutionFixedWidth) { m_fScaleY = m_fScaleX; m_obDesignResolutionSize.height = ceilf(m_obScreenSize.height/m_fScaleY); } |
细心的你一定会发现当resolutionPolicy == kResolutionFixedWidth或kResolutionFixedHeight时,视口大小会横等于屏幕尺寸(m_obScreenSize),这也正是去除黑边的办法(如果你的版本没有kResolutionFixedWidth和kResolutionFixedHeight的话,将相关的东西复制过去即可),这样我们就可以把原来是黑边废物利用了!
这里有两种方式:
1).将黑边部分用图片填充,那里仍然不能有触摸事件,这样游戏内容的可用空间并没有增加,只是美观了些许!比如MT:
2).显示更多的游戏内容,一般用在原本游戏内容就大于视口的游戏中!比如COC类游戏:
这两种模式,一种是固定设计时的高,一种是固定设计时的宽,如果你的游戏是横屏的,我们肯定希望无用区域在左右两边,即固定高度(kResolutionFixedHeight),而不是上下,因为上下本来就很小!竖屏游戏则反之!
2.不同的东西有不同的适配策略
1).背景层
一般来说我们并不十分关心背景层上的东西,我们只希望它能够平铺整个画面
- 因此我们可以准备一张很大的背景图,足以覆盖任何设备的屏幕!
- 准备几张用来填充多余区域的图片,贴在多余区域即可!
- 考虑使用CCLayerColor,CCLayerGradient作为背景,这样会自动填充满整个窗口的!
我选择的是1的方法,这样与背景的融合图更高(点击查看大图):
这里才是我们要关心的,这里一般会有两种做法:
- 按照实际的屏幕尺寸的百分比去设置每个元素的位置,这样做的代价非常大!
- 如果你的游戏内容就大于屏幕尺寸的话,恭喜你,你不用做任何操作,只是玩家看到的东西更多罢了!
- 还是按照正常的方式去设置元素位置,只不过将所有的元素加入到一个节点上,最后将这个节点设置在屏幕中心位置!
我采用的是3的方法来的,所有的元素都加入到CGameLaye身上:
- 一种做法是按照”米”这样9个基准点去设置相对位置(上,右上,右,右下,下,左下,左,左上,中心)!
- 按照屏幕的%百分比去设置位置.
我采用的是第二种做法,因为我的UI是在cocosbuilder中编辑的,大家可以对比下面两张图:
在分辨率差距很大的情况下,UI在两张图片上的相对位置是不变的!