cocos2dx 3.x 相机机制
一,3.x相机使用方法:
CCSize winSize=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
Vec3 eyePosOld=camera->getPosition3D();
Vec3 eyePos=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,view,model,...)
而在3.x中相机不是隶属于节点的,而是全局的,所以节点没有自己的局部view矩阵,只有一个起始的view矩阵,即矩阵堆栈将形如:(proj,view,model,model,model,...)
三,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(), cameras.end(), 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, Camera::_visitingCamera->getViewProjectionMatrix());
//visit the scene
visit(renderer, Mat4::IDENTITY, 0);
renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
Camera::_visitingCamera = nullptr;
}
上面函数逻辑看起来写得很墨迹,其实意思是:
对于这个Scene,用其_cameras中各相机分别render一遍。(不过它把默认相机的那遍render强制放到最后了,正是这个举动使代码变墨迹的)。
针对上面逻辑,需澄清下面两个问题:
问题1,关于“_cameras中各相机分别render一遍”:
问题来了,假如_cameras里有十个相机,那么Scene就要render十遍,这不坑爹吗?其实也不算很坑爹,因为每遍render只render与当前相机匹配的节点,所以总起来仍然是每个节点render一次(除非用户人为地为某节点创建了两个cameraFlg与此节点cameraMask相匹配的相机)。
从哪里可以看出每次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, _modelViewTransform, flags);
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://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;
}
问题2,关于“把默认相机的那遍render强制放到最后”:
不知道cocos团队为何要这样做,我还由于这个被坑了一次,经过如下:
我在layer上加了一个background sprite (zOrder=0),然后又加了一个layer2 (zOrder=1),其中layer2使用了自定义相机camera2。
本想让layer2显示在background sprite前面,但显示结果却是background会挡住layer2。
开始不解,然后去看引擎代码,发现此结果是Scene::render(...)函数的逻辑造成:
Scene::render(...)的逻辑是先用自定义相机去渲染使用它的相应节点,最后再用default相机渲染使用它的相应节点。
所以导致无论zOrder怎么设,都会先渲染使用自定义相机的layer2,然后再渲染使用默认相机的background sprite。
于是为了能够先渲染background sprite再渲染layer2,我只好又定义了一个camera1,让background sprite改为使用camera1(并且保证camera1是在camera2之前加到Scene:: _cameras中,即camera1先于camera2加到场景中),这样渲染效果就正常了。
cocos2dx 3.x 相机机制的更多相关文章
- cocos2dx的内存管理机制
首先我们必须说一下c++中变量的内存空间的分配问题,我们在c++中写一个类,可以在栈上分配内存空间也可以使用new在堆上分配内存空间,如果类对象是在栈上分配的内存空间,这个内存空间的管理就不是我们的事 ...
- Cocos2d-x之Log输出机制
| 版权声明:本文为博主原创文章,未经博主允许不得转载. 在cocos2d-x中,我们使用log这个函数进行输出,log可以输出很多参数,它的使用方式就和使用c语言中的printf的使用方式差不多 ...
- cocos2dx 3.0 触摸机制
在cocos2dx 3.0版本号中,废弃了以往2.x版本号的写法,我们先来看一下Layer.h中的一段代码 /* Callback function should not be deprecated, ...
- cocos2d-x 添加纹理自动回收机制
转自:http://www.cnblogs.com/lancidie/archive/2013/04/13/3019375.html 1.不是一个完整的模块,所以不提供完整代码,只提供思路和核心代码. ...
- Cocos2d-x之Touch事件处理机制
一.两种机制的四种不同的事件 CCStandardTouchDelegate 默认事件 virtual void ccTouchesBegan(CCSet *pTouches, CCEvent * ...
- [置顶] 【玩转cocos2d-x之二十】从CCObject看cocos2d-x的内存管理机制
原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/13765639 再看CCObject,剔除上节的拷贝相关,以及Lua脚本相关的 ...
- cocos2dx 3.2 事件机制
一个sprite的情况 // oneSprite void HelloWorld::touchableSpriteTestOne() { Vec2 origin = Director::getInst ...
- Cocos2d-x内存自动释放机制--透彻篇
首先在架构里面需要明白,如果使用new创建对象的话,我们需要自己释放内存,如果直接用引擎提供的警静态方法,我们可以不做内存管理,引擎自动处理,因为引擎背后有一个自动释放池.通过查看源码可以知道,每个静 ...
- 【Cocos2d-x 3.x】内存管理机制与源码分析
侯捷先生说过这么一句话 : 源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...
随机推荐
- dev -c++ 快捷键
转自:http://blog.csdn.net/abcjennifer/article/details/7259222 F8:开始调试 F7:进一步执行当前行,并跳到下一行 F4:添加查看 ctrl ...
- unity3d插件Daikon Forge GUI 中文教程-3-基础控件Button和Sprite的使用
(游戏蛮牛首发)大家好我是孙广东.官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上.要观看是须要FQ的. 只是教 ...
- 创建一个pre标签展开折叠的UI组件(原创)
这些天练习UI组件的编写,顺便模仿一个h5版本的pre标签收缩展开的效果组件: 兼容ie8.9,谷歌,火狐: 图片效果如下: demo.html代码: <!DOCTYPE html> &l ...
- 通过WebRTC实现实时视频通信(二)
通过WebRTC实现实时视频通信(一) 通过WebRTC实现实时视频通信(二) 通过WebRTC实现实时视频通信(三) 在上一篇文章中,我们讲解了WebRTC的概述.历史.安全性和开发者工具.接下来我 ...
- GraphQL返回分页对象
private GraphQLOutputType testUserOutputType; private GraphQLOutputType pageType; private void initO ...
- Oracle常用标准表
一.INV(库存) 子库存:mtl_secondary_inventories 事物处理:mtl_material_transactions mmt 事务处理来源类型:mtl_txn_source_t ...
- 腾讯云-搭建 WordPress 个人博客
搭建 WordPress 个人博客 准备 LNMP 环境 任务时间:30min ~ 60min LNMP 是 Linux.Nginx.MySQL 和 PHP 的缩写,是 WordPress 博客系统依 ...
- 【laravel5.4】{{$name}}、{{name}}、@{{$name}} 和 @{{name}} 的区别
1.前面带@符号的,表示不需要laravel的blade引擎进行解析:有@的,则需要blade解析 2.{{$name}} 表示:blade解析 后台php的 name变量 {{name}} 表示:b ...
- [转载]在rhel 6 x86_64 上安装oracle 11g xe
原文地址:在rhel 6 x86_64 上安装oracle 11g xe作者:pccom Oracle 11g xe for linux目前只有x86_64 版本,没有i386, i686 版本,如果 ...
- [转]TCP(HTTP)长连接和短连接区别和怎样维护长连接
原文链接 一.HTTP协议和TCP协议 HTTP的长连接和短连接本质上是TCP长连接和短连接.HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议.IP协议主要解决网络路由和寻址问题, ...