cpp-tests ClippingNodeTest--HoleDemo

来源:互联网 发布:阿里云服务器无法联网 编辑:程序博客网 时间:2024/05/16 14:03

~~~~我的生活,我的点点滴滴!!


    此例子是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的相对坐标,所以下面的值还是要转换成NodeSpaceVec2 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 ;}}}


     看上面的onTouchesBegan函数,我最开始会以为有一个bug,就是当精灵旋转到45度后,我点击4个角时,他不会正确的响应,因为看区域函数

Rect(0, 0, _outerClipper->getContentSize().width, _outerClipper->getContentSize().height);

他产生的效果是一个规整的形状(也就是与x轴平行的一个区域)那么当物体旋转45度后,4个角就已经挤出这个区域,应该是对象的长度了,但是了,

他巧妙的把点击的坐标转换成了以自己为坐标系相对自己的坐标,那么这种情况就不会产生了。




0 0