本文采用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)
总结:把 设计分辨率的宽高比 按照 设备屏幕的宽高比 调整成实际分辨率,实际分辨率和屏幕分辨率宽高比一定是相等的,然后做等比缩放