cpp-tests ClippingNodeTest--HoleDemo

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


    此例子是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 ;
		}
	}
}


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

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

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

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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值