Cocos Creator Shader实践 :手写一个搓扑克牌的效果(六)

今天来实现触摸控制搓牌,先来点熟悉的JS代码。

触摸事件

我们在HelloWorld.js中添加触摸事件,onLoad中添加以下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
this.node.on(cc.Node.EventType.TOUCH_MOVE, (touch) => {
var location = touch.getLocation();
//计算触摸点与原点(0,0)连线,过其连线中点,并与连线垂直的直线
//注:以扑克牌左下角为原点,需平移(-180,-70)
location.x += -180;
location.y += -70;
//触摸点与原点连线为y=location.y/location.x * x;
//斜率
var k = location.y / location.x;
//连线中点
var mid = cc.v2(location.x / 2, location.y / 2);
//已知斜率+过一点,y-mid.y=-1/k(x - mid.x)
//1/k*x+y-mid.y-mid.x/k=0 由此可知
this.UNI_A = 1 / k;
this.UNI_B = 1;
this.UNI_C = -mid.y - mid.x / k;
}, this);

这里出现的三个变量UNI_A,UNI_B,UNI_C就是我们要传递给shader的。这三个变量是与 触摸点到原点连线L垂直,并且过L中点的直线的三个值,即:
UNI_Ax + UNI_By + UNI_C = 0;

斜率+一点求直线公式

大家根据上图公式推导即可。

传递变量

外部代码给Shader传递变量,分两步即可:

  1. 获取变量地址
  2. 为变量赋值

使用代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
this.glProgram.UNI_A = gl.getUniformLocation(this.glProgram.getProgram(), "UNI_A");
this.glProgram.UNI_B = gl.getUniformLocation(this.glProgram.getProgram(), "UNI_B");
this.glProgram.UNI_C = gl.getUniformLocation(this.glProgram.getProgram(), "UNI_C");

//默认值
this.UNI_A = 1;
this.UNI_B = 1;
this.UNI_C = 0;

//重写Draw方法
this._glNode.draw = function () {
//启用面剔除
gl.enable(gl.CULL_FACE);
// gl.cullFace(gl.FRONT);//此函数可以使正反面颠倒
this.glProgram.use();
this.glProgram.setUniformsForBuiltins();
this.glProgram.setUniformLocationF32(this.glProgram.UNI_A, this.UNI_A);
this.glProgram.setUniformLocationF32(this.glProgram.UNI_B, this.UNI_B);
this.glProgram.setUniformLocationF32(this.glProgram.UNI_C, this.UNI_C);
//绑定卡背纹理
gl.bindTexture(gl.TEXTURE_2D, this._spCardBack.name);
this.addVertsTexCoord(this._backVertsBuffer, this._backTexsBuffer);
//绑定卡面纹理
gl.bindTexture(gl.TEXTURE_2D, this._spCardFace.name);
this.addVertsTexCoord(this._faceVertsBuffer, this._faceTexsBuffer);
//停止面剔除
gl.disable(gl.CULL_FACE);
}.bind(this);

我们先准备好变量,并且在TOUCH_MOVE中实时计算变量值,glNode draw方法中使用前刷新变量值。
除此之外Shader中也需要一些小调整,只需要给传递的变量改成uniform修饰即可。

uniform修饰符

const float UNI_D = 10;
这个圆弧区域距离的值,50的话,有些边界效果不是很好,我这里改成了10.

差点忘了,释放资源

1
2
3
4
5
6
7
8
9
10
11
//结束需要移除
onDestroy: function () {
console.log("onDestroy");
gl._deleteBuffer(this._backVertsBuffer)
gl._deleteBuffer(this._backTexsBuffer)
gl._deleteBuffer(this._faceVertsBuffer)
gl._deleteBuffer(this._faceTexsBuffer)
this.glProgram.release();
this._spCardFace.release();
this._spCardBack.release();
},

大功告成!!
上一段视频效果:

总结

关键还是解决问题的思路,以及对顶点Shader和片元Shader的理解。这不是唯一的解决方式,很多小细节可以调整。这里只做了左下角,但是另外的三个角与复杂的触摸控制也都可以借此实现。

是不是突然感觉代数、几何没白学~~~

完整代码下载:
点击下载

为方便新手,我把完整项目放到GitHub了:

https://github.com/ICrazyMouse/Cocos-Shader-Poker