cocos2dx 3.x 相机机制
http://www.cnblogs.com/wantnon/p/4158135.htmlv一,3.x相机使用方法: CCSizewinSize=CCDirector::sharedDirector()->getWinSize(); Camera*camera=Camera::create(); camera->setCameraFlag(CameraFlag::USER1); this->addChild(camera); sprite->setCameraMask(2); //因为2 & CameraFlag::USER1 !=0,所以cameraMask=2与CameraFlag::USER1匹配,sprite将使用上面创建的camera Vec3eyePosOld=camera->getPosition3D(); Vec3eyePos=Vec3(x,y,eyePosOld.z); camera->setPosition3D(eyePos); assert(eyePos.z>0); camera->lookAt(Vec3(eyePos.x,eyePos.y,0),Vec3(0,1,0)); 注意,这里有个坑:camera->lookAt必须在camera->setPostion3D之后,因为lookAt中有一句 Vec3::subtract(this->getPosition3D(),lookAtPos,&zaxis),即相减得出相机空间z轴, 使用了getPosition3D。所以必须先设定好position3D再调lookAt才能得到正确结果。 参考: http://www.cocos2d-x.org/news/344 cocos2d_tests - Camera3DTest.cpp 二,3.x与2.x相机的差别: cocos2dx 3.x中的相机机制与cocos2dx 2.x中差别很大。 在2.x中每个节点都有camera,所以每个节点都有自己的局部view矩阵。矩阵堆栈将形如:(proj,view,model,...) 而在3.x中相机不是隶属于节点的,而是全局的,所以节点没有自己的局部view矩阵,只有一个起始的view矩阵,即矩阵堆栈将形如:(proj,...) 三,3.x相机实现原理: 前面已经看到,使用3.x相机关键有三点: 1,用户自己创建相机并指定cameraFlag。 2,为节点指定与cameraFlg按位与不为0的cameraMask,则此节点即使用此相机。 3,相机可addChild到任意一个节点(尽量使用根节点)。 自定义相机的cameraFlag可取USER1~USER8,定义如下: enum class CameraFlag { DEFAULT = 1, USER1 = 1 << 1, USER2 = 1 << 2, USER3 = 1 << 3, USER4 = 1 << 4, USER5 = 1 << 5, USER6 = 1 << 6, USER7 = 1 << 7, USER8 = 1 << 8, }; Node::setCameraMask(unsigned short mask,bool applyChildren)用来指定cameraMask,其第二个参数用来指明子节点是否递归地使用相同的mask,默认为true。要特别注意:node->setCameraMask(mask,true)只能使node的当前所有子节点的cameraMask设置为mask,但在此之后新添加的子节点则不会受影响(仍然是默认camera),需要记着手动进行设置,这里很容易被坑。又比如在Layer::init()里开头写了一句this->setCameraMask(mask,true)紧接着加了些子节点,那么要意识到这些子节点是不会被设置的,要么对每个子节点都手动调一次setCameraMask,要不就把this->setCameraMask写到init函数的末尾。 不管camera被addChild到哪个节点,其都会被添加到Scene的_cameras成员中。如果Scene中任何一个节点都没有添加用户自定义相机,则scene的_cameras成员中只含有一个默认相机,就是Scene::_defaultCamera所引用的相机;如果Scene中某些节点添加了用户自定义相机,则_cameras[0]是默认相机,其余元素是用户相机。 至于camera是如何被添加到_cameras中的,逻辑在Camera的onEnter函数中,如下: void Camera::onEnter() { if (_scene == nullptr) { auto scene = getScene(); if (scene) setScene(scene); } Node::onEnter(); } void Camera::setScene(Scene* scene) { if (_scene != scene) { //remove old scene if (_scene) { auto& cameras = _scene->_cameras; auto it = std::find(cameras.begin(),cameras.end(),this); if (it != cameras.end()) cameras.erase(it); _scene = nullptr; } //set new scene if (scene) { _scene = scene; auto& cameras = _scene->_cameras; auto it = std::find(cameras.begin(),this); if (it == cameras.end()) _scene->_cameras.push_back(this); } } } 下面解释3.x是如何实现相机与节点通过cameraFlag/cameraMask值进行配对儿的: 只需看Scene::render(Renderer* renderer)函数: void Scene::render(Renderer* renderer) { auto director = Director::getInstance(); Camera* defaultCamera = nullptr; for (const auto& camera : _cameras) { Camera::_visitingCamera = camera; if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT) { defaultCamera = Camera::_visitingCamera; continue; }
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,Camera::_visitingCamera->getViewProjectionMatrix());
//visit the scene visit(renderer,Mat4::IDENTITY,0); renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); } //draw with default camera if (defaultCamera) { Camera::_visitingCamera = defaultCamera; director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,0); renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); } Camera::_visitingCamera = nullptr; } 上面函数逻辑看起来写得很墨迹,其实意思很简单,就是: 对于这个Scene,用其_cameras中各相机分别render一遍。 那么问题来了,假如_cameras里有十个相机,那么Scene就要render十遍,这不坑爹吗?其实也不算很坑爹,因为每遍render只render与当前相机匹配的节点,所以总起来仍然是每个节点render一次。 从哪里可以看出每次render只render与当前相机匹配的节点呢?下面Node::visit(...)中的bool visibleByCamera = isVisitableByVisitingCamera()一句就是这个作用。 void Node::visit(Renderer* renderer,const Mat4 &parentTransform,uint32_t parentFlags) { // quick return if not visible. children won't be drawn. if (!_visible) { return; } 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* director = Director::getInstance(); 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 for( ; i < _children.size(); i++ ) { auto node = _children.at(i); if ( node && node->_localZOrder < 0 ) node->visit(renderer,_modelViewTransform,flags); else break; } // self draw if (visibleByCamera) this->draw(renderer,flags); for(auto it=_children.cbegin()+i; it != _children.cend(); ++it) (*it)->visit(renderer,flags); } else if (visibleByCamera) { this->draw(renderer,flags); } director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0?? // Please refer tohttps://github.com/cocos2d/cocos2d-x/pull/6920 // reset for next frame // _orderOfArrival = 0; } bool Node::isVisitableByVisitingCamera() const { auto camera = Camera::getVisitingCamera(); bool visibleByCamera = camera ? (unsigned short)camera->getCameraFlag() & _cameraMask : true; return visibleByCamera; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |