cocos2dx 屏幕适配解决方案,包含刘海屏处理

本文采用AspectFit 方式来计算实际屏幕分辨率,计算方式如下:

local framesize = cc.Director:getInstance():getOpenGLView():getFrameSize()
local scaleX,scaleY = framesize.width/1136,framesize.height/640
local realScale = math.min(scaleX,scaleY)
local realWidth = framesize.width/realScale
local realHeight = framesize.height/realScale
local realSize = cc.size(realWidth,realHeight)

理想设计分辨率:1136*640 ,是美术出图采用的设计分辨率,

实际设计分辨率:调整后的理想设计分辨率,frameSize除以MIN(scaleX,scaleY)的结果,上面代码中的realSize

实际分辨率是:realWidth,realHeight,通过以下方式设置实际设计分辨率:

 cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(realWidth,realHeight,cc.ResolutionPolicy.NO_BORDER)

以OPPO R15为例,屏幕分辨率是2280x1080计算出来的实际分辨率是1351x640

为了把 设计分辨率的宽高比 调整为 屏幕分辨率的宽高比,从而得到了实际分辨率,即:2280/1080 = 1351/640 ,然后然后让实际分辨率乘以1080/640(也就是等比缩放),让游戏窗口充满屏幕:


void GLView::updateDesignResolutionSize()
{
    //_designResolutionSize是实际分辨率,
    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;
        //这里_scaleX 和 _scaleY已经相等,下面MAX函数可以忽略
        _scaleX = _scaleY = MAX(_scaleX, _scaleY);
        // 对设计分辨率等比缩放,让游戏视口充满屏幕
        float viewPortW = _designResolutionSize.width * _scaleX;
        float viewPortH = _designResolutionSize.height * _scaleY;
        
        _viewPortRect.setRect((_screenSize.width - viewPortW) / 2, (_screenSize.height - viewPortH) / 2, viewPortW, viewPortH);

        auto director = Director::getInstance();
        director->_winSizeInPoints = getDesignResolutionSize();
        director->_isStatusLabelUpdated = true;
        director->setProjection(director->getProjection());

        glViewport(0, 0, _screenSize.width, _screenSize.height);
    }
}

屏幕适配原则:

        首先设置所有界面的父节点g_MainSceneLayer 的size为实际设计分辨率:g_MainSceneLayer:setContentSize(realSize),

        然后设置界面本身size也为实际设计分辨率,self:setContentSize(realSize);self.resourceNode:setContentSize(realSize)

没有刘海,界面分为两种:

      1.居中的功能界面:只要设置界面的锚点为(0.5,0.5),位置设置为设计界面居中即可 :

         self:setAnchorPoint(0.5,0.5), self:setPosition(size.width * 0.5,size.height * 0.5)

      2.锁定固定边距位置的界面,如图:

      这种固定节点边距位置的界面,通过函数 ccui.helper:doLayout(self.resourceNode_)实现,在实际设计分辨率的size中,将指定节点固定在其父节点距离上下左右四条边固定距离的位置上。

有刘海的情况:一般采用下图这种适配方案:

具体实现如下:

  • 1.IOS苹果手机因为机型类型比较少,所以游戏正文所占屏幕区域的宽高比(safeArea)我们可以直接配置,比如["iPhone Xs"] = 2、["iPhone Xs Max"] = 2。
  • 2.安卓机器机型比较多而杂,无法通过配置写死safeArea,所以通过代码取得,指的一提的是安卓9.0以下的时候没有统一的获取方式,华为 oppo Vivo 小米需要各自厂商不同的接口分别获取,安卓9.0以上可以统一获取。(具体可自行百度或者参考AppActivity.java)
    取得safeArea以后:
    local safeWidth = realHeight * safeArea (游戏正文宽度)
    local offset = (realWidth - safeWidth)/2 (左右刘海宽度)

    g_MainSceneLayer:setPositionX(offset) (将所有界面的父节点 右移offset)
    local safeSize = cc.size(safeWidth,realHeight)

    g_MainSceneLayer:setContentSize(safeSize) (设置contentSize为正文大小)
    如果界面不需要适配,需要全屏显示。比如有的背景界面需要在刘海区域显示,就需要将界面左移回来,并设置contentSize大小为realSize

下面是代码实现:

默认适配在下面ViewBase:createResourceNode()和ViewBase:adjustToScene()中实现

ViewBase是最基础的基类

ViewBase需要一个默认的adjustToScreen

function ViewBase:createResourceNode(resourceFileName)
    --此处省略
    ---
    ---
    ---
    self.resourceNode_ = cc.CSLoader:createNode(resourceFileName)
    ---
    local  director = cc.Director:getInstance()
    local visibleSize = director:getVisibleSize()
    self:addChild(self.resourceNode_)
    local contentSize = self.resourceNode_:getContentSize()
    self:setContentSize(contentSize)
    if self.EnableAdjustToScreen == nil then
        --默认只有全屏的界面才启用屏幕适配
        --小界面也可以通过设置该变量为true启动默认的屏幕适应机制(简单居中)
        --通过adjustToScreen方法自定义屏幕适应机制
        --使用固定边距位置进行自适应的界面可以直接继承UILayoutPanel
        self.EnableAdjustToScreen = contentSize.width = self.DESIGN_WIDTH and contentSize.height = self.DESIGN_HEIGHT
    end
end
function ViewBase:adjustToScreen(size)
    if self.EnableAdjustToScreen then
        self:setAnchorPoint(0.5,0.5)
        self:setPosition(size.width * 0.5,size.height * 0.5)
    end
end

一般情况下我们会创建一个我们自己常用的基类UIPanel,继承于ViewBase

local UIPanel = class("UIPanel",cc.load("mvc").ViewBase);

需要一个单例类g_UIManager来管理所有UIPanel 界面,

还需要一个使用Layout进行屏幕适配的基类,

local UILayoutPanel = class("UILayoutPanel ",UIPanel );

当你新加的界面需要设置固定边距位置(自适应父节点,或者此界面左右边缘需要避免被刘海遮盖住就要继承于这个UILayoutPanel,当然有的界面自适应时候需要特殊处理,通过重写adjustToScene()方法自定义该界面的屏幕适配方案。

UILayoutPanel 主要是实现这个函数,(一般需要设置固定和拉伸的界面才需要继承这个类,比如需要固定在屏幕边缘的界面,所有可以结合刘海屏的适配一起处理)

--有效区域的长度是去掉刘海部分的长度,是小于屏幕长度的
--各种刘海屏机型宽高比值配置:有效区域的宽度比屏幕高度的比值
g_Config.iPhoneXSafeArea = {

    ["iPhone Xs"] = 2,
    ["iPhone Xs Max"] = 2,
    ["iPhone Xr"] = 2,
    ["vivo X21"] = 1.9,
}
--界面管理类中 执行屏幕适配的接口 每个界面执行adjustToScreen,
--这里传的是visibleSize
function UIManage:requestAdjustToScreen(width,height)
    local offset = 0
    if g_Config.iPhoneXSafeArea then
        --有效区域宽度:也就是去掉刘海部分的 宽度(横屏游戏)
        local adjWidth = math.floor(height*g_Config.iPhoneXSafeArea)
        offset = width - adjWidth 
        offset = offset * 0.5 --刘海的尺寸 高度
        if  offset > 0 then
            width = adjWidth 
        else
            offset = 0
        end
    end
    if g_MainSceneLayer then --所有界面的父节点,要显示的所有界面ui都加到这个上面
        g_MainScenLayer:setPositionX(offset)
    end
    if g_SpecialLayer then --特殊界面的父节点,
        g_SpecialLayer:setPositionX(offset)
    end
    --有效区域的尺寸 去掉两边刘海区域的尺寸
    local size = {width = width,height = height}
    if g_MainSceneLayer then 
        g_MainScenLayer:setContentSize(size)
    end
    if g_SpecialLayer then 
        g_SpecialLayer:setContentSize(size)
    end
    for k,v in next,self.m_panels do
        local count = #v
        for i=1,count do
            local panel = v[i]
            local parent = panel:getParent()
            if panel.ForceAdjustToScreen or parent == g_MainScenLayer or parent == g_SpecialLayer then
                panel:adjustToScreen(size,offset)
            end
        end
    end
end
--因为屏幕左边缘或右边缘的显示需要:1.固定位置,调用doLayout  2.避免被刘海覆盖,要
--调整位置  此函数实现了这两点:
function UILayoutPanel:adjustToScreen(size)
  --像iphoneX这种刘海屏的机型的处理,需要适配刘海屏的机型配置一下就行了
  if self.IPHONEX_ADJUST_FULLSCREEN and g_Config.iPhoneXSafeArea then
    local screenSize = cc.Director:getInstance():getVisibleSize()
    --有效区域宽度减去屏幕宽度 得到刘海宽度
    local offset = size.width - screenSize.width
    self:setPositionX(offset*0.5)
    size = screenSize
  end
  local realSize = size 
  local mode = self._adjustModule
  if mode == self.ADJUST_KEEPWIDTH then --按照按照宽度比例缩放
    local scale = size.width/self.DESIN_WIDTH
    realSize = {width = self.DESGIN_WIDTH ,height = size.height/scale}    
    self:setScale(scale)
  elseif mode == self.ADJUST_KEEPHEIGHT then --按照高度比例缩放
    
  elseif mode == self.ADJUST_FILL then  
     local scaleX = size.width/self.DESGIN_WIDTH 
     local scaleY = size.heigt/self.DESGIN_HEIGHT
     local scale = math.min(scaleX,scaleY)
     realSize = {width = size.width/scale,height = size.height/scale}
     self:setScale(scale)
  end
  self:setContentSize(realSize)
  self.resourceNode_:setContentSize(realSize)
  ccui.helper:doLayout(self.resourceNode_)

end

想要在WIN端模拟刘海屏的适配情况只需要这样调用:

g_Config.iPhoneXSafeArea = 1.7 
local displaySize = cc.Director:getInstance():getVisibleSize()
g_UIManage:requestAdjustToScreen(displaySize.width,displaySize.height)

总结:把 设计分辨率的宽高比 按照 设备屏幕的宽高比 调整成实际分辨率,实际分辨率和屏幕分辨率宽高比一定是相等的,然后做等比缩放

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值