在上篇中主要作了三件事javascript
在本篇中咱们将运用推导的变换矩阵,一一验证代码中更新节点变换矩阵的代码背后的逻辑。游戏场景中的节点都成树形的父子关系。当前节点 worldMatrix是经过父级节点对应的矩阵获取,因此当前场景中只有一个节点时,当前节点的 worldMatrix 应该与localMatrix相同(未测试)。因此这里就经过分析 updateLocalMatrix 来了解。html
cocos creator 中图形变换采用标记脏数据方式告知渲染流须要更新变换矩阵。采用位运算存储当前须要更新的变换信息。updateLocalMatrix 方法代码行数比较长,也为了方便分析,因此会将代码块拆分。如想总体浏览此代码块,请下载v2.1.3版本的cocos creator。对应的代码在安装文件 \resources\engine\cocos2d\core\CCNode.jsjava
从以下代码就能够论证我上边说明的变换过程,判断脏值,有更新无返回。判断是否有图形变换,有更新无返回。这里有必要说明下位运算是如何作到存储多值的。微信
设某选择题存在ABC三个选项,正确选项为AC。咱们设ABC的权值分别是 A=001 B=010 C=100 ,AC的权值 AC=A|B = 101。此时若用户选择 B,程序只须要将 AC & B > 0 就知道是否正确。代码中就恰好利用这一点,在节点变换过程当中,不断改变存在变换 localMatDirty 的值。函数
// author:herbert 公众号:小院不小
// 原文连接 https://www.cnblogs.com/yfrs/p/ccmatrix2.html
_updateLocalMatrix() {
let dirtyFlag = this._localMatDirty;
if (!dirtyFlag) return;
let t = this._matrix;
if (dirtyFlag & (LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)) {
// 旋转 缩放 倾斜
}
t.m12 = this._position.x;
t.m13 = this._position.y;
this._localMatDirty = 0;
this._worldMatDirty = true;
}
复制代码
代码中(LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)
LocalDirtyFlag.RT=LocalDirtyFlag.POSITION | LocalDirtyFlag.SCALE | LocalDirtyFlag.ROTATION 因此代码中if 判断是当前变换中是否存在 位置 、缩放 、旋转、倾斜。虽然判断了位置,并无放到if代码块中设置,也许还有其余什么变换致使位置变换。最后就是还原标记,以及告诉渲染流得更新worldMatrix了。测试
矩阵的乘法是不知足交换律,即 AB <> BA 因此体如今代码中就是判断旋转和倾斜还在设置缩放的缘由。复合变换的顺序会影响最终节点的位置。此部分代码逻辑以下ui
// author:herbert 公众号:小院不小
if (dirtyFlag & (LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)) {
let rotation = -this._eulerAngles.z;
let hasSkew = this._skewX || this._skewY;
let sx = this._scale.x, sy = this._scale.y;
if (rotation || hasSkew) {
// 旋转 倾斜
}
else {
t.m00 = sx;
t.m01 = 0;
t.m04 = 0;
t.m05 = sy;
}
}
复制代码
代码中,没有旋转或倾斜信息,就只剩下缩放。那么当前矩阵就是缩放矩阵,只须要把矩阵对角线上的值依次设置成sx sy。let rotation = -this._eulerAngles.z;
取负值是由于,rotation 顺时针为正,而后欧拉角中,逆时针为正。取z轴旋转角,是由于2d中旋转轴就是z轴。this
从代码中可知,若是存在复合变换。cocos creator 变换顺序为,旋转->缩放->倾斜->位移。这里有两个须要注意地方spa
// author:herbert 公众号:小院不小 wx:464884492
// 原文连接 https://www.cnblogs.com/yfrs/p/ccmatrix2.html
if (rotation || hasSkew) {
let a = 1, b = 0, c = 0, d = 1;
// rotation
if (rotation) {
let rotationRadians = rotation * ONE_DEGREE;
c = Math.sin(rotationRadians);
d = Math.cos(rotationRadians);
a = d;
b = -c;
}
// scale
t.m00 = a *= sx;
t.m01 = b *= sx;
t.m04 = c *= sy;
t.m05 = d *= sy;
// skew
if (hasSkew) {
// 倾斜
}
}
复制代码
代码中将旋转矩阵分块,只提取左上角的四项,得出具体的分块矩阵A为。A此时就应该等于选择矩阵,即 a=cos(b) b=sin(b) c=-sin(b) d=cos(b).从上篇中咱们推导旋转矩阵是逆时针旋转推倒。而后代码中rotation为了符合使用习惯是顺时针的。全部对应的旋转矩阵应该乘以-1;code
因为cos是偶函数,sin是奇函数,将-1带入矩阵获得 a=cos(b) b=-sin(b) c=sin(b) d=cos(b);接下来处理缩放,将缩放矩阵右乘(cocos 中复合变换矩阵,是左乘仍是右乘,没有明确的地方说明。此处是经过代码反推可能有误)变化后的矩阵,以下图所示
根据矩阵乘法规则(行乘列)可知 a=asx b=bsx c=csy d= dsy
倾斜实际上是两个变换,X轴倾斜和Y轴倾斜。在上篇推导中,获得对应变换矩阵。同上边同样也只取左上角的的分块矩阵A.其中
// author:herbert 公众号:小院不小 wx:464884492
if (hasSkew) {
let a = t.m00, b = t.m01, c = t.m04, d = t.m05;
let skx = Math.tan(this._skewX * ONE_DEGREE);
let sky = Math.tan(this._skewY * ONE_DEGREE);
if (skx === Infinity)
skx = 99999999;
if (sky === Infinity)
sky = 99999999;
t.m00 = a + c * sky;
t.m01 = b + d * sky;
t.m04 = c + a * skx;
t.m05 = d + b * skx;
}
复制代码
因为 tan(90)趋近无穷大,当计算值为Infinity skx 和 sky 分别作一个值限定。接下来看代码对应的矩阵变换。首先先从y到x的顺序,将Asky和Askx相乘获得一个复合矩阵。再左乘当前变换矩阵p,为了和前边对照,依然采用a b c d
矩阵乘法知足结合率,先将右边的两个矩阵相乘
因此经过矩阵乘法规则获得新的值 m00=a+c*sky m04=c+a*skx m01=d+b*sky m05=d+b*skx
中篇相对于上篇,中间间隔了一个多月的时间,原创实属不易。这期间一直在恶补图形学和矩阵相关知识。最初分析版本2.0.10,当时代码中存在rotationX rotationY 旋转,当rotationX 和rotationY 不相等时,一直卡在那段代码的分析过程当中。后来还去官方论坛提问 forum.cocos.com/t/topic/846… ,没有获得满意的结果,也没多少人回复。后来各类查资料,才发现官方将那段代码移除了,采用欧拉角的方式。全部我将本地版本升级成v2.1.3版本。到这个版本后,又卡在了 let rotation = -this._eulerAngles.z这个负号问题。越分析越,感受欠缺的越多,欧拉角,四元数。同时感受可能写不出中篇了,不过我仍是找了一个感受是对的推导将中篇完成了。固然其中我以为不清楚的地方还有
欢迎感兴趣的朋友关注个人微信订阅号"小院不小",或者点击下方的二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到个人订阅号中。须要本文demo能够在公众号中回复matrix
维护了一个Coscos Creator 的游戏开发群,欢迎喜欢聊技术的朋友加入
闲来无事,采用cocos creator开发了一个小游戏【坦克侠】,感兴趣的朋友一个能够来玩玩