Cocos2dx开发之屏幕适配
由于各种智能手机的屏幕大小都不一致,会出现同一张图片资源在不同的设备分辨率下显示不一样的问题。为避免这样的情况,需要Cocos引擎能提供多分辨率的支持,也就是说要求实现这样的效果 — 开发者不需要考虑程序实际运行在什么分辨率下而只需要制定设置好设计分辨率就行,接着引擎便会自动实现设计分辨率到屏幕分辨率的转化,以及不同资源分辨率到设计分辨率的转化。下面逐一分析理解涉及到的概念:
一 设计分辨率
顾名思义,指由开发者自定义的分辨率,最终引擎会拿实际的屏幕分辨率和这个自定义的设计分辨率得到缩放因子。但实际的项目开发中还有细节要兼顾,如是否需要宽高都等比缩放,故有一知识点—缩放策略。在cocos2dx中通过setDesignResolutionSize来设置,方法使用如下:
setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy);
参数width和height指定设计分辨率的尺寸,resolutionPolicy指定缩放策略,由ResolutionPolicy枚举定义,如下图所示:
//Cocos2dConstants.lua
cc.ResolutionPolicy =
{
EXACT_FIT = ,
NO_BORDER = ,
SHOW_ALL = ,
FIXED_HEIGHT = ,
FIXED_WIDTH = ,
UNKNOWN = ,
}
EXACT_FIT :充满整个屏幕,唯一一个不按等比缩放的策略,宽高比不相等可能导致拉伸或压缩发生形变,开发中不建议用;
NO_BORDER :充满屏幕,等比缩放,实质上是屏幕宽、高分别和设计分辨率宽、高计算缩放因子,取较(大)者作为宽、高的缩放因子。保证了设计区域总能一个方向上铺满屏幕,而另一个方向一般会超出屏幕区域。 而区域是被居中对齐到屏幕(如上图);
SHOW_ALL :保持全部元素可见,等比缩放。实质是屏幕宽、高分别和设计分辨率宽、高计算缩放因子,取较(小)者作为宽、高的缩放因子。保证了设计区域全部显示到屏幕上,但可能会有黑边(如上图)。
以上三者都不需要开发者对元素的位置进行调整,都是优先字面上的意思,NO_BORDER是“没黑边,但可能有裁剪”,SHOW_ALL是“完全没裁剪,但可能有黑边”,可以说两者刚好是相反的情况。
FIXED_XXXX :也是等比缩放,按开发者指定的某一边来建立缩放比例。与上述不同的是,它使用屏幕左下角作为原点,且充满屏幕。
二 调整元素位置
除了FIXED_XXXX,其他的缩放策略的绘制区域都是设计分辨率表示的有效区域,都是设计分辨率对应区域的原点(如上述的中点)。但是FIXED_XXXX对应的区域改变了,则程序中设定的绝对坐标往往失效,如下图的在一个分辨率中位于中点的点(100,50),在另一分辨率下就不再是中点了:
故针对使用FIXED_XXXX缩放策略的项目,开发过程中尽量不要使用绝对坐标,除此以外别的策略都没有问题。解决这个问题的方法是不固定边方向的坐标不要用绝对坐标,可以通过引入visibleSize辅助调整,如auto p2 = Vec2(visibleSize.width/2 - 10,visibleSize.height/2 + 3),这样便可实现逻辑对齐的自适应了。
三 视口设置
ViewPort的设置在OpenGL的GPU渲染管线中的屏幕映射占重要作用,在Cocos2dx中通过setViewPortInPoints方法来设置视口的大小:
void Director::setViewport()
{
if (_openGLView)
{
_openGLView->setViewPortInPoints(, , _winSizeInPoints.width, _winSizeInPoints.height);
}
} //CCGLView.cpp
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));
}
setViewPortInPoints方法将基于设计分辨率的坐标信息转换为基于屏幕实际像素大小的坐标信息,然后使用GL指令glViewPort进行设置。
四 资源分辨率
上面已经讲述了设计分辨率到屏幕分辨率的转化流程,下面简述设计分辨率到资源分辨率的转化。虽说场景元素的位置不应和屏幕的实际分辨率有什么关系的,但是在实际的项目应用开发中,我们最好使设计分辨率和资源分辨率保持一致,这样开发者只需要设置好设计分辨率之后对应的资源UI元素就能被放置在正确的位置上了。
在Cocos2dx中使用setContentScaleFactor(float scaleFactor)方法来对资源进行相应缩放,参数scaleFactor表示设计分辨率与资源分辨率的缩放因子。
void Director::setContentScaleFactor(float scaleFactor)
{
if (scaleFactor != _contentScaleFactor)
{
_contentScaleFactor = scaleFactor;
_isStatusLabelUpdated = true;
}
}
补充总结一点,由于纹理坐标使用归一化的坐标值,因此对图元的贴图是与分辨率无关的。但是对于2D绘图,渲染系统要依赖于纹理的实际大小来计算顶点坐标,这就需要对不同分辨率的资源进行确定的缩放因子值缩放。
Size Texture2D::getContentSize() const
{
Size ret;
ret.width = _contentSize.width / CC_CONTENT_SCALE_FACTOR();
ret.height = _contentSize.height / CC_CONTENT_SCALE_FACTOR(); return ret;
}
到此为止,相关的概念已经比较笼统的理解了一遍,下面举之前我的项目中屏幕适配方案来加深巩固下对这些内容的理解:
在lua入口加载文件开头设置好设计分辨率长和宽,以及缩放策略:
CONFIG_SCREEN_WIDTH =
CONFIG_SCREEN_HEIGHT =
CONFIG_SCREEN_AUTOSCALE = "SHOW_ALL"
这里设置宽和高分别为960和540,秉着“设计分辨率和资源分辨率保持一致”的做法,项目中用Cocos Studio拼的界面中panel的大小和设计分辨率保持一致,只要在编辑器中调整好UI控件的位置就好,之后程序中再也不用理会。同时,策略使用“SHOW_ALL”保证全部元素都能看到,而出现的黑边会额外用花纹图片挡住,后面会继续讲解。
屏幕分辨率是960x640,在SHOW_ALL策略下960x540的设计分辨率,便以小的缩放因子为主,那明显高height方向上会出现黑边,需要添加花纹图片掩盖黑边
ClsStarttScene.onEnter = function(self)
local utils = require("update/utils")
utils.makeOutSideEdge("update/screen_edge.jpg")
self:showLogo()
end utils.makeOutSideEdge = function(file_path)
local glview = CCDirector:sharedDirector():getOpenGLView()
local framesize = glview:getFrameSize()
local scaleX = framesize.width / CONFIG_SCREEN_WIDTH
local scaleY = framesize.height / CONFIG_SCREEN_HEIGHT local parent = getNotification()
if scaleY > scaleX then --上下出现黑边
local viewportsprite_down = ViewPortSprite:create(file_path, CONFIG_SCREEN_WIDTH, CONFIG_SCREEN_HEIGHT);
parent:addChild(viewportsprite_down) --按照游戏主窗口的x轴大小缩放花纹图片的大小
local need_width = framesize.width
local contentsize = viewportsprite_down:getContentSize()
local sp_width = contentsize.width*scaleX
viewportsprite_down:setScaleX(need_width/sp_width)
local offsetX = ((sp_width - need_width)/)/scaleX ... ... ... ....
else
--Iphonex等机型是左右出现黑边
local viewportsprite_left = ViewPortSprite:create(file_path, CONFIG_SCREEN_WIDTH, CONFIG_SCREEN_HEIGHT);
parent:addChild(viewportsprite_left)
viewportsprite_left:setRotation(-)
viewportsprite_left:setAnchorPoint(CCPoint(,)) local contentsize = viewportsprite_left:getContentSize()
--横向资源,旋转90度放直
local sp_ct = {}
sp_ct.height = contentsize.width
sp_ct.width = contentsize.height ... ... ... ...
end
效果如下,完美:
适配效果实现了,但还有两个细节需要特别学习记录一下。一个是只要设置了设计分辨率,游戏程序都会重新更新重设视口,投影变换矩阵等等,后续添加的UI元素都会在这个设计分辨率基础上进行渲染,如下:
void GLView::updateDesignResolutionSize()
{
if (_screenSize.width > && _screenSize.height >
&& _designResolutionSize.width > && _designResolutionSize.height > )
{
_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) / , (_screenSize.height - viewPortH) / , viewPortW, viewPortH); // reset director's member variables to fit visible rect
auto director = Director::getInstance();
director->_winSizeInPoints = getDesignResolutionSize();
director->_isStatusLabelUpdated = true;
director->setGLDefaultValues(); //重设
}
} 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();
}
另外一个细节就是添加挡住黑边的花纹图片的时候,不应该在游戏逻辑设计分辨率下而是在实际屏幕分辨率下添加,这样计算主窗口大小比较方便调整坐标,但要记住恢复游戏的设计分辨率。如下:
ViewPortSprite* ViewPortSprite::create(const char *pszFileName, int nViewPortW, int nViewPortH)
{
ViewPortSprite *pobSprite = new ViewPortSprite();
if (pobSprite && pobSprite->initWithFile(pszFileName))
{
pobSprite->autorelease();
pobSprite->setViewPort(nViewPortW, nViewPortH);
pobSprite->ignoreAnchorPointForPosition(true);
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
void ViewPortSprite::updateViewPort()
{
CCSize szframsize = CCDirector::sharedDirector()->getOpenGLView()->getFrameSize();
glViewport(, , szframsize.width, szframsize.height);
} void ViewPortSprite::draw(void)
{
updateViewPort();
CCSprite::draw();
//绘制完毕后要记得恢复
CCDirector::sharedDirector()->getOpenGLView()->setDesignResolutionSize(m_nViewPortW, m_nViewPortH, kResolutionShowAll);
}
默认情况下,当不调用方法显示去修改,游戏初始化之后设计分辨率和屏幕分辨率是保持一致的。
Cocos2dx开发之屏幕适配的更多相关文章
- cocos2d-x 3.10 屏幕适配问题
cocos2d-x 的屏幕适配问题困扰了我很久,差不多有一个星期吧.通过亲身实践才解决了问题,分享一下解决办法,供大家借鉴学习. 其实解决办法很简单,把下面代码注释掉就好了 // if (frameS ...
- Cocos2dx 3.x 屏幕适配
Cocos2dx 3.10+Cocos Studio3.10 1.在适配过程中必须明确几个概念: ①Frame大小:这个值在windows/mac/linux下就是创建窗体的大小,在手机上就是屏幕大小 ...
- ios 开发最新屏幕适配
- Android屏幕适配框架-(今日头条终极适配方案)
在Android开发中,屏幕适配是一个非常头痛的问题,因而为了去进行屏幕适配,作为程序员,是呕心沥血,历经磨难,哈哈 我们之前做屏幕适配一般都会用到一下两种方式: 第一种就是宽高限定符适配,什么是宽高 ...
- cocos2dx屏幕适配方案
我们在利用cocos2dx来开发游戏时,在开始时就不可避免的会遇到屏幕适配问题,来使我们的游戏适应移动终端的各种分辨率大小.目前,大家采用的屏幕适配方案不一,网上的资料也比较丰富,下面我也将自己使用的 ...
- Cocos2d-x——Cocos2d-x 屏幕适配新解【转载】
Cocos2d-x 屏幕适配新解 本文出自[无间落叶](转载请保留出处):http://blog.leafsoar.com/archives/2013/05-10-19.html 为了适应移动终端的各 ...
- cocos2d-x 屏幕适配新解
转自:http://blog.leafsoar.com/archives/2013/05-10-19.html 为了适应移动终端的各种分辨率大小,各种屏幕宽高比,在 cocos2d-x(当前稳定版:2 ...
- cocos2dx ——屏幕适配
本文出自 “夏天的风” 博客,请务必保留此出处 http://shahdza.blog.51cto.com/2410787/1550089 手机的屏幕大小千差万别,如现在流行的安卓手机屏幕大部分长宽比 ...
- 两分钟让你明白cocos2dx的屏幕适配策略
闲来无事,整理了一下cocos2dx的屏幕适配策略,本文适用于想快速理解cocos2dx适配的开发者. 我们先假设:以854 * 480 的屏幕为标准进行开发,当然,这也就是cocos2dx所说的设计 ...
随机推荐
- [JLOI2011]不重复数字
原题链接 题解 题目大意:给出N个数,要求把其中重复的去掉,只保留第一次出现的数.最后按顺序输出N <= 50000 然这题是个哈希的典型题目 HASH,我对于它的理解就是一个桶%一个数,当然并 ...
- .Net调用Java带验证的WebService解决方法
最近遇到了一个问题,需要通过验证用户名.密码去调用对方Java给出的WebService接口. 搜索了很多资料,没想到最终很简单就完了.... (捂脸 第一步:添加web引用 第二步:进行验证,并完成 ...
- python set 集合复习--点滴
一.set特性: set是一个无序不重复的元素集合. 集合对象是一组无序排列的可哈希的值,集合成员可以做字典中的键.集合支持用in和not in操作符检查成员,由len()内建函数得到集合的基数(大小 ...
- django基础 -- 8.cookie 和 session
一. cookie 1.cookie 的原理 工作原理是:浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容, 浏览器收到相应后保存在本地:当浏览器再次访问时,浏览器会自动带上Cooki ...
- Python 解决: from pip import main ImportError: cannot import name 'main'
此次报错是因为 pip 升级出的问题: from pip import mainif __name__ == '__main__': sys.exit(main()) 改为: from pip imp ...
- golang http proxy反向代理
本文介绍golang中如何进行反向代理. 下面例子中, proxy server接收client 的 http request,转发给true server,并把 true server的返回结果再发 ...
- Java面试题 Web+EJB & Spring+数据结构& 算法&计算机基础
六.Web 部分:(共题:基础40 道,基础37 道,中等难度3 道) 122.说出Servlet 的生命周期,并说出Servlet 和CGI 的区别? [基础] 答:Web 容器加载Servlet ...
- SysUtils.CompareText的注释
两个字符串对象进行比较,忽略大小写,两个字符串缓冲区地址利用EAX和EDX两个寄存器传给该函数,字符串的长度用4个字节保存在缓冲区的前面,函数用EAX返回比较结果,结果为0表示相同. function ...
- HTTP响应过程
完整的一次 HTTP 请求响应过程(一)http://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247484648&idx=1&am ...
- Pyhon文件的用途
Python1个文件2种用途: 当文件被当做脚本运行时 __name__='__main__'当模块被导入使用 __name__='spam' ==>等于模块名 假定spam代码如下 money ...