谈一谈Cocos2d-x中的某些“大小”

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ群:249941957加群写:Cocos2d-x


谈一谈Cocos2d-x中的某些“大小”


首先感谢各位的大力支持,让本博主的博客排名坐上火箭,一路向前。吾与所有Cocos2d-x的学习者同在~

又是一个孤独的周末,应各位同学的要求,把Cocos2d-xcocos2d-2.0-x-2.0.2版本下载下来。继续用自已的VS2005方案打开,大概看了一下目录。嗯,改动还是挺多的。首先,最明显的是HelloWorld改名为了HelloCpp,其次加入了Javascript脚本支持,以及增加了Shader支持。感觉Cocos2d-x的更新速度算挺快了,而且比较紧跟现在游戏研发的方向。对于使用过Unity3d或长期进行图形引擎开发的程序来说,新增的两个节目应该还是比较对胃口的。最近Cocos2D-HTML5也发布了Alpha版,不错,学Cocos2d-x真是大有前途啊。

好了,话归正转,本章的标题起的怪一点,其实我只是在重新学习2.0版本时看到一些东西,感觉对于初学者来说非常容易迷糊,正好写篇博文Mark一下,留得日后备查。

这里说的“大小”,包括了以下一些内容:

(1).窗口的大小

(2).分辨率的大小

(3).影幕的大小

(4).视口的大小

(5).裁剪区域的大小

我们先来看(1),窗口的大小

窗口的大小,即是Windows窗体大小。我们以HelloCpp为例,打开main.cpp,。找到这两句代码:

CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView->setFrameSize(960, 640 );

这里取得了Opengl视窗类CCEGLView单件实例指针返回给指针变量eglView,并调用其setFrameSize函数设置一个所谓的Frame大小。这个大小是神马东西?进去看看!

进入到CCEGLView.cpp的相应函数:


void CCEGLView::setFrameSize(float width, float height)
{
    //我们在这里看到调用了Create函数,这里传入的width,height就是要创建的窗口的大小。
Create((LPCTSTR)m_szViewName, (int)width, (int)height);
//后面调用基类的setFrameSize,一会儿再分析,暂略过。
    CCEGLViewProtocol::setFrameSize(width, height);
}

看一下Create函数:

bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)
{
    bool bRet = false;
    do 
{
			//这里通过判断m_hWnd是否有效来确保只Create一次,不允许创建多个窗体。
        		CC_BREAK_IF(m_hWnd);
			//
			HINSTANCE hInstance = GetModuleHandle( NULL );
			WNDCLASS  wc;       //窗口类信息结构

			//填充信息结构
			wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  
			wc.lpfnWndProc    = _WindowProc;                    
			wc.cbClsExtra     = 0;                              
			wc.cbWndExtra     = 0;                              
			wc.hInstance      = hInstance;                      
			wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );  
			wc.hCursor        = LoadCursor( NULL, IDC_ARROW );  
			wc.hbrBackground  = NULL;                          
			wc.lpszMenuName   = NULL;                          
			wc.lpszClassName  = kWindowClassName;              
			//注册窗口类
			CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());        

			//取得电脑屏幕大小矩形
			RECT rcDesktop;
			GetWindowRect(GetDesktopWindow(), &rcDesktop);
			//将窗口标题多字节转宽字符。
			WCHAR wszBuf[50] = {0};
			MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf));

			// 使用注册过的窗口类创建窗口
			m_hWnd = CreateWindowEx(
				WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,    // Extended Style For The Window
				kWindowClassName,                                    // Class Name
				wszBuf,                                                // Window Title
				WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,        // Defined Window Style
				0, 0,                                                // Window Position
				0,                                                  // Window Width
				0,                                                  // Window Height
				NULL,                                                // No Parent Window
				NULL,                                                // No Menu
				hInstance,                                            // Instance
				NULL );
        //通过窗口句柄有效性判断确保创建成功才能继续
        CC_BREAK_IF(! m_hWnd);
	   //重点函数,调整窗口大小
        resize(w, h);
        //初始化OpenGL
        bRet = initGL();
	   //返回成功判断
        CC_BREAK_IF(!bRet);
        //保存单件实例指针
        s_pMainWindow = this;
        bRet = true;
    } while (0);
    //返回成败
    return bRet;
}

创建窗口时并没有用到w和h,所以只有继续看重点函数才能知道窗口大小是怎么设置的。


void CCEGLView::resize(int width, int height)
{	
//窗口句柄有效性判断
    if (! m_hWnd)
    {
        return;
    }

    //获取窗口的客户区大小矩形,这个客户区其实就是咱们OpenGL视窗的实际大小,不包含一般窗口的菜单,边框,状态栏等部分。
RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
	//如果当前窗口的客户区部分大小与参数width,height相同,直接返回
		if (rcClient.right - rcClient.left == width &&
			rcClient.bottom - rcClient.top == height)
		{
			return;
		}
		// 否则重新设置客户区大小矩形变量的值。
		rcClient.right = rcClient.left + width;
		rcClient.bottom = rcClient.top + height;
		//此函数将使窗口的整体大小(即如果有则包含菜单栏,边框,底部状态栏和客户可视区的整个窗口的大小)按照
指定的客户区大小rcClient和窗口样式来自动调整,确保了客户区就是rcClient指定大小。其中GetWindowLong用来获取当前窗口的
样式,GWL_STYLE为基本样式信息,GWL_EXSTYLE为扩展栏式信息。返回窗口的整体大小再传给rcClient。
		AdjustWindowRectEx(&rcClient, GetWindowLong(m_hWnd, GWL_STYLE), false, 
                GetWindowLong(m_hWnd, GWL_EXSTYLE));

		// 设置窗口的显示位置并应用rcClient做为窗口的大小。
		SetWindowPos(m_hWnd, 0, 0, 0, rcClient.right - rcClient.left, 
			rcClient.bottom - rcClient.top, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER 
                | SWP_NOZORDER);
}

好了,我们看,窗口的大小就是这么设置的。作者考虑到了不同的样式窗口对于客户区大小的影响,故做了相应的处理。所以我们创建指定大小的窗口时其实真正的意思是创建一个指定客户区大小的窗口。


我们再来看 (2),分辨率的大小和 (3),影幕的大小 (4),视口的大小


分辨率:即是屏幕上图像的精细度,在Cocos2d-x中其大小为屏幕在横向和纵向可以容纳的逻辑点数量,为了好理解,我们把它想像成投影机的分辨率。

影幕:想想小时候看电影时用到的那块白布吧,当然也许公司会议室里也能看到它。也就是投影机用来投射画面的画布。

视口:其实就是上面说的投影机投影出来的影片画面所占的矩形。它如果大于影幕的大小,那么你就不能看到完整的影片画面。如果小于影幕的大小。你就可以在它显示的区域里看到影片画面。


我们现在看一下刚才略过的代码:CCEGLViewProtocol::setFrameSize(width,height);

进入CCEGLViewProtocol.cpp中相应函数定义:


void CCEGLViewProtocol::setFrameSize(float width, float height)
{
    //这里使用参数对两个变量进行了赋值,第一个就是分辨率大小。第二个就是影幕的大小。
    m_obDesignResolutionSize = m_obScreenSize = CCSizeMake(width, height);
}

双击“m_obDesignResolutionSize”,按下Ctrl+F,在弹出查找对话框里对当前文档CCEGLViewProtocol.cpp中查找全部使用。





点击第一个查找结果,看一下所在函数setDesignResolutionSize,这个函数是用来设置分辨率大小的。前两个参数无疑就是设置分辨率横向纵向的像素数量的。最后一个参数resolutionPolicy我们必须了解一下,进入ResolutionPolicy的定义:

enum ResolutionPolicy
{
    // 扩展填充模式:这里等于是直接设置投影机的分辨率。如果这个分辨率与画布大小的比例失调,就会出现失真。
    kResolutionExactFit,
    // 低调整模式:这个吧,是按照设置的分辨率在横纵方向上最低值调整视口大小,使投影机投影出来的影片画面所占的矩形在
    画布上所对应的相应方向上与最低值一致。
    kResolutionNoBorder
    // 高调整模式:与上面恰恰相反,按照设置的分辨率在横纵方向上最高值调整视口大小,使投影机投影出来的影片画面所占的
    矩形在画布上所对应的相应方向上与最高值一致。同时这个矩形的另一个方向按最低值进行裁剪,区域外部分填充黑色。
    kResolutionShowAll,
    //无效值
    kResolutionUnKnown,
};


这个枚举归结为“分辨率模式”。是不是有点迷糊,得,我们在继续下面的函数之前,先以例子来说明一下:

打开HelloLua工程的AppDelegate.cpp,看一下这个函数:

bool AppDelegate::applicationDidFinishLaunching()
{
    // 取得显示设备的单件实例指针
CCDirector *pDirector = CCDirector::sharedDirector();
// 设置其使用的OpenGL视窗为单件OpenGL视窗
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
	//在这里设置了分辨率为480,320,与窗口客户区一致,显示模式为kResolutionShowAll。
    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(480, 320, kResolutionShowAll);
    
	//开始高清显示模式
    // pDirector->enableRetinaDisplay(true);

    // 设置显示FPS相关
    pDirector->setDisplayStats(true);

    // 设置帧间隔时间
    pDirector->setAnimationInterval(1.0 / 60);

    // 注册LUA管理器并设置为当前使用的脚本系统
    CCScriptEngineProtocol* pEngine = CCLuaEngine::engine();
    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
	//执行hello.lua脚本启动游戏,不懂的可以参见本博主的博文“HelloLua”深入分析一文
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    CCString* pstrFileContent = CCString::createWithContentsOfFile("hello.lua");
    if (pstrFileContent)
    {
        pEngine->executeString(pstrFileContent->getCString());
    }
#else
    std::string path = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("hello.lua");
    pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
    pEngine->executeScriptFile(path.c_str());
#endif 

    return true;
}

我们要干点坏事,好吧。我们尝试着将其分别改为

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(80, 320, kResolutionExactFit);
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(80, 320, kResolutionNoBorder);
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(80, 320, kResolutionShowAll);

并进行测试。下面是测试图。




好好理解一下。

再回来看setDesignResolutionSize函数:


void CCEGLViewProtocol::setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy)
{
	//是否使用高清模式(视网膜屏幕),高清模式不让设置分辨率。
CCAssert(m_bIsRetinaEnabled == false, "can not enable retina while set design resolution size!");
//确保分辨率显示方式有效
    CCAssert(resolutionPolicy != kResolutionUnKnown, "should set resolutionPolicy");
    //如果参数无效,直接返回。
    if (width == 0.0f || height == 0.0f)
    {
        return;
    }
    //在这里对变量m_obDesignResolutionSize进行了设置。
    m_obDesignResolutionSize.setSize(width, height);
    //这里计算出影幕大小与分辨率大小的比率。存放在变量m_fScaleX,m_fScaleY中。
    m_fScaleX = (float)m_obScreenSize.width / m_obDesignResolutionSize.width;
    m_fScaleY = (float)m_obScreenSize.height / m_obDesignResolutionSize.height;
//如果模式是kResolutionNoBorder,按照最大影幕方式,即保证像素不失真的情况下,影幕适应分辨率
//如果是低调整模式
    if (resolutionPolicy == kResolutionNoBorder)
{
	   //缩放值按最大
        m_fScaleX = m_fScaleY = MAX(m_fScaleX, m_fScaleY);
    }
    //如果是高调整模式
    if (resolutionPolicy == kResolutionShowAll)
{
	    //缩放值按最小
        m_fScaleX = m_fScaleY = MIN(m_fScaleX, m_fScaleY);
    }

    // 根据缩放值和分辨率计算视口的宽高    
    float viewPortW = m_obDesignResolutionSize.width * m_fScaleX;
    float viewPortH = m_obDesignResolutionSize.height * m_fScaleY;
	// 设置视口位置居屏幕中间
    m_obViewPortRect.setRect((m_obScreenSize.width - viewPortW) / 2, (m_obScreenSize.height - viewPortH) / 2, viewPortW, viewPortH);
    //保存分辨率模式
    m_eResolutionPolicy = resolutionPolicy;
    //使用变量设置Opengl视口(此处屏蔽,在SetGLDefaultValues中会有设)
    //setViewPortInPoints(0, 0,m_obScreenSize.width, m_obScreenSize.height);
// 创建FPS的文字标签
CCDirector::sharedDirector()->createStatsLabel();
//初始化逻辑点和像素值大小
CCDirector::sharedDirector()->m_obWinSizeInPoints = CCDirector::sharedDirector()->m_obWinSizeInPixels = getSize(); 
//对OpengGL用到的一些设置进行初始化,其中有重设投影函数,会再重置视口。
    CCDirector::sharedDirector()->setGLDefaultValues();
}

现在大家明白我为什么将(2),(3),(4)并在一起说了。它们其实是有机结合的。相辅相成,缺一不可。


我们最后来看一下:(5),裁剪区域的大小:

裁剪区域:如果对Opengl显示窗口设定裁剪区域,则裁剪区域外的像素将不会显示。


我们仍然在HelloLua工程中进行说明:

在函数AppDelegate::applicationDidFinishLaunching()我们在

CCEGLViewProtocol*	tpCCEGLView = (CCEGLViewProtocol*) CCEGLView::sharedOpenGLView();

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(80, 320, kResolutionShowAll);

后面增加代码:


//设定裁剪显示区域
tpCCEGLView->setScissorInPoints(100,100,100,100);
	//开启裁剪检测
glEnable(GL_SCISSOR_TEST);

运行测试:



可以看到,我们对画面进行了裁剪。从屏幕100,100位置起的大小为宽100,高100的区域被设为了裁剪显示区域,其它部分均被裁切掉了。

好了,以上这些个“大小”的基础知识,也颇让我费了些脑子去想该怎么讲出来。希望本节课目不会令大家失望。







你可能感兴趣的:(谈一谈Cocos2d-x中的某些“大小”)