1.版本
cocos3.9
2.屏幕适配
cocos有5种屏幕适配策略。 EXACT_ALL:非等比缩放,使画面充满整个屏幕,但会变形。 NO_BORDER:等比缩放,也会使画面充满整个屏幕,不会变形,但SS/DS比较大的一边刚好充满整个屏幕,而另一边就会超出屏幕,被截断。 SHOW_ALL:等比缩放,会让整个画面都显示出来,不会变形,不会截断,SS/DS比较小的一边刚好充满整个屏幕,而另一边往往会有黑边。 FIXED_HEIGHT:等比缩放,不会变形,高刚好充满整个屏幕,而宽可能有黑边也可能被截断。 FIXED_WIDTH:等比缩放,不会变形,宽刚好充满整个屏幕,而高可能有黑边也可能被截断。
5种适配策略效果如图所示: cocos里面有四种尺寸概念,分别是VisibleSize,WinSize,FrameSize,DesignSize。
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);
}
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->createStatsLabel();
director->setGLDefaultValues();
}
}
由代码可以看到除了FIXED_HEIGHT和FIXED_WIDTH外其他策略都没有调整设计分辨率的大小。
WinSize: 指的是OpenGL视口转化到设计分辨率下的尺寸,通俗的讲就是OpenGL画布在我们设计世界的大小,它实际上是经过引擎调整后的设计尺寸。
const Size& Director::getWinSize(void) const
{
return _winSizeInPoints;
}
void GLView::updateDesignResolutionSize()
{
...
auto director = Director::getInstance();
director->_winSizeInPoints = getDesignResolutionSize();
director->createStatsLabel();
director->setGLDefaultValues();
}
}
VisibleSize: 指的是OpenGL视口转化到设计分辨率下我们可以看到的最大区域的大小。并不是说OpenGL的画布有多大我们就可以看到多大,它有可能超出屏幕外。所以VisibleSize总是小于等于DesignSize。 其实也就是说只要你用大小等于VisibleSize的图片,它就能把整个游戏画面填满。(填满不是指没有黑边,黑边有两种情况,一种是在VisibleSize的范围内,但因为在黑边的范围内没有渲染元素,所以才显示黑色,如果你的图片足够大,是不会有黑边的,如FIXED_HEIGHT和FIXED_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);
}
...
结合上面的三个代码也能印证VisibleSize≤DesignSize 的猜测。
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
GLView* view = getOpenGLView();
if(getOpenGLView() != nullptr)
{
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix());
}
#endif
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:
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_BORDER,FIXED_HEIGHT,FIXED_WIDTH需要做位置的调整,SHOW_ALL、EXACT_FIT不用。
此文章只是我学习过程中用来记录自己的理解,可作参考,可能有错误。
|