【Cocos2dx 3.3 Lua】剪裁结点ClippingNode

参考资料:

   http://blog.csdn.net/jackystudio/article/details/17160973

 

【ClippingNode】

1、原理

    ClippingNode(裁剪节点)可以用来对节点进行裁剪。ClippingNode是Node的子类,可以像普通节点一样放入Layer,Scene,Node中。

    主要是根据一个模板(Stencil)切割图片的节点,生成任何形状的节点显示。

    ClippingNode是利用模板遮罩来完成对Node区域裁剪的技术。

    如何理解ClippingNode的遮罩?看下图的例子吧。

wKiom1Q2LVXAUAoEAACGmnfvB2U591.jpg

 

2、举例说明

    模板(Stencil):可以使用Layer、Node、Sprite等。

    底板           :可以使用Layer、Node、Sprite等。

    Layer层

 

  2.1、第一组(Layer层无背景图片)

    > 模板(Stencil):模板为Node节点,放入5个Sprite的小球

    > 底板           :底板为Node节点,放入1个Sprite的ABCD图

    > Layer层        :无元素,背景颜色为黑色

wKioL1Q2Po3BRAGQAAA0HQ88DTw552.jpg            wKiom1Q2PlXB8lS9AABDigaXvj0088.jpg

 

    > 裁剪遮罩效果示意图:

    wKioL1Q2QaHgGl_MAAAqz_2tsso946.jpg

 

  2.2、第二组(Layer层有背景图片)

    > 模板(Stencil):模板为Node节点,放入5个Sprite的小球

    > 底板           :底板为Node节点,放入1个Sprite的ABCD图

    > Layer层        :有一个Sprite的cocos2dx背景图片

wKioL1Q2QFjTWjVzAAA0HQ88DTw670.jpg            wKioL1Q3cdTioMMRAABDigaXvj0272.jpg

 

wKiom1Q2QCDzGsnXAAFHbk8yBjQ276.jpg

    

    > 裁剪遮罩效果示意图:

wKiom1Q2QdOz9Uz_AAFU71cfm4M930.jpg

 

  2.3、分析总结

    通过ClippingNode进行裁剪遮罩,其实是这样的:

        > 将模板(Stencil)上所有元素的形状集合作为“形状模板”,其元素本身不渲染。

        > 使用“形状模板”对底板进行裁剪。

        > 显示从底板上裁剪下来的图片区域。

    总的来说:

        > 模板(Stencil)相当于是一个样板,上面有很多不同形状的"洞洞"。

        > 然后根据样板,对底板进行裁剪,"挖洞"。

        > 然后将剪下来的那些碎片,按照原来的位置进行摆放。

    其中:模板(Stencil)只是一个“形状模板”,本身的图片是不进行绘制的。

 

3、主要函数

    ClippingNode继承于Node类,用于节点的裁剪与遮罩。

 

  3.1、创建ClippingNode

    两种方式:是否使用模板(stencil)来创建。

1
2
3
4
5
6
7
//
    //创建,不含模板(stencil)
    ClippingNode* clippingNode = ClippingNode::create();
 
    //创建,使用模板(stencil)
    ClippingNode* clippingNode = ClippingNode::create(stencil);
//

    

  3.2、设置模板(Stencil)

    模板节点是Node的子类,一般常常使用DrawNode,因为它可以绘制不同形状的图形。当然也可以直接使用Node节点作为作为模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
/**
 *     用来做裁剪的模板(stencil)节点(Node)
 *     模板(stencil)对象,默认为空(nullptr)
 **/
    Node* stencil = Node::create();    //模板stencil节点Node
     
    stencil->addChild(spriteBall1);    //添加小球1
    stencil->addChild(spriteBall2);    //添加小球2
    stencil->addChild(spriteBall3);    //添加小球3
    stencil->addChild(spriteBall4);    //添加小球4
    stencil->addChild(spriteBall5);    //添加小球5
     
    clippingNode->setStencil(stencil); //设置模板Stencil
//

 

  3.3、设置底板(Content)

1
2
3
4
//
    //创建ClippingNode后,使用addChild()添加的节点,即为底板内容
    clippingNode->addChild(content); //设置底板
//

 

  3.4、倒置显示(Inverted)

    > false :显示被模板裁剪下来的底板内容。默认为false。

    > true  :显示剩余部分。

1
2
3
4
5
//
    //默认为false
    //表示显示被裁剪下来的底板内容
    clippingNode->setInverted(false);
//

 

  3.5、alpha阈值(alphaThreshold)

    alpha:表示像素的透明度值。

    > 只有模板(stencil)中像素的alpha值大于alpha阈值时,内容才会被绘制。

    > alpha阈值(alphaThreshold):取值范围[0,1]

    > 默认为 1 ,表示alpha测试默认关闭,即全部绘制。

    > 若不是1  ,表示只绘制模板中,alpha像素大于alphaThreshold的内容。

1
2
3
4
5
//
    //设置alpha透明度闸值
    //即显示模板中,alpha像素大于0.05的内容
    holesClipper->setAlphaThreshold(0.05f); 
//

  具体说明:

    以下是一张40*40的图片,其中小球以外的其他区域像素为透明的(即:alpha为 0 )。

    wKiom1Q2UjvQHtewAAALmrZIcP4953.jpg

    (1)在不设置AlphaThreshold闸值或者setAlphaThreshold(1.0f),的情况下:

    wKioL1Q2U-CyKJR5AABse9r4z7I334.jpg

 

    (2)在设置setAlphaThreshold(0.5f),的情况下:

    wKiom1Q2U6iTDl2eAABkCos2yS8384.jpg

    (3)结论:

        > 可以发现在不设置alpha闸值时,模板绘制的区域为一个40*40的矩形。

        > 设置了alpha闸值为0.5时,透明度alpha为0的像素不被绘制,只绘制了一个小圆。

 


 

具体实例:

1、子弹穿孔
    1.1 创建子弹的ClippingNode
 
HoleClipping.lua

Link: http://codepad.org/jmwuH8CM    [ raw code | fork ]  
HoleClipping=class("HoleClipping",function()
    return cc.ClippingNode:create()
end)

HoleClipping.ctor=function(self)
    self:setInverted(true)
    self:setAlphaThreshold(0.5)

    self.stencil=cc.Node:create()
    self.holes=cc.Node:create()

    self:setStencil(self.stencil)
    self:addChild(self.holes)
end

--在指定点添加子弹孔
HoleClipping.addHole=function(self,point)
    self.rotate=math.random(0,1)*360    --旋转角度
    self.scale=math.random(0,1)*0.2+0.9 --缩放

    local stencil=function()
        local sprite=cc.Sprite:create("Images/hole_stencil.png")
        sprite:setPosition(point.x,point.y)
        sprite:setScale(self.scale)
        sprite:setRotation(self.rotate)
        return sprite
    end

    local content=function()
        local sprite=cc.Sprite:create("Images/hole_effect.png")
        sprite:setPosition(point.x,point.y)
        sprite:setScale(self.scale)
        sprite:setRotation(self.rotate)
        return sprite
    end

    self.holes:addChild(content())
    self.stencil:addChild(stencil())
end

--添加剪裁内容
HoleClipping.addContent=function(self,content)
    self.holes:addChild(content)
end

HoleClipping.create=function()
    local clip=HoleClipping.new()
    return clip
end

return HoleClipping

 

 
Background.lua

Link: http://codepad.org/C13kJ4vP    [ raw code | fork ]

Background=class("Background",function()
    return cc.ClippingNode:create()
end)

Background.ctor=function(self)
    local size=cc.Director:getInstance():getWinSize()
    self:setPosition(size.width/2,size.height/2)
    self:setAnchorPoint(0.5,0.5)
    local action=cc.RotateBy:create(1,90)
    self:runAction(cc.RepeatForever:create(action))

    self:setStencil(self:block())
end

--背景图片
Background.block=function(self)
    local sprite=cc.Sprite:create()
    sprite:setTexture("Images/blocks.png")
    sprite:setScale(3)
    return sprite
end

Background.create=function()
    local sprite=Background.new()
    return sprite
end

return Background

 

HoleLayer.lua
Link: http://codepad.org/FZKuKRDS    [ raw code | fork ]
HoleLayer=class("HoleLayer",function()
    return cc.LayerColor:create(cc.c4b(0,60,30,255))
end)

HoleLayer.ctor=function(self)
    local function onTouchBegin(touch,event)
        if self:onTouchBegin(touch,event) then
            return true
        end
        return false
    end

    local function onTouchMoved(touch,event)
        self:onTouchMoved(touch,event)
    end

    local function onTouchEnded(touch,event)
        self:onTouchEnded(touch,event)
    end
    local listener=cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegin,cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)
    listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)
    local dispacher=cc.Director:getInstance():getEventDispatcher()
    dispacher:addEventListenerWithSceneGraphPriority(listener,self)

    --添加BackgrounClipping
    local node=require("sprite/clipping/Background")
    self.outerClip=node.create()
    
    --添加子弹HoleClip
    local hole=require("sprite/clipping/HoleClipping")
    self.holeClip=hole.create()
    self.holeClip:addContent(self.outerClip:block())
    
    self.outerClip:addChild(self.holeClip)
    self:addChild(self.outerClip)
end

HoleLayer.onTouchBegin=function(self,touch,event)
    cclog("<HoleLayer.onTouchBegin>")
    return true
end

HoleLayer.onTouchMoved=function(self,touch,event)

end

HoleLayer.onTouchEnded=function(self,touch,event)
    local point=touch:getLocation() --返回的是WordSpace坐标
    self.holeClip:addHole(self.holeClip:convertToNodeSpace(point))  --转换成NodeSpace
    
    --outerClip特效
    local action1=cc.ScaleBy:create(0.05,0.95)
    local action2=cc.ScaleTo:create(0.1,1)
    self.outerClip:runAction(cc.Sequence:create(action1,action2))
end

HoleLayer.create=function()
    local layer=HoleLayer.new()
    return layer
end

return HoleLayer

 

 
执行效果:
    
 
 
2、闪烁字实现
    ClipFont.lua
 
   [ raw code | fork ]
 

Lua, pasted 2 seconds ago: 
ClipFont=class("ClipFont",function() return cc.ClippingNode:create() end) ClipFont.init=function(self) self:setInverted(false) self:setAlphaThreshold(0.5) local size=cc.Director:getInstance():getWinSize() local loadTitle=function() local title=cc.Sprite:create() title:setTexture("clipfont/game_title.png") title:setPosition(size.width/2,size.height/2) return title end local loadSpark=function() local spark=cc.Sprite:create() spark:setTexture("clipfont/spark.png") spark:setAnchorPoint(1,1) spark:setPosition(0,size.height) local move_end=cc.MoveTo:create(1,cc.p(size.width,size.height)) local move_begin=cc.MoveTo:create(1,cc.p(0,size.height)) local seq=cc.Sequence:create(move_end,move_begin) spark:runAction(cc.RepeatForever:create(seq)) return spark end local title=loadTitle() local spark=loadSpark()  self:addChild(title,1) self:addChild(spark,2) self:setStencil(title)  --ClippingNode的模板是特效字 end ClipFont.create=function(self) local node=ClipFont.new() node:init() return node end return ClipFont 
闪烁字效果:
    

3、新手指引
    ClipGuide.lua

Link: http://codepad.org/7ZMoNJz2    [ raw code | fork ]
ClipGuide=class("ClipGuide",function()
    return cc.ClippingNode:create()
end)

ClipGuide.init=function(self)
    local size=cc.Director:getInstance():getWinSize()
    local loadTexture=function(texture,position)
        local sprite=cc.Sprite:create()
        sprite:setTexture(texture)
        sprite:setPosition(position.x,position.y)
        return sprite
    end

    --模板
    local stencilPosition=cc.p(size.width/2,size.height/2)
    local stencil=loadTexture("clipguide/CloseSelected.png",stencilPosition)
    local stencilSize=stencil:getBoundingBox()
    stencil:setScale(1.5)
    self.stencil=stencil

    --背景图层
    local layer=cc.LayerColor:create(cc.c4b(0,0,0,200),size.width,size.height)

    self:setInverted(true)
    self:setAlphaThreshold(1)
    self:setStencil(stencil)
    self:addChild(layer)
end

ClipGuide.getStencilRect=function(self)
    local pointX=self.stencil:getPositionX()
    local pointY=self.stencil:getPositionY()
    local size=self.stencil:getBoundingBox()
    return cc.rect(pointX-size.width/2,pointY-size.height/2,size.width,size.height)
end

ClipGuide.create=function(self)
    local clip=ClipGuide.new()
    clip:init()
    return clip
end

return ClipGuide
BackLayer.lua

Link: http://codepad.org/P7ZpcqTW    [ raw code | fork ]
BackLayer=class("BackLayer",function()
    return cc.Layer:create()
end)

BackLayer.ctor=function(self)
    local size=cc.Director:getInstance():getWinSize()
    self.size=size
    local sprite=cc.Sprite:create("clipguide/HelloWorld.png")
    sprite:setPosition(size.width/2,size.height/2)
    self:addChild(sprite)

    --添加ClippingNode遮罩
    local clip=require("sprite.clipping.guide.ClipGuide")
    self.clip=clip.create()
    self:addChild(self.clip)

    --添加提示标志
    self.tip=self:guideSprite()
    self:addChild(self.tip)

    --添加监听事件
    self:event()
end

BackLayer.guideSprite=function(self)
    --提示标志
    local guide=cc.Sprite:create()
    guide:setTexture("clipguide/tip.png")
    guide:setPosition(self.size.width/2-55,self.size.height/2+40)
    guide:setScale(0.4)
    guide:setRotation(60)

    --提示标志动作
    local scale1=cc.ScaleBy:create(0.25,0.95)
    local scale2=cc.ScaleTo:create(0.25,0.4)
    local action=cc.Sequence:create(scale1,scale2)
    guide:runAction(cc.RepeatForever:create(action))

    return guide
end

BackLayer.event=function(self)
    local function onTouchBegin(touch,event)
        return true
    end

    local function onTouchMoved(touch,event)
    end

    local function onTouchEnded(touch,event)
        local location=touch:getLocationInView()  -- touch in screen
        local glPoint=cc.Director:getInstance():convertToGL(location)
        if self.clip and self.tip then
            local rect=self.clip:getStencilRect()
            if cc.rectContainsPoint(rect,glPoint) then
                self:removeChild(self.clip,true)
                self:removeChild(self.tip,true)
                self.clip,self.tip=nil,nil
            end
        end
    end

    local listener=cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegin,cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)
    listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)

    local dispacher=cc.Director:getInstance():getEventDispatcher()
    dispacher:addEventListenerWithSceneGraphPriority(listener,self)
end

BackLayer.create=function()
    local layer=BackLayer.new()
    return layer
end

return BackLayer

 

执行效果:
    

posted @ 2015-03-15 12:15  罗松超  阅读(11161)  评论(0编辑  收藏  举报