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所说的设计 ...
随机推荐
- 【java编程】java中的移位运算符
java中有三种移位运算符 << : 左移运算符,num << 1,相当于num乘以2 >> : 右移运算符,num >& ...
- esxi5 的tart命令使用注意点
esxi5.0 注意tar命令参数使用和centos6稍微有点不一样,注意下 注意需要把-f参数单独分离出来,紧接着文件. 而不能和cz命令一起用 ~ # touch abc.txt ~ # ec ...
- [JAVA]对象的别名问题
对于JAVA的基本数据类型,a=b就是把b的内容复制给a.若接着又修改了a,对b是没有影响的. 但是在为对象“赋值”的时候,情况发生了变化.对一个对象进行操作时,我们真正操作的是对象的引用. 下面对两 ...
- 理念的创新——从keep和得到app谈起
浅谈keep创新之路 不得不说,这是一个健康越来越重要的时代,也是身体素质越来越被重视的一个年代.随着交通工具日新月异地发展,我们不太需要再徒步远行,甚至连骑自行车的机会也越来越少,这给我们的出行带来 ...
- MQTT研究之mosquitto:【环境搭建】
环境信息: 1. Linux Centos7.2 环境,CPU 2核,内存8G. 2. mosquitto版本:mosquitto-1.5.4 官网:http://mosquitto.org/down ...
- 在Win10 Anaconda中安装Tensorflow
有需要的朋友可以参考一下 1.安装Anaconda 下载:https://www.continuum.io/downloads,我用的是Python 3.5 下载完以后,安装. 安装完以后,打开A ...
- ORM操作 数据库外键 一对多
创建外键: from django.db import models class usergroup(models.Model): uid = models.AutoField(primary_key ...
- 围绕Buganizer的产品流程
做技术的一定知道缺陷跟踪系统(bug系统),更不用说做测试的了,不过普遍都认为这系统是用来记录bug的,其实在google内部,这套系统是产品/项目围绕的核心.Google Buganizer扩展了类 ...
- mysqli用户权限操作
此操作指令在 mysql 的数据库中 所以要 use mysql 查询mysqli中所有用户的权限 select host,user form user; 添加用户 grant all privil ...
- 原生js中用Ajax进行get传参
原生js中用Ajax进行get传参 案例: <!DOCTYPE html> <html> <head> <meta charset="UTF-8&q ...