cocos2dx的屏幕适配

1.版本

cocos3.9

2.屏幕适配

cocos有5种屏幕适配策略。
EXACT_ALL:非等比缩放,使画面充满整个屏幕,但会变形。
NO_BORDER:等比缩放,也会使画面充满整个屏幕,不会变形,但SS/DS比较大的一边刚好充满整个屏幕,而另一边就会超出屏幕,被截断。
SHOW_ALL:等比缩放,会让整个画面都显示出来,不会变形,不会截断,SS/DS比较小的一边刚好充满整个屏幕,而另一边往往会有黑边。
FIXED_HEIGHT:等比缩放,不会变形,高刚好充满整个屏幕,而宽可能有黑边也可能被截断。
FIXED_WIDTH:等比缩放,不会变形,宽刚好充满整个屏幕,而高可能有黑边也可能被截断。

5种适配策略效果如图所示:
在这里插入图片描述
cocos里面有四种尺寸概念,分别是VisibleSizeWinSizeFrameSizeDesignSize

FrameSize:
指的是屏幕的实际分辨率,通过Director::getInstance()->getOpenGLView()->getFrameSize()获取。在调试的时候可以通过Director::getInstance()->getOpenGLView()->setFrameSize()来设置。

DesignSize:
指的是设计分辨率,通过Director::getInstance()->getOpenGLView()->setDesignResolutionSize来设置。这个也是我们设置屏幕适配策略的手段。
setDesignResolutionSize会调用updateDesignResolutionSize来调整设计分辨率,


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;
        
        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);
        }
        
        // calculate the rect of viewport
        float viewPortW = _designResolutionSize.width * _scaleX;
        float viewPortH = _designResolutionSize.height * _scaleY;
        
        _viewPortRect.setRect((_screenSize.width - viewPortW) / 2, (_screenSize.height - viewPortH) / 2, viewPortW, viewPortH);
        
        // reset director's member variables to fit visible rect
        auto director = Director::getInstance();
        director->_winSizeInPoints = getDesignResolutionSize();
        director->createStatsLabel();
        director->setGLDefaultValues();
    }
}

由代码可以看到除了FIXED_HEIGHTFIXED_WIDTH外其他策略都没有调整设计分辨率的大小。

WinSize
指的是OpenGL视口转化到设计分辨率下的尺寸,通俗的讲就是OpenGL画布在我们设计世界的大小,它实际上是经过引擎调整后的设计尺寸。

const Size& Director::getWinSize(void) const
{
    return _winSizeInPoints;
}
void GLView::updateDesignResolutionSize()
{
		...
        // reset director's member variables to fit visible rect
        auto director = Director::getInstance();
        director->_winSizeInPoints = getDesignResolutionSize();  //将DesignSize赋值给WinSize
        director->createStatsLabel();
        director->setGLDefaultValues();
    }
}

VisibleSize
指的是OpenGL视口转化到设计分辨率下我们可以看到的最大区域的大小。并不是说OpenGL的画布有多大我们就可以看到多大,它有可能超出屏幕外。所以VisibleSize总是小于等于DesignSize
其实也就是说只要你用大小等于VisibleSize的图片,它就能把整个游戏画面填满。(填满不是指没有黑边,黑边有两种情况,一种是在VisibleSize的范围内,但因为在黑边的范围内没有渲染元素,所以才显示黑色,如果你的图片足够大,是不会有黑边的,如FIXED_HEIGHTFIXED_WIDTH。另一种是超出了VisibleSize的范围,如SHOW_ALL,在这种情况下就算在这个范围里有渲染元素,它显示出来也是黑边,因为超出了OpenGL画布的范围。)

Size Director::getVisibleSize() const
{
    if (_openGLView)
    {
        return _openGLView->getVisibleSize();
    }
    else
    {
        return Size::ZERO;
    }
}
Size GLView::getVisibleSize() const
{
    if (_resolutionPolicy == ResolutionPolicy::NO_BORDER)
    {
        return Size(_screenSize.width/_scaleX, _screenSize.height/_scaleY);
    }
    else 
    {
        return _designResolutionSize;
    }
}
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;
        
        if (_resolutionPolicy == ResolutionPolicy::NO_BORDER)
        {
            _scaleX = _scaleY = MAX(_scaleX, _scaleY);
        }
        ...

结合上面的三个代码也能印证VisibleSizeDesignSize
的猜测。

3.视口和投影矩阵的设置

3.1.视口设置

cocos会用WinSize来设置OpenGL视口的大小。cocos内部又会把它转换成现实尺寸的大小。

void Director::setViewport()
{
    if (_openGLView)
    {
        _openGLView->setViewPortInPoints(0, 0, _winSizeInPoints.width, _winSizeInPoints.height);
    }
}
void GLView::setViewPortInPoints(float x , float y , float w , float h)
{
    glViewport((GLint)(x * _scaleX + _viewPortRect.origin.x),
               (GLint)(y * _scaleY + _viewPortRect.origin.y),
               (GLsizei)(w * _scaleX),
               (GLsizei)(h * _scaleY));
}

3.2.投影矩阵设置

cocos还会用WinSize来设置投影矩阵。

void Director::setProjection(Projection projection)
{
    Size size = _winSizeInPoints;

    setViewport();

    switch (projection)
    {
       ...
            
        case Projection::_3D:
        {
            float zeye = this->getZEye();

            Mat4 matrixPerspective, matrixLookup;

            loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
            
#if CC_TARGET_PLATFORM == CC_PLATFORM_WP8
            //if needed, we need to add a rotation for Landscape orientations on Windows Phone 8 since it is always in Portrait Mode
            GLView* view = getOpenGLView();
            if(getOpenGLView() != nullptr)
            {
                multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix());
            }
#endif
            // issue #1334
            Mat4::createPerspective(60, (GLfloat)size.width/size.height, 10, zeye+size.height/2, &matrixPerspective);

            multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixPerspective);

            Vec3 eye(size.width/2, size.height/2, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f);
            Mat4::createLookAt(eye, center, up, &matrixLookup);
            multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixLookup);
            
            loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
            break;
        }

        case Projection::CUSTOM:
            // Projection Delegate is no longer needed
            // since the event "PROJECTION CHANGED" is emitted
            break;

        default:
            CCLOG("cocos2d: Director: unrecognized projection");
            break;
    }

    _projection = projection;
    GL::setProjectionMatrixDirty();

    _eventDispatcher->dispatchEvent(_eventProjectionChanged);
}

4.可视区域原点

通过上面那张适配策略效果图,我们可以知道世界左边的原点其实不一定是在屏幕的左下角,而可视区域的原点除了NO_BORDER外都是(0,0),它的原点可以通过Director::getInstance()->getVisibleOrigin()获取。如果我们希望一张图片不论什么屏幕都显示在屏幕的正中间那么可以这么做,

Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

一句话,NO_BORDERFIXED_HEIGHTFIXED_WIDTH需要做位置的调整,SHOW_ALLEXACT_FIT不用。

此文章只是我学习过程中用来记录自己的理解,可作参考,可能有错误。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cocos2d-JS开发之旅 从HTML 5到原生手机游戏》 《Cocos2d-JS开发之旅——从HTML 5到原生手机游戏》从简单到复杂逐渐深入介绍Cocos2d-JS,包括HTML5和手机原生游戏两个方面的内容。这些内容融汇了作者多年的工作经验和Cocos2d-JS 的亲身使用教训,有助于读者快速掌握游戏开发的方法和避开不必要的麻烦。 《Cocos2d-JS开发之旅——从HTML 5到原生手机游戏》以两个游戏为线索,每一章的学习都为最终实现游戏而准备。除了基础的Cocos2d-JS使用方法,本书还会探讨如何调试发布原生手机游戏和另外一些高级话题。 《Cocos2d-JS开发之旅——从HTML 5到原生手机游戏》适合所有对2D 游戏开发感兴趣的人群,尤其是计算机专业学生、Flash/JS 开发者,因为Cocos2d-JS简单易懂,读者会发现自己已有的基础能快速应用或转移到Cocos2d-JS 游戏的开发中。 目录 第一部分 准 备 篇 第1 章 Cocos2d-JS 介绍 / 2 第2 章 跑起我们的HelloWorld / 10 第一部分总结 / 27 第二部分 做一个简单的小游戏 第3 章 Cocos2d-JS 的平面世界 / 30 第4 章 让世界来点动静 / 51 第5 章 让玩家操纵这个世界 / 72 第6 章 控制小怪按时出现——定时器 / 84 第7 章 游戏界面 / 96 第8 章 不能光说不练——小小碰碰糖 / 122 第9 章 把成果分享到网上 / 143 第二部分总结 / 158 第三部分 再做一个高大上游戏 第10 章 走向高大上的必经之路——简单的性能优化 / 160 第11 章 让主角不再死板 / 173 第12 章 动态的背景 / 188 第13 章 界面的文字有点丑 / 204 第14 章 超炫的效果——粒子系统 / 211 第15 章 尝试做一个更大的游戏——Hungry Hero(上篇)/ 226 第16 章 尝试做一个更大的游戏——Hungry Hero(下篇)/ 235 第三部分总结 / 291 第四部分 把两个游戏做成原生手机游戏 第17 章 咱们也来做APP / 294 第18 章 真是这么美好吗?更多问题来了 / 304 第四部分总结 / 320 第五部分 高 级 篇 第19 章 订阅者模式——事件机制 / 322 第20 章 屏幕尺寸适配哪家强 / 331 第21 章 让死去的主角灰掉——渲染控制 / 342 第22 章 动态热更新 / 363 第23 章 想说的还有很多 / 374 第五部分总结 / 376

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值