书生教你cocos2d-x入门篇(三)—屏幕适配
在前几篇博文里,我们已经完成了一个打气球的游戏,可以开始可以结束。但是却还够不上打包给别人当demo看的水准。因为我们的游戏没有做屏幕适配,在480,X320以外的机器上就不能看了。
先看一下完成屏幕适配后的效果:
1. 分辨率960X640(iphone4)
2. 分辨率1024X768(ipad)
3.分辨率1136X640(iphone5)
事实上,我连800X800或是800X400这种不存在的极限分辨率都测试了,全部可以过关.学会这一手就再也不用被千奇百怪的安卓分辨率困扰了,是不是很开心呢,下面进入正题
要点:
务必把坐标系和分辨率的概念分开
分辨率是指屏幕的像素大小.譬如说iphone3的分辨率是480X320
而屏幕坐标系是逻辑概念,以屏幕左下角为原点,x,y方向延伸,构成一个屏幕坐标系,右上角和左下角构成一个矩形,这是屏幕的逻辑大小.
默认的情况下,没有做任何操作,逻辑大小和分辨率是1:1的关系
也就是说iphone3的分辨率是480X320,则逻辑坐标原点对应屏幕左下角点,逻辑坐标(480,320)对应屏幕右上角点.此时我们如果设一个精灵坐标为x,y,则在屏幕上显示的位置也是(x,y)
我把主界面的代码改回去.
我们创建一个背景图,坐标是(0,0)点,锚点是左下角,精灵创建出来的逻辑大小默认和图片像素大小一致,也就是说,我们创建了一个大小为480X320的精灵,坐标为(0,0).
那么在480X320的屏幕上是刚好的,从左下角点绘制到右上角点.
但是在960X640的屏幕上,就只能绘制在屏幕的四分之一面积,其他地方都是黑的.
这是由于我们的分辨率变大了,导致屏幕坐标系也变大了,一个逻辑上480X320大小的精灵无法覆盖全屏了.
实现一套代码适配所有分辨率的基本思路就是逻辑大小不能有大的改变,最好就根本不变(不大可能).我们写的主界面是针对逻辑大小480X320的情况下写的.那么对于960X640分辨率的屏幕,我们也认为它对应的逻辑大小还是480X320,这样逻辑上的0,0点对应屏幕左下角而逻辑坐标(480X320)的点对应像素坐标(960,640)的像素点,也就是屏幕右上角,坐标和像素的比例变成1:2了.我们的图像会被刚好放大一倍,就能适配960X640的屏幕了.
旧版本cocos2d-x的屏幕适配设置不完善,所以我简单的在所有节点最外层套了个大节点,进行缩放,实现改变逻辑点和屏幕像素比例的改变,2.2版本的cocos2d-x优化了这一块的内容.在这个基础上,加入我的屏幕适配策略,我们可以实现任何情况下的屏幕适配.
打开AppDelegate.cpp,注意我注释的这三行代码
Cocos2d-x2.2里设置屏幕适配的方法如下:
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(逻辑宽,逻辑高,适配方式);
这里有2个概念:
1,DesignResolutionSize的概念
意思就是我们在设计游戏时参考的大小,譬如说我们这个游戏是在480X320分辨率下设计的,图片也都是按照这个分辨率做的,所以我们这里传480,320,这个概念是2.2的,旧版本似乎没有,记不大清楚。
2,适配方案
ResolutionPolicy是适配方案,cocos2d-x 2.2给我们提供了这么几种 适配方案
-kResolutionExactFit(拉伸全屏)
-kResolutionNoBorder(等比缩放至无黑边)
-kResolutionShowAll(等比缩放全部显示)我们的屏幕适配策略就是在这个基础上改的
-以及新出的kResolutionFixedHeight,kResolutionFixedWidth,这两个概念跟我的屏幕适配策略相似,但是旧版本似乎没有,就没有在这2个基础上改了
下面给大家讲解几种适配的效果
1. kResolutionExactFit
kResolutionExactFit的效果是不管屏幕比例,都把屏幕的逻辑大小设为我们传入的值,
由于我们传入的是480X320,所以屏幕左下角对应0,0点,右上角对应480,320点,那么在
960X640的分辨率下显示效果如图:
刚好合适,因为960X640这个分辨率跟我们预设的480X320是等比的,等于说把我们的显示效果等比拉伸了2倍,所以刚好合适,
但是在当屏幕分辨率和预设分辨率宽高比不同时,图像就会变形了
譬如说iphone5的1136X640效果:
此时逻辑点(480,320)对应像素点(1136,640)的位置
不知道你们能不能发现,整个图像像被压扁了一样。这样的效果在大多数情况下是无法过关的,所以直接pass掉
2让我们看kResolutionNoBorder的效果:
整个视图被等比缩放了,没有变形,并且有部分图像被切掉了。
自带的Helloworld采用的就是这种适配策略,直接这样写,对于部分游戏的适配策略来说OK,至少对于我们的主界面来说,在1136X640来说还算OK。但是有部分图像被切掉了,这样的情况对大多数游戏来说是不允许的,假设我把一个按钮坐标设定在(0,0)点,那么它可能就显示不了了,就像我们的play按钮一样,差点就要被切掉了。
helloworld也给出了例子,告诉我嘛在这种情况下如果去写代码。我来说明几个概念,以及此时坐标系和分辨率的情况。引入几个专业术语,不然老是屏幕大小,分辨率这种非专业的词,讲解起来太痛苦。
WinSize是我们写代码中认为的窗口的逻辑大小,我们传入的是480X320,所以WinSize的大小为480X320。
但是此时0,0点对应的已经不是屏幕左下角,同理。480,320对应的也不是屏幕右上角。480X320所对应的区域被等比放大到如果的效果。这本例中
分辨率的高和预设大小的高的比为640/320=2;
分辨率的宽和预设大小的宽的比为1136/480=2.36;
则每个逻辑坐标点对应的像素大小按大的比值被放大到了2.36。
结果导致上下部分显示不全。
VisibleSize就是我们逻辑窗口大小中能显示在屏幕上的逻辑大小
通过 cocos2d::CCDirector::sharedDirector()->getVisibleSize()取得。
它的width或height中的一个数值和winsize的数值相等,另外一个比winsize的数值要小。
本例中它的大小是(480,小于320的某个值)
VisibleOrigin是逻辑中与于屏幕左下角对应的点。
通过cocos2d::CCDirector::sharedDirector()->getVisibleOrigin();取得
本例中,它的值是(0,某个值)
如果是在1024X768的分辨率下,会左右显示不全。
背景图显示不全我们可以接受,但是如果是重要的菜单按钮,玩家血条什么的显示不出来就不行了。
在这种情况下,如果要,确保一个图标在屏幕左下角,就不能取(0,0)了,需要取VisibleOrigin
要放在屏幕右上角,就要同时取得VisibleOrigin和VisibleSize,具体可以参考helloworld
采用这种适配方案,在某些情况下能实现屏幕适配,但是它的弊端也很明显:
1. 首先我们设置坐标要同时取VisibleOrigin和VisibleSize,这就导致了代码很繁琐,用起来不方便
2. 原本我们是在480X320的大小下,设计与编写的代码,出的图。而在这种适配方案下,屏幕对应的区域是比480X320要小,但是创建出来的精灵的逻辑大小却没有变。放在手机上的时候,策划会说“怎么所有的按钮都变大了,这么拥挤”
拿本例来说,如果我在屏幕右边竖着放了比较多的图标,譬如说5个60X60的按钮。我依然可以通过写法,控制他们相对屏幕的坐标不变,但是由于他们的总逻辑高度等于300,而我们实际显示在屏幕上的高度小于300,所以为了避免图标重叠,我们只能再去缩小图标的sacle,具体缩小多少,又需要去计算,非常不方便。
下面看第三种适配方案
3,kResolutionShowAll:
运行效果如下:
整个图像都显示出来了,但是有2条黑边。
有些朋友一看有2条黑边,就直接pass了这个方案。而实际情况,只要我们想法子去掉黑边,这个适配方案就是最优的了。先看黑边是怎么出来的,了解一些概念
这种适配方案和上一种类似,都是将winsize在屏幕上的投影区域等比放大,只不过这次是按x,y方向缩放率小的比例进行缩放,确保整个视图都能显示出来,由于我们传入的480,和320的宽高比是(4:3)和屏幕本身的宽高比不符,所以多余的两侧就出现黑边了。在1024X768上会是上下有黑边。
在这种情况下,就没有visibleSize的概念了,它的值和winsize相等。我们要做的就是想法子去除黑边。如果你尝试把背景图放大1.5倍,或者是黑边处填放别的精灵并没有作用,因为等比缩放后黑边的部分压根就不进行绘制,就好像被截掉了。
最终解决方案:
先单方向延长视图逻辑大小再等比缩放。
之所以方案三会有黑边亦或是方案2的显示不全,说到底就是我们传入的设计大小和屏幕分辨率宽高比例不同造成的。看一下我的最终适配代码:
480X320是我们的参考逻辑大小。我们按照这个大小传值给winsize会造成黑边,因此在这之前,我们先计算x,y2个方向分辨率大小和参考逻辑大小的缩放比例,scale_x和scale_y。最终我们会按最小缩放比例进行投影区域的缩放。
本例中,我们会按y方向的比例进行投影比例的缩放也就是scale_y,在那之前,我们通过这个缩放比scale_y和分辨率的宽换算出屏幕分辨率高对应的逻辑大小的宽,修正参考大小的宽的值,传入winsize。
那么最终结果等价于把黑边部分对应的逻辑大小加到了winsize里,我们的winsize的某个方向会比参考的480X320要大(因为是变大了,所以不会出现之前说的图标重叠的情况),同时另外一个方向的值不变(这就保证了最终效果和美术针对预设分辨率时的设计大小相同)。
简单说就是对逻辑大小单方向延长再进行等比缩放。
先看一下此时的效果:
确实是等比缩放了,此时我们的winsize的高还是是320,宽是568(相对最初的480增加了88)。
右边还有黑边,但是此时的黑边是由于winsize的宽增加了,导致我们480X320大小的背景图不能覆盖全屏造成的。
同理,我们的按钮和文字也不是屏幕中间,也是由于逻辑大小的winsize改变了。这里就要求我们写代码的时候不可以写逻辑坐标,要采用相对坐标来写。
cocos2d::CCSize size=cocos2d::CCDirector::sharedDirector()->getWinSize();
这样取得屏幕对应坐标大小。左下角点是(0,0),右上角点是(size.width,size.height)。
假设我们要把一个精灵放在屏幕正中,则它本身的锚点是(0.5,0.5)坐标应该为(size.width/2,size.height/2)。
下面看我修改过的主界面的代码
背景图被我放在了屏幕中间,标题的x坐标是屏幕中间size.width/2,y坐标则是7/10处,按钮在2/10处,都是用的相对坐标,没有写死成480X320相关的数。运行效果如下:
看上去跟刚才没改变winsize大小的时候似乎没区别。但是事实上,这个黑边是背景图大小不够大的问题。我们是可以在黑边处贴精灵的。假设我们把背景放大一点,就看不到黑边了。
图标和菜单的屏幕适配写法,就是根据相对坐标来设置位置。而对于全屏背景图的适配,我要先说明以下几点:
1. 各种分辨率的长宽比是不同的。用一张有内容的图在所有屏幕手机上看到的效果完全一致是不可能的。
2. 如果手上只有一张图,要么通过缩放让其有部分看不到,要么留出黑边,填充一些别的精灵,要么是拉伸导致变形。
也就是说,如果你的策划给你一张1024X768的背景图,要求你用在1136X640的手机上,并且要求全部显示出来,不能留黑边同时还不准拉伸变形。你就直接把鼠标砸他脸上。
我没有精力去画更多的背景图来适配不同分辨率,也不能容忍黑边和变形。但是我们可以把我们的背景放大一下,放大到刚好遮住黑边的大小。所以我对背景加了一个缩放处理。
代码的意思我就别解释了,功能就是让背景图放大到能遮住黑边。超出的部分我可以容忍。
在1136X640分辨率下,上下部分会有一点看不见:
在1024X768分辨率下,左右会有一部分看不见:
我是一个粗枝大叶的人,因此我对背景里的气球被截了一部分没有感觉。如果有朋友想让气球被保留,下面的纯色部分被截掉,那么就改一下背景图的锚点和位置就可以了。
游戏场景的代码我大部分写的都是相对坐标,应该是朋友们自己改一下源码的背景部分就可以了。懒得改的朋友可以直接下载我的源码,我放在附件里。
到此,我们就已经实现了一套代码一套资源适配所有分辨率的功能了。
下篇博文我会讲解如何用一套代码多套资源来适配不同分辨率。
先预习一下为什么要这么做:
手机分辨率的差距很大,iphone3是480X320,而ipad4的分辨率高达2048×1536
如果直接缩放会非常模糊。这里我们就可能要多出一套高清图。希望在ipad4这种大分辨率的机型下去加载大图,同时不想修改逻辑代码。我在下一篇博客里给大家讲解。
结尾贴一张游戏在800X800分辨率下的效果,实际是没有这种机型的哦。
本文出自 “书生教你cocos2dx” 博客,请务必保留此出处http://luoposhusheng.blog.51cto.com/8148702/1322544