在cocos2d-x中实现橡皮擦功能
How to make an eraser in cocos2d-x?
cocos2d-x 是使用 OpenGL ES 来渲染的,实现橡皮擦,需要一点点 OpenGL 知识。
/* 下面是可以跳过不看的废话。
是的,不需要 OpenGL ,我们也能使用 cocos2d-x 制作出游戏。至少我接触过的几个团队都是这么干的,有的团队中甚至无人了解 C++。
但是,在我学习 cocos2d-x 的这几个月里,我发现不学习 OpenGL ES 对我来说是无法想象的。在看源码的时候,你不能碰到 OpenGL 就无视它们,对程序员来说这是罪过。
这不难,Trust me.
废话结束 */
依赖
本文基于 cocos2d-x 2.2.2
效果截图
原理
先将要被擦除的像素渲染到 FrameBuffer 中,然后使用 Alpha 为 0 的像素块与已有像素做混合,将已有的像素替换成 Alpha 为 0 的像素即可完成擦除。
注意,这里说的“擦除”,准确的描述是“擦除到透明”。对于不透明画布(例如白色背景)的擦除,只需要使用画布的背景颜色进行绘制就行了,本文并不讨论。
我们并不需要真的去考虑 FrameBuffer 的建立和渲染,cocos2d-x 已经为我们准备好了 CCRenderTexture 类。
使用 CCRenderTexture ,我们可以方便地完成绘制工作,比使用 Shader 要灵活,弱点可能就是性能较低。
设置混合模式,可使用 CCNode 提供的 ccBlendFunc 这个预定义类型。
C++代码
这里仅贴出主要代码,详细的代码请在本文最后下载。
本文代码使用 cocos2d-x project_creator 创建的项目修改。
1. 以下代码位于 HelloWorldScene.cpp 文件的 init 方法中,详细的内容看注释。
1//启用触摸支持
2this->setTouchMode(kCCTouchesOneByOne);
3this->setTouchEnabled(true);
4//... 其它初始化内容
5//...
6// 显示背景图片,方便查看“擦除到透明”效果
7CCPoint center = ccp(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y);
8CCSprite* pSprite = CCSprite::create("HelloWorld.png");
9pSprite->setPosition(center);
10this->addChild(pSprite, 0);
11
12// 创建一个橡皮擦,注意颜色的设置是全透明黑色
13pEraser = CCDrawNode::create();
14pEraser->drawDot(ccp(0, 0), 20, ccc4f(0, 0, 0, 0));
15pEraser->retain();
16
17// 创建画布,并显示它
18pRTex = CCRenderTexture::create(visibleSize.width, visibleSize.height);
19pRTex->setPosition(ccp(visibleSize.width/2, visibleSize.height/2));
20this->addChild(pRTex, 10);
21
22// 创建被擦除的内容,将其渲染到画布上
23CCSprite* pBg = CCSprite::create("dirt.png");
24pBg->setAnchorPoint(ccp(0.5,0.5));
25pBg->setPosition(center);
26pRTex->begin();
27pBg->visit();
28pRTex->end();
2. 下面是执行擦除逻辑。代码位于 ccTouchMoved 方法中。
1// 获取触摸坐标并移动橡皮擦到该坐标
2CCPoint touchPoint = touch->getLocation();
3pEraser->setPosition(touchPoint);
4
5// 设置混合模式
6ccBlendFunc blendFunc = { GL_ONE, GL_ZERO };
7pEraser->setBlendFunc(blendFunc);
8
9// 将橡皮擦的像素渲染到画布上,与原来的像素进行混合
10pRTex->begin();
11pEraser->visit();
12pRTex->end();
关于混合
混合(blend) 发生在 OpenGL 将像素渲染 FrameBuffer 之前。我们使用的混合模式 {GL_ONE, GL_ZERO}
的含义如下:
完全使用源像素(橡皮擦)的内容,替换掉目标像素(被擦除画面)对应坐标的内容。
假设源像素(橡皮擦)的颜色值为 Rs, Gs, Bs, As
,目标像素的颜色值为 Rd, Gd, Bd, Ad
,那么使用上面的混合模式,最终的像素值为:
(Rs*1 + Rd*0), (Gs*1 + Gd*0), (Bs*1 + Bd*0), (As*1 + Ad*0)
从上面的公式可以看出,最终的结果与橡皮擦原来的颜色值完全相同。
quick-cocos2d-x lua代码
lua代码依赖我forked的 quick-cocos2d-x 。
display.newSolidCircle 方法,目前并没有收录进入 dualface 的官方 quick-cocos2d-x 版本。
1local __touchPoint = cc.p(0, 0)
2local __sp = display.newSolidCircle(20, {fillColor=cc.c4f(0,0,0,0)})
3local __bf = ccBlendFunc:new()
4__bf.src = GL_ONE
5__bf.dst = GL_ZERO
6__sp:setBlendFunc(__bf)
7
8local __rTex = CCRenderTexture:create(display.width, display.height)
9__rTex:begin()
10__sp:align(display.CENTER, __touchPoint.x, __touchPoint.y)
11__sp:visit()
12__rTex:endToLua()
13
14__rTex:setPosition(display.cx, display.cy)
15self:addChild(__rTex)
项目下载
请将此项目放在 cocos2d-x 中的 projects 目录下。
参考文章
- Eraser in OpenGL ES iphone
- Blending a texture to erase alpha values softly with OpenGL
- "Scratch Off" effect (mask) CCRenderTexture + CCSprite instead of a solid color
- 文章ID:2067
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/how_to_make_an_eraser_in_coco2d-x/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。