Scene* Scene::create()
{
Scene *ret =new (std::nothrow) Scene();
if (ret && ret->init())
{
ret->autorelease();
return ret;
}
else
{
CC_SAFE_DELETE(ret);
return nullptr;
}
}
Scene::Scene()
{
// only full screen node ingnore anchor point
_ignoreAnchorPointForPosition =true;
// anchor point in center of screen
setAnchorPoint(Vec2(0.5f, 0.5f));
// camera need sort
_cameraOrderDirty =true;
// create default camera, all node see by default camera// will create a perspective camera
_defaultCamera = Camera::create();
// camera also need draw background or not
addChild(_defaultCamera);
_event = Director::getInstance()->getEventDispatcher()->addCustomEventListener(Director::EVENT_PROJECTION_CHANGED, std::bind(&Scene::onProjectionChanged, this, std::placeholders::_1));
_event->retain();
Camera::_visitingCamera= nullptr;
}
bool Scene::init()
{
auto size = Director::getInstance()->getWinSize();
// use window size to set scene's content size, so scene is full screen defaultreturn initWithSize(size);
}
bool Scene::initWithSize(const Size& size)
{
setContentSize(size);
returntrue;
}
2.render
void Scene::render(Renderer* renderer, const Mat4& eyeTransform, const Mat4* eyeProjection)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
// get transform matrix use for transform children(parents's transform will affact children// position), that's if you translate scene, all node in the scene will translate with scene// this function will call one more times by visit()'s processParentFlags()// but this function will not transform one more time, because _transformDirty turn false
const auto& transform = getNodeToParentTransform();
for (const auto& camera : getCameras())
{
if (!camera->isVisible())
continue;
Camera::_visitingCamera= camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
defaultCamera = Camera::_visitingCamera;
}
// There are two ways to modify the "default camera" with the eye Transform:// a) modify the "nodeToParentTransform" matrix// b) modify the "additional transform" matrix// both alternatives are correct, if the user manually modifies the camera with a camera->setPosition()// then the "nodeToParent transform" will be lost.// And it is important that the change is "permanent", because the matrix might be used for calculate// culling and other stuff.if (eyeProjection)
camera->setAdditionalProjection(*eyeProjection * camera->getProjectionMatrix().getInversed());
camera->setAdditionalTransform(eyeTransform.getInversed());
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
// apply viewport
camera->apply();
//clear background with max depth
camera->clearBackground();
//visit the scene
visit(renderer, transform, 0);
// render scene
renderer->render();
camera->restore();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
// we shouldn't restore the transform matrix since it could be used// from "update" or other parts of the game to calculate culling or something else.// camera->setNodeToParentTransform(eyeCopy);
}
Camera::_visitingCamera= nullptr;
}
3.visit
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.if (!_visible)
{
return;
}
// if scene get to this, it will only process flag, not transform _modelViewTransform// even though it will get into "_modelViewTransform = this->transform(parentTransform)"// it return quick, get flags = FLAGS_DIRTY_MASK = 3
uint32_t flags = processParentFlags(parentTransform, parentFlags);
// IMPORTANT:// To ease the migration to v3.0, we still support the Mat4 stack,// but it is deprecated and your code should not rely on it
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
bool visibleByCamera = isVisitableByVisitingCamera();
int i =0;
if(!_children.empty())
{
sortAllChildren();
// draw children zOrder < 0// draw left hand node
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);
if (node && node->_localZOrder <0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw// draw centre node// if scene get to this, draw will do nothingif (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
// draw right hand node
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
elseif (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
uint32_t Node::processParentFlags(const Mat4& parentTransform, uint32_t parentFlags)
{
// Fixes Github issue #16100. Basically when having two cameras, one camera might set as dirty the// node that is not visited by it, and might affect certain calculations. Besides, it is faster to do this.if (!isVisitableByVisitingCamera())
return parentFlags;
uint32_t flags = parentFlags;
flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0);
flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0);
if(flags & FLAGS_DIRTY_MASK)
// transform this node by parent's transform
_modelViewTransform = this->transform(parentTransform);
_transformUpdated =false;
_contentSizeDirty =false;
return flags;
}
Mat4 Node::transform(const Mat4& parentTransform)
{
// get self transform and multiply with parent's transformreturn parentTransform * this->getNodeToParentTransform();
}
4.draw
// scene draw will do nothingvoid Node::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags)
{
}
5.run
auto scene = HelloWorld::scene();
// this use only when director first run a scene// if not, please use replaceScene()
director->runWithScene(scene);
void Director::runWithScene(Scene *scene)
{
CCASSERT(scene != nullptr, "This command can only be used to start the Director. There is already a scene present.");
CCASSERT(_runningScene == nullptr, "_runningScene should be null");
pushScene(scene);//
startAnimation();
}
void Director::pushScene(Scene *scene)
{
CCASSERT(scene, "the scene should not null");
_sendCleanupToScene =false;
// push this scene to the scenes stack
_scenesStack.pushBack(scene);
// set this scene to be next scene, and will repalce running scene// before replace running scene, must setup new scene first, and then// delete running scene, and run new scene
_nextScene = scene;
}
// init data before first run mainloopvoid DisplayLinkDirector::startAnimation()
{
// get last update time
_lastUpdate = std::chrono::steady_clock::now();
// 将场景不可运行标志_invalid改为false表示可以进入主循环
_invalid =false;
_cocos2d_thread_id = std::this_thread::get_id();
Application::getInstance()->setAnimationInterval(_animationInterval);
// fix issue #3509, skip one fps to avoid incorrect time calculation.// 将会跳过一次calculateDeltaTime()函数执行,并且把_deltaTime清零
setNextDeltaTimeZero(true);
}
// after set run scene, get to mainloopvoid DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop =false;
purgeDirector();
}
elseif (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop =false;
restartDirector();
}
elseif (! _invalid)
{
//
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
// Draw the Scenevoid Director::drawScene()
{
// calculate "global" dt
calculateDeltaTime();
if (_openGLView)
{
_openGLView->pollEvents();
}
if (! _paused)
{
_eventDispatcher->dispatchEvent(_eventBeforeUpdate);
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();
// we have set the first scene to _nextScene// so will get to setNextScene() and set first scene to _runningScene// and set _nextScene to nullptr, and before we set a second scene the// first scene will always get render, and setNextScene() will not run againif (_nextScene)
{
setNextScene();
}
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// if you not set a next scene this _runningScene will always get renderif (_runningScene)
{
//clear draw stats
_renderer->clearDrawStats();
//render the scene
_openGLView->renderScene(_runningScene, _renderer);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications nodeif (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}
if (_displayStats)
{
showStats();
}
_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
// swap buffersif (_openGLView)
{
_openGLView->swapBuffers();
}
if (_displayStats)
{
calculateMPF();
}
}
void Director::setNextScene()
{
// if first run a scene, _runningScene = nullptr, // runningIsTransition = false, first scene will get into onEnter()
bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
// if first run a scene, _nextScene = first scene, newIsTransition = false// _runningScene will not get into onExit(), becuse _runningScene = nullptr
bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;
// If it is not a transition, call onExit/cleanupif (! newIsTransition)
{
// before get into next scene, current scene will do cleanup and onExit()if (_runningScene)
{
_runningScene->onExitTransitionDidStart();
_runningScene->onExit();
}
if (_sendCleanupToScene && _runningScene)
{
_runningScene->cleanup();
}
}
// if (_runningScene)
{
_runningScene->release();
}
// replace running scene
_runningScene = _nextScene;
_nextScene->retain();
_nextScene = nullptr;
// if new scene is not a transition scene, call onEnter() because it is a new sceneif ((! runningIsTransition) && _runningScene)
{
_runningScene->onEnter();
_runningScene->onEnterTransitionDidFinish();
}
}
6.replace
voidDirector::replaceScene(Scene *scene)
{
//CCASSERT(_runningScene, "Use runWithScene: instead to start the director");
CCASSERT(scene != nullptr, "the scene should not be null");
//ifthis scene is the first scene, start with runWithScene(scene) not replaceScene(Scene *scene)
if (_runningScene == nullptr) {
runWithScene(scene);
return;
}
//if _nextScene == scene, maybe you have replace scene but you call replace again
if (scene == _nextScene)
return;
// you have push one scene to scene stack, andthen you call replace scene
if (_nextScene)
{
// impossible
if (_nextScene->isRunning())
{
_nextScene->onExit();
}
// cleanup current one and set a new one
_nextScene->cleanup();
_nextScene = nullptr;
}
ssize_t index = _scenesStack.size() - 1;
_sendCleanupToScene = true;
_scenesStack.replace(index, scene);
// set thisnew scene to be next scene
_nextScene = scene;
}