精通Cocos2d-x游戏开发(进阶卷)第5章 分辨率适配

注1:本书作者王永宝,出版时间:2017-03-01。
注2:《Cocos2d-x 2.x、3.x读书摘要》这篇日志过于凌乱,计划按照知识点拆分细化,故转载王永宝这本书的这一章节。
第5章 分辨率适配
5.1 Cocos2d-x适配策略
5.1.1 分辨率适配策略
5.1.2 坐标编码
5.1.3 OpenGL窗口与可视化窗口
5.1.4 setDesignResolutionSize详解
5.2 分辨率适配经验
5.2.1 宽度或高度锁定
5.2.2 计算设计分辨率
5.2.3 场景固定内容
5.2.4 经验小结
5.3 CocoStudio分辨率适配
  当在不同的分辨率下运行程序时,就会碰到分辨率适配的问题,如出现黑边、界面的一部分显示在屏幕外,我们希望程序在不同的分辨率下运行都能有良好的表现。在Cocos2d-x中,可以通过选择合适的分辨率适配策略,结合合适的坐标编码,适配各种不同的分辨率。而灵活使用Cocos2d-x的分辨率适配策略,还可以解决各种分辨率适配的难题。本章主要介绍以下内容:
  * Cocos2d-x适配策略。
  * 分辨率适配经验。
  * CocoStudio分辨率适配。
  5.1 Cocos2d-x适配策略
  可以在Cocos2d-x中调用CCEGLView的setDesignResolutionSize方法设置游戏的分辨率策略,以及我们的设计分辨率。
  setDesignResolutionSize()方法包含3个参数,分别是设计分辨率的宽和高,以及分辨率的适配策略。下面这行代码设置了960×640的设计分辨率,并使用了SHOW_ALL分辨率适配策略。
  Director::getInstance()->getOpenGLView()->setDesignResolutionSize(960, 640, ResolutionPolicy::SHOW_ALL);
  5.1.1 分辨率适配策略
  Cocos2d-x的分辨率适配一般不是为每一种分辨率设计一种布局方案,而是在一种分辨率下进行设计(也就是设计分辨率),然后通过分辨率适配策略,让程序能够适应不同的分辨率。Cocos2d-x提供以下5种分辨率适配策略。
  * EXACT_FIT以设置的分辨率为标准,按照该分辨率对x和y进行拉伸。
  * NO_BORDER不留黑边,不拉伸,等比缩放,有一个方向(上下或左右)可能超出屏幕。
  * SHOW_ALL设置的分辨率区域内全部可见,但上下左右都可能出现黑边。
  * FIXED_HEIGHT锁定分辨率的高度,宽度不管,可能出现黑边也可能超出屏幕。
  * FIXED_WIDTH锁定分辨率的宽度,高度不管,可能出现黑边也可能超出屏幕。
  通过图5-1~图5-3可以直观地了解到在不同分辨率下,各个分辨率适配策略的表现。以960×640为设计分辨率,然后通过调整窗口的实际分辨率,选择不同的适配模式进行观察。在PC上调用Director的setFrameSize()方法可以自定义窗口的尺寸,但不要在移动设备上设置FrameSize。
  首先是EXACT_FIT模式,当在不同的分辨率下运行时,界面的宽和高都会根据我们的设计分辨率进行缩放,例如,当设计分辨率是100×200,在200×300的分辨率下运行时,宽会放大2.0,高会放大1.5,当实际分辨率小于设计分辨率时,Cocos2d-x又会相应地缩小界面使其适配,如图5-1所示。
  图5-1 EXACT_FIT模式
  NO_BORDER模式下会根据实际分辨率进行等比缩放,不留黑边。首先按照EXACT_FIT模式的缩放规则计算出宽和高的缩放值,按照*高的缩放值进行等比缩放。当实际分辨率无法完整放下缩放后的界面时,会有一部分内容显示在屏幕外,如图5-1所示,当界面以NO_BORDER模式进行适配时,红色边框为界面的完整内容,红色边框左下角的红色原点为OpenGL窗口的原点坐标,如图5-2所示。
  图5-2 NO_BORDER模式
  SHOW_ALL模式下会根据实际分辨率进行等比缩放,完全显示界面的完整内容,与NO_BORDER模式相反,其会先按照EXACT_FIT模式的缩放规则计算出宽和高的缩放值,按照*低的缩放值进行等比缩放。由于是按照*小的分辨率进行缩放,所以左右和上下都有可能出现黑边,图5-3右侧图片中的红点处为OpenGL窗口的原点坐标,如图5-3所示。
  FIXED_HEIGHT和FIXED_WIDTH模式比较类似,它们会将高度或宽度锁定,按照高度或宽度进行等比缩放,另外一个方向既可能超出,也有可能留下黑边。这两种模式会先按照EXACT_FIT模式的缩放规则计算出宽和高的缩放值,FIXED_HEIGHT取高度缩放值进行等比缩放,保证设计分辨率的高度刚好铺满设计分辨率,FIXED_WIDTH取宽度进行等比缩放,保证设计分辨率的宽度刚好铺满设计分辨率。
  图5-3 SHOW_ALL模式
  5.1.2 坐标编码
  当我们的程序在不同的分辨率下运行时,setDesignResolutionSize()方法会对整个程序按照适配策略根据设计分辨率和实际分辨率进行缩放。在对坐标进行编码时,需要使用相对坐标编码,而根据窗口尺寸可以进行相对坐标的编码,如希望将一个节点放置在屏幕的正中间,就需要将其坐标的x和y分别设置为窗口尺寸的宽和高的1/2。相对左下角原点的坐标则可以直接使用绝对坐标,设置相对位置可以使得程序在不同的分辨率下运行,我们的对象都能够显示在正确的位置上。
  在使用相对坐标编码时,Director单例中有几个方法可以获取尺寸,下面了解一下这几个获取尺寸相关的方法。
  * getWinSize,获取OpenGL窗口的单位尺寸。【之前认为getWinSize就代表设计分辨率大小,是不严谨的
  * getWinSizeInPixels,获取OpenGL窗口的实际像素尺寸。
  * getVisibleSize,获取可视窗口的尺寸。
  * getVisibleOrigin,获取可视窗口左下角坐标的位置。
  另外GLView对象还提供了以下两个接口来获取其他的尺寸。
  * getFrameSize,获取设备或窗口的尺寸。
  * getDesignResolutionSize,获取设置的设计分辨率。
  如图5-4直观地演示了上面描述的各种尺寸,WinSize和WinSizeInPixels分别是当前整个OpenGL窗口的单位尺寸和像素尺寸。VisibleSize和VisibleOrigin可以共同构成当前窗口中实际可见部分内容的矩形范围,FrameSize为当前窗口或设备的真实尺寸。
  图5-4 WinSize与VisibleSize
  * WinSize分别为图5-4中左右两图的红色框范围,虽然看上去范围不同,但这是一个单位尺寸,所以值并没有变化,也就是原图尺寸960×640,一般等同于设计分辨率的尺寸,也是OpenGL窗口的单位尺寸。
  * WinSizeInPixels也对应图5-4两图中的红色框范围,但这个尺寸为实际占用的像素尺寸,所以在不同分辨率下有不同的值(程序逻辑中使用的坐标是单位尺寸,而非像素尺寸)。
  * VisibleSize表示可视内容的尺寸,在图5-4左图中为红色框范围,右图则为黄色框范围,也就是可以看到的有内容的显示区域尺寸。
  * VisibleOrigin表示可视内容的左下角坐标,分别是左右图中左下角的红点的位置,左图中OpenGL窗口原点的坐标与红点重叠,而右图中OpenGL窗口的原点为红色框的左下角,VisibleOrigin的Y轴比原点高了64个像素。
  * FrameSize为窗口或设备的实际尺寸,也就是图5-4中两个窗口的窗口大小1200×640。
  Cocos2d-x推荐使用VisibleSize和VisibleOrigin进行相对位置的计算,就是因为根据它们来计算可以保证我们的对象能够处于可视范围中。
  WinSize和(0,0)坐标构成了OpenGL窗口,VisibleSize和VisibleOrigin构成了可视窗口,可视窗口不会大于OpenGL窗口,因为OpenGL窗口以外的内容都是不可见的!但OpenGL窗口范围内的对象并不一定可见,如当屏幕窗口容不下OpenGL窗口时。可视窗口可以理解为OpenGL窗口和设备实际分辨率窗口相交的矩形区域。
  5.1.3 OpenGL窗口与可视化窗口
  绝大部分的游戏都可以使用FIXED_HEIGHT或FIXED_WIDTH模式来实现简单的分辨率适配,只需要在背景上将可能有黑边的内容进行填充即可。这两种模式与SHOW_ALL有些类似,就是都可能导致黑边或超出,但有一种本质区别,即它们的OpenGL窗口不同,这对于坐标编码是有巨大影响的!OpenGL窗口不同,说的是原点位置不同,WinSize、VisibleSize不同。
  在图5-5中,使用FIXED_HEIGHT和SHOW_ALL模式都是同样的表现,左右都会有同样的黑边,但FIXED_HEIGHT模式下的OpenGL窗口和可视化窗口对应的是图5-5中的黄色矩形区域(包括左右的黑边),而SHOW_ALL模式下的OpenGL窗口和可视化窗口对应的是图5-5中的红色矩形区域(不包括黑边)。
  图5-5 OpenGL窗口和可视化窗口
  *直观的表现就是,在(0,0)的位置创建一个对象,FIXED_HEIGHT模式下会出现在黄色矩形区域的左下角,而SHOW_ALL模式下会出现在红色矩形区域的左下角。SHOW_ALL模式下的黑边部分是不会出现任何显示对象的,因为不在OpenGL窗口中。而FIXED_HEIGHT模式则可以正常显示,所以只要背景图片大一些,将左右的黑边区域遮住,即可简单地解决适配黑边的问题。正是由于这种实现方式,FIXED_HEIGHT和FIXED_WIDTH模式才可以在背景上对可能有黑边的内容进行填充来解决黑边的问题。
  5.1.4 setDesignResolutionSize详解
  在了解了适配策略和Cocos2d-x的各种尺寸之后,下面来进一步了解setDesign- ResolutionSize()方法,setDesignResolutionSize()方法中会简单判断传入的设计分辨率的宽度和高度,以及分辨率适配策略,将这些参数保存并调用updateDesignResolutionSize()方法更新分辨率。
  void GLView::setDesignResolutionSize(float width, float height,
  ResolutionPolicy resolutionPolicy)
  {
  CCASSERT(resolutionPolicy != ResolutionPolicy::UNKNOWN, "should set
  resolutionPolicy");
  if (width == 0.0f || height == 0.0f)
  {
  return;
  }
  _designResolutionSize.setSize(width, height);
  _resolutionPolicy = resolutionPolicy;
  updateDesignResolutionSize();
  }
  在updateDesignResolutionSize()方法中,首先根据屏幕尺寸和设计分辨率计算出x和y方向的缩放值,然后根据分辨率适配模式选择*终的缩放值,计算完缩放值之后,再计算视口的大小。
  void GLView::updateDesignResolutionSize()
  {
  if (_screenSize.width > 0 && _screenSize.height > 0
  && _designResolutionSize.width > 0 && _designResolutionSize.height
  > 0)
  {
  _scaleX = (float)_screenSize.width / _designResolutionSize.width;
  _scaleY = (float)_screenSize.height / _designResolutionSize.height;
  //NO_BORDER模式下取*大的缩放值等比缩放
  if (_resolutionPolicy == ResolutionPolicy::NO_BORDER)
  {
  _scaleX = _scaleY = MAX(_scaleX, _scaleY);
  }
  //SHOW_ALL模式下取*小的缩放值等比缩放
  else if (_resolutionPolicy == ResolutionPolicy::SHOW_ALL)
  {
  _scaleX = _scaleY = MIN(_scaleX, _scaleY);
  }
  //FIXED_HEIGHT模式下取y轴缩放值等比缩放,并将设计分辨率的宽度调整为全屏的
  宽度
  else if ( _resolutionPolicy == ResolutionPolicy::FIXED_HEIGHT) {
  _scaleX = _scaleY;
  _designResolutionSize.width = ceilf(_screenSize.width/_scaleX);
  }
  //FIXED_WIDTH模式下取x轴缩放值等比缩放,并将设计分辨率的高度调整为全屏的
  高度
  else if ( _resolutionPolicy == ResolutionPolicy::FIXED_WIDTH) {
  _scaleY = _scaleX;
  _designResolutionSize.height = ceilf(_screenSize.height/_
  scaleY);
  }
  //计算视口的尺寸,并设置视口的矩形区域
  float viewPortW = _designResolutionSize.width * _scaleX;
  float viewPortH = _designResolutionSize.height * _scaleY;
  _viewPortRect.setRect((_screenSize.width - viewPortW) / 2,
  (_screenSize.height - viewPortH) / 2, viewPortW, viewPortH);
  //重置Director的成员变量来适应可视化矩形
  auto director = Director::getInstance();
  director->_winSizeInPoints = getDesignResolutionSize();【看了引擎代码之后,getWinSize应该代表设计分辨率适配之后的大小】
  director->_isStatusLabelUpdated = true;
  director->setGLDefaultValues();
  }
  }
  5.2 分辨率适配经验
  5.2.1 宽度或高度锁定
  我们希望所有的机型都能够很完美地适配,不要拉伸!不要黑边!FIXED_HEIGHT或FIXED_WIDTH模式是比较容易做到的。
  要做到上面的要求,需要选取一个范围,即要适配的分辨率比例的范围。我们都知道iPhone 5的比例非常长,应该没有什么机器比这个比例更长的了,所以一般笔者将iPhone 5的比例设置为要适配的极限比例,也就是说,如果有比iPhone 5更长的手机,笔者就基本放弃这个机型了。接下来选择一个*扁的比例,一般在平板电脑上的比例会更扁一些,纵观主流的分辨率,基本都在iPhone 5和iPad之间,所以笔者习惯将要适配的比例在iPhone 5到iPad之间(这里的是以横屏游戏为例,如果是竖屏游戏,只需要把宽和高对调一下即可)。也就是说,假设选择固定高度的FIXED_HEIGHT模式,那么就要选择一个*宽和*窄的宽度。
  选择好比例之后,需要好好设计一下游戏内容,以方便不同分辨率的适配,主要包含游戏的背景、游戏的内容区域,以及UI等。
  背景的设计是非常重要的一步,因为背景设计的好坏,直接决定了是否有黑边,以及游戏内容的布局。首先,背景图需要有多大?其次,游戏区域只能有多大?这些问题需要根据游戏的内容来设计。
  如果是一个横屏的斗地主游戏,可以将游戏区域放在游戏的正中间,游戏内容可以根据游戏区域的原点为相对坐标计算,这时候两边各有一部分区域是可裁剪的。
  如果是一个竖屏的雷电射击游戏,可以将游戏区域放在正下方,上方是可裁剪区域,敌人从上方出现,可以将上方的可裁剪区域也纳入游戏区域,敌机根据左上角为原点设置相对坐标。
  如果是一个消除类游戏,如果是横屏的,一般把游戏区域放在正中间,左右两边裁剪;如果是竖屏的,一般也把游戏区域放在正中间,上下两边是可裁剪区域。
  5.2.2 计算设计分辨率
  使用FIXED_HEIGHT或FIXED_WIDTH模式结合一个比较大的背景,一般可以解决大部分游戏的分辨率适配问题,但如果游戏背景并不是锁定宽度或高度的,那么就需要选择其他的分辨率适配策略了。
  下面介绍一个简单的适配示例,如图5-6所示。首先背景尺寸是1280×800,这个分辨率没有任何讲究,是美工随便给出的一个分辨率,是一个足够大的尺寸,宽度和高度都不进行锁定,而是根据实际设备的分辨率进行动态调整,这个分辨率尽管不怎么标准,但还是可以用来完成完美适配。
  图5-6 分辨率480×320
  游戏中有两部分UI,主界面的菜单面板是居中对齐,getWinSize得到的大小的一半即是居中的位置,面板设置锚点为(0.5,0.5),并设置居中的位置即可。第二部分UI是顶部的信息栏,信息栏的位置是靠上居中,信息栏设置锚点为(0.5,1.0),然后设置getWinSize的width×0.5f为x坐标,height为y坐标即可。
  在这里选择的策略是SHOW_ALL,但是设计分辨率需要动态计算出来(一般的代码这里都会设置一个分辨率),因为要使用好1280×800的背景图,关键有以下几点:
  * 不拉伸,不留黑边。
  * 根据手机的分辨率调整可视区域(设置的标准分辨率)。
  * 当目标分辨率比背景还要宽时,把目标分辨率等比缩小,直到分辨率内容全部在可视区域内。
  * 当目标分辨率比背景还要高时,把目标分辨率等比缩小,直到分辨率内容全部在可视区域内。
  * 当目标分辨率比背景小时,把目标分辨率等比放大,直到分辨率内容全部在可视区域内。
  我们的背景分辨率是1280×800,720是笔者自己定义的一个值,因为笔者不希望整个背景太宽,所以进行了限制,然后根据实际的分辨率与预期分辨率计算出期望的高和宽(要么高变,要么宽变),代码如下。
  float height = 800.0f;
  float width = 1280.0f;
  float ratio = sz.width / sz.height;
  float maxratio = width / height;
  float minratio = width / 720.0f;
  if (ratio > maxratio)
  {
  //比*宽的还要宽
  height = width / ratio;
  }
  else if(ratio < minratio)
  {
  //比*窄的还要窄
  width = height * ratio;
  ……
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页