~~~~我的生活,我的点点滴滴!!
此例子是ClippingNode中一个例子,说实话我还没有弄太明白,先暂时把自己的一点点理解放这里,ClippingNode是利用模板遮罩来完成
对Node区域裁剪的技术,那么我们要先理解一下遮罩是个什么样子的东西?看下图:
所谓模板,就是一个形状,透过该形状可看到底板上的图层,如果底板上没有任何内容,则直接看到Layer上的内容,而底板上的东西又不会
妨碍Layer上的东西,即模板在底板之外的空间对于Layer来说是透明的。假如有个精灵在模板之外的底板上运动,我们是看不到它的。除非它进入
模板的区域,我们才能看到它。这说得我自己都不懂,像绕口令!
我们看看HoleDemo是什么样子的?
鼠标点击上面的图片,然后产生像中弹一样的效果,然后会留下疑似弹孔的痕迹。此实例用到了两层模版遮罩处理,第一层是弹孔遮罩,用弹孔图
遮住弹痕图。
实际使用时并不会为每个子弹都创建一个模版遮罩结点,而是将所有的弹孔放在一个结点中,并用此结点做为模板遮罩。第二层是背景图的区域遮罩,
让脱靶的子弹不产生弹孔。
看看代码是如何进行的(虽然代码不多,但是看得我是焦头烂额啊)
//背景的网格线是一张图片,并不是时时画的 bool BaseClippingNodeTest::init() { if (BaseTest::init()) { //在这里添加了一张网格图片当背景 auto background = Sprite::create(s_back3); background->setAnchorPoint( Vec2::ZERO ); background->setPosition( Vec2::ZERO ); this->addChild(background, -1); this->setup(); return true; } return false; } //释放掉所有资源 HoleDemo::~HoleDemo() { CC_SAFE_RELEASE(_outerClipper); CC_SAFE_RELEASE(_holes); CC_SAFE_RELEASE(_holesStencil); }
//模版遮罩 void HoleDemo::setup() { auto target = Sprite::create(s_pathBlock); target->setAnchorPoint(Point::ZERO); log("before %lf, %lf", target->getContentSize().width, target->getContentSize().height); //放大3倍 target->setScale(3); //放大与缩小是不会改变物体原始的size大小的。 log("after %lf, %lf", target->getContentSize().width, target->getContentSize().height); _outerClipper = ClippingNode::create(); _outerClipper->retain(); //仿射变换,搜索全文,你会发现找不到这个仿射对象在哪初始化的,根进源码才发现 IDENTITY 为 //静态成员变量,那就意味着,他在此对象产生前就已经初始化好了,我们看下他的源码: /************************************************************************ * * AffineTransform AffineTransformMakeIdentity() * { * return __CCAffineTransformMake(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); * } * extern const AffineTransform AffineTransformIdentity = AffineTransformMakeIdentity(); * const AffineTransform AffineTransform::IDENTITY = AffineTransformMakeIdentity(); * ************************************************************************/ //这样看来,这里这样使用就理所当然了,统一了仿射标准矩阵,即单位矩阵。 AffineTransform tranform = AffineTransform::IDENTITY; //背景图结点按x轴与y轴都扩大3倍,仿射拉伸。 tranform = AffineTransformScale(tranform, target->getScale(), target->getScale()); //SizeApplyAffineTransform根据仿射矩阵来调整坐标值,得到目标坐标,此处是拉伸。 _outerClipper->setContentSize( SizeApplyAffineTransform(target->getContentSize(), tranform)); _outerClipper->setAnchorPoint( Point(0.5, 0.5) ); _outerClipper->setPosition(Point(this->getContentSize()) * 0.5f); _outerClipper->runAction(RepeatForever::create(RotateBy::create(1, 45))); //将背景图结点设置为此ClippingNode的模版缓冲遮罩结点。 //也就是对layer层使用了遮罩 _outerClipper->setStencil( target ); //创建一个裁剪结点 auto holesClipper = ClippingNode::create(); //设置它在模版缓冲运算时按反向处理,通过这个剪裁节点,我们将在效果图中抠出来一个图形 holesClipper->setInverted(true); //设置ALPHA的测试参考值,ALPHA的测试参考值,用于进行ALPHA测试比较所用,一般比较算法为小于此值的像素直接会被舍弃。 //这样就可以实现图像的镂空,这样运行后的效果就像是产生了一个洞一样。 holesClipper->setAlphaThreshold( 0.05f ); holesClipper->addChild(target); //创建用于包含所有弹痕的结点_holes _holes = Node::create(); _holes->retain(); //将_holes放入到ClippingNode中做为要遮挡的结点 holesClipper->addChild(_holes); //再创建一个用于包含所有弹孔的结点holesClipper _holesStencil = Node::create(); _holesStencil->retain(); //ClippingNode设置_holesStencil做为模版遮罩结点 holesClipper->setStencil( _holesStencil); //将第二个创建的ClippingNode放入第一个创建的ClippingNode做为被遮罩影响的结点 _outerClipper->addChild(holesClipper); //将第一个ClippingNode放入当前层中 this->addChild(_outerClipper); auto listener = EventListenerTouchAllAtOnce::create(); listener->onTouchesBegan = CC_CALLBACK_2(HoleDemo::onTouchesBegan, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); }
创建两个ClippingNode结点,一个是子弹的痕迹,也就是弹孔“周围的环境”在这里是_holes,另一个是弹孔,也就是透过去的那个“小眼”在这里
是_holesStencil(这就是我们模板--遮罩),我们要把弹孔也就是小眼放到弹痕上,然后在把弹痕放到裁剪区域图上(_outerClipper)。
//产生弹孔效果代码 void HoleDemo::pokeHoleAtPoint(Vec2 point) { //随即产生缩放效果与旋转角度, 这样看起来有打击感和自然感 float scale = CCRANDOM_0_1() * 0.2 + 0.9; float rotation = CCRANDOM_0_1() * 360; //开始蒙皮, 看得不是太懂,所以这里的注释写的少 auto hole = Sprite::create("Images/hole_effect.png"); hole->setPosition( point ); hole->setRotation( rotation ); hole->setScale( scale ); //加进来,到时候好释放,不然那一块空间永远无法访问了 _holes->addChild(hole); auto holeStencil = Sprite::create("Images/hole_stencil.png"); holeStencil->setPosition( point ); holeStencil->setRotation( rotation ); holeStencil->setScale( scale ); //加进来,到时候好释放,不然那一块空间永远无法访问了 _holesStencil->addChild(holeStencil); _outerClipper->runAction(Sequence::createWithTwoActions(ScaleBy::create(0.05f, 0.95f), ScaleTo::create(0.125f, 1))); }
void HoleDemo::onTouchesBegan(const std::vector<Touch*>& touches, Event* event) { Touch *touch = (Touch *)touches[0]; //cocos2dx和opengl使用的是相同的坐标系 Vec2 screenPoint = touch->getLocationInView(); Vec2 UIToGL = Director::getInstance()->convertToGL(screenPoint); //上面两行看似复杂的代码其实直接用touch->getLocation();就行了 Vec2 point = _outerClipper->convertToNodeSpace(UIToGL); auto rect = Rect(0, 0, _outerClipper->getContentSize().width, _outerClipper->getContentSize().height); if (!rect.containsPoint(point)) return; this->pokeHoleAtPoint(point); //下面这个方法其实和上面一样的 if( touch ) { Vec2 localPoint = touch->getLocation(); //但是setPosition使用是他的父类node的相对坐标,所以下面的值还是要转换成NodeSpace Vec2 p = _outerClipper->convertToNodeSpace(localPoint); Rect layerRect = Rect(0, 0, _outerClipper->getContentSize().width, _outerClipper->getContentSize().height); if( layerRect.containsPoint(p) ) { log("xx"); pokeHoleAtPoint(p); } else { return ; } } }
Rect(0, 0, _outerClipper->getContentSize().width, _outerClipper->getContentSize().height);
他巧妙的把点击的坐标转换成了以自己为坐标系相对自己的坐标,那么这种情况就不会产生了。