cocos虽然定位于移动端的手机游戏引擎,但也有不少公司用它开发PC游戏,由于cocos自身的原因,许多功能并不尽如人意。开发cocos游戏时,都有这样的经历,拖动cocos窗口时,或者点击窗口边框,cocos就会暂停渲染主线程,这个时候主进程阻塞(如同切入后台不渲染一样,切入后台不渲染问题,可以直接更改invalid属性解决)。然而有些游戏,需要拖动的时候,需要继续渲染,完成此需求的代码如下。
首先director中提供主渲染:
void Director::drawMoveScene(int frameNumbers)
{
//if (bFlag == true)
// return;
bFlag = true;
if (frameNumbers < 1)
return;
LONGLONG interval = 0LL;
LONG waitMS = 0L;
static LARGE_INTEGER nLast;
static LARGE_INTEGER nNow;
static bool bFirst = true;
if (bFirst)
{
bFirst = false;
QueryPerformanceCounter(&nLast);
}
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
double lastPositionX=-999, lastPositionY=-999;
_openGLView->getCursorPos(&lastPositionX, &lastPositionY);
while (frameNumbers > 0)
{
QueryPerformanceCounter(&nNow);
interval = nNow.QuadPart - nLast.QuadPart;
//_openGLView->pollEvents();
if (interval >= 30000)
{
nLast.QuadPart = nNow.QuadPart;
double curPositionX = -999, curPositionY = -999;
_openGLView->getCursorPos(&curPositionX, &curPositionY);
if (curPositionX != lastPositionX || curPositionY != lastPositionY)
{
bFlag = false;
return;
}
calculateDeltaTime();
//tick before glClear: issue #533
if (!_paused)
{
_eventDispatcher->dispatchEvent(_eventBeforeUpdate);
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();
/* to avoid flickr, nextScene MUST be here: after tick and before draw.
* FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
*/
if (_nextScene)
{
setNextScene();
}
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
if (_runningScene)
{
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
_runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
//clear draw stats
_renderer->clearDrawStats();
//render the scene
_openGLView->renderScene(_runningScene, _renderer);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}
if (_displayStats)
{
showStats();
}
else
{
static float prevDeltaTime = 0.016f; // 60FPS
static const float FPS_FILTER = 0.10f;
float dt = _deltaTime * FPS_FILTER + (1 - FPS_FILTER) * prevDeltaTime;
prevDeltaTime = dt;
_frameRate = 1 / dt;
}
_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers();
}
if (_displayStats)
{
calculateMPF();
}
frameNumbers -= 1;
}
else{
waitMS = (30000 - interval) * 1000LL / freq.QuadPart - 1L;
if (waitMS > 1L)
Sleep(waitMS);
}
bFlag = false;
// _openGLView->pollEvents();
}
}
然后,debug模式下更改SimulatorWindow函数:
LRESULT CALLBACK SNewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_MOVING:
Director::getInstance()->drawMoveScene(100000);
break;
case WM_NCLBUTTONDOWN:
Director::getInstance()->drawMoveScene(100000);
break;
}
return g_oldProc(hWnd, message, wParam, lParam);
}
release模式,在AppDelegate下修改:
/*@brief new windows process*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_MOVING:
Director::getInstance()->drawMoveScene(100000);
break;
case WM_NCLBUTTONDOWN:
Director::getInstance()->drawMoveScene(100000);
break;
}
return g_Proc(hWnd, message, wParam, lParam);
}
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
#if (LUA_SUPPORT && COCOS2D_DEBUG > 0 && CC_CODE_IDE_DEBUG_SUPPORT > 0)
// NOTE:Please don't remove this call if you want to debug with Cocos Code IDE
initRuntime();
// turn on display FPS
director->setDisplayStats(true);
#endif
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
if (!glview) {
Size viewSize = ConfigParser::getInstance()->getInitViewSize();
//string title = ConfigParser::getInstance()->getInitViewName();
string title = StringUtils::UTF8Str("无线棋牌Demo");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) && (COCOS2D_DEBUG > 0 && CC_CODE_IDE_DEBUG_SUPPORT > 0)
extern void createSimulator(const char* viewName, float width, float height, bool isLandscape = true, float frameZoomFactor = 1.0f);
bool isLanscape = ConfigParser::getInstance()->isLanscape();
createSimulator(title.c_str(), viewSize.width, viewSize.height, isLanscape);
director->getOpenGLView()->setFrameSize(ConfigParser::getInstance()->getInitViewSize().width, ConfigParser::getInstance()->getInitViewSize().height);
#else
auto glView = cocos2d::GLViewImpl::createWithRect(title.c_str(), Rect(0, 0, viewSize.width, viewSize.height));
auto director = Director::getInstance();
director->setOpenGLView(glView);
HWND hWnd = glView->getWin32Window();
g_Proc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc);
if (g_Proc == 0)
{
printf("SetWindowLong NewWndProc Error:%d\n", GetLastError());
}
#endif
}
后续:有提出,当点击最小化,不放移动后,依旧渲染,接着修改如下:
void Director::drawMoveScene(int frameNumbers)
{
bFlag = true;
if (frameNumbers < 1)
return;
LONGLONG interval = 0LL;
LONG waitMS = 0L;
static LARGE_INTEGER nLast;
static LARGE_INTEGER nNow;
static bool bFirst = true;
if (bFirst)
{
bFirst = false;
QueryPerformanceCounter(&nLast);
}
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
double lastPositionX=-999, lastPositionY=-999;
_openGLView->getCursorPos(&lastPositionX, &lastPositionY);
auto frameSize = _openGLView->getFrameSize();
bool bClose = false;
int det = (_openGLView->getChangeScreen()?10:0)+105;
int fullscreenWidth, fullscreenHeight;
Device::getWidthAndHeight(fullscreenWidth, fullscreenHeight);
int bottomY=-8;
if (frameSize.width == fullscreenWidth)
{
bottomY += 6;
det = 114;
}
if ((frameSize.width - det < lastPositionX &&lastPositionX <= frameSize.width) && (-31 < lastPositionY&&lastPositionY < bottomY))
bClose = true;
while (frameNumbers > 0)
{
QueryPerformanceCounter(&nNow);
interval = nNow.QuadPart - nLast.QuadPart;
if (interval >= 30000)
{
nLast.QuadPart = nNow.QuadPart;
double curPositionX = -999, curPositionY = -999;
_openGLView->getCursorPos(&curPositionX, &curPositionY);
auto frameSize = _openGLView->getFrameSize();
auto state = GetAsyncKeyState(VK_LBUTTON);
if (!state)
{
bFlag = false;
return;
}
if ((curPositionX != lastPositionX || curPositionY != lastPositionY)&&(!bClose))
{
bFlag = false;
return;
}
calculateDeltaTime();
//tick before glClear: issue #533
if (!_paused)
{
_eventDispatcher->dispatchEvent(_eventBeforeUpdate);
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();
/* to avoid flickr, nextScene MUST be here: after tick and before draw.
* FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
*/
if (_nextScene)
{
setNextScene();
}
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
if (_runningScene)
{
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
_runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
//clear draw stats
_renderer->clearDrawStats();
//render the scene
_openGLView->renderScene(_runningScene, _renderer);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}
if (_displayStats)
{
showStats();
}
else
{
static float prevDeltaTime = 0.016f; // 60FPS
static const float FPS_FILTER = 0.10f;
float dt = _deltaTime * FPS_FILTER + (1 - FPS_FILTER) * prevDeltaTime;
prevDeltaTime = dt;
_frameRate = 1 / dt;
}
_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers();
}
if (_displayStats)
{
calculateMPF();
}
frameNumbers -= 1;
}
else{
waitMS = (30000 - interval) * 1000LL / freq.QuadPart - 1L;
if (waitMS > 1L)
Sleep(waitMS);
}
bFlag = false;
// _openGLView->pollEvents();
}
}