Cocos Creator 中 _worldMatrix 到底是什么(中)

1. 中篇摘要

上篇中主要做了三件事

  • 简单表述了矩阵的基本知识,以及需要涉及到的三角函数知识
  • 推导了图形变换中 位移 、旋转、缩放 对应的变换矩阵。
  • cocos creator 中矩阵存储方式

在本篇中我们将运用推导的变换矩阵,一一验证代码中更新节点变换矩阵的代码背后的逻辑。游戏场景中的节点都成树形的父子关系。当前节点 worldMatrix是通过父级节点对应的矩阵获取,所以当前场景中只有一个节点时,当前节点的 worldMatrix 应该与localMatrix相同(未测试)。所以这里就通过分析 updateLocalMatrix 来了解。

cocos creator 中图形变换采用标记脏数据方式告知渲染流需要更新变换矩阵。采用位运算存储当前需要更新的变换信息。updateLocalMatrix 方法代码行数比较长,也为了方便分析,所以会将代码块拆分。如想整体浏览此代码块,请下载v2.1.3版本的cocos creator。对应的代码在安装文件 \resources\engine\cocos2d\core\CCNode.js

2. updateLocalMatrix函数整体逻辑

从如下代码就可以论证我上边说明的变换过程,判断脏值,有更新无返回。判断是否有图形变换,有更新无返回。这里有必要说明下位运算是如何做到存储多值的。

设某选择题存在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了。

3. 旋转 缩放 倾斜代码逻辑

矩阵的乘法是不满足交换律,即 AB <> BA 所以体现在代码中就是判断旋转和倾斜还在设置缩放的原因。复合变换的顺序会影响最终节点的位置。此部分代码逻辑如下

// 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轴。

4. 存在旋转倾斜代码逻辑

从代码中可知,如果存在复合变换。cocos creator 变换顺序为,旋转->缩放->倾斜->位移。这里有两个需要注意地方

  • 传入的旋转角度需要转换成弧度。不经常用math.cos math.sin 可能不知道这些函数的参数是弧度值
  • Math.sin(Math.PI/6)不等于0.5是因为浮点数的原因
// 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;

由于cos是偶函数,sin是奇函数,将-1带入矩阵得到

a=cos(b) b=-sin(b) c=sin(b) d=cos(b);接下来处理缩放,将缩放矩阵右乘(cocos 中复合变换矩阵,是左乘还是右乘,没有明确的地方说明。此处是通过代码反推可能有误)变化后的矩阵,如下图所示

根据矩阵乘法规则(行乘列)可知

a=asx b=bsx c=csy d= dsy

5. 倾斜代码逻辑

倾斜其实是两个变换,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

6. 总结

中篇相对于上篇,中间间隔了一个多月的时间,原创实属不易。这期间一直在恶补图形学和矩阵相关知识。最初分析版本2.0.10,当时代码中存在rotationX rotationY 旋转,当rotationX 和rotationY 不相等时,一直卡在那段代码的分析过程中。后来还去官方论坛提问 https://forum.cocos.com/t/topic/84680/4 ,没有得到满意的结果,也没多少人回复。后来各种查资料,才发现官方将那段代码移除了,采用欧拉角的方式。所有我将本地版本升级成v2.1.3版本。到这个版本后,又卡在了 let rotation = -this._eulerAngles.z这个号问题。越分析越,感觉欠缺的越多,欧拉角,四元数。同时感觉可能写不出中篇了,不过我还是找了一个感觉是对的推导将中篇完成了。当然其中我觉得不清楚的地方还有

  • 在2.0.10 版本中Y轴旋转和X轴选中问题,为啥可以根据Z轴旋转的结果一分为二
  • 在2.1.3 版本中旋转取欧拉角z负号问题,旋转矩阵 b c 值相互取反问题
  • 复合矩阵变换左乘右乘问题,在本文中我通过代码推导应该是左乘
  • 倾斜变换Asky和Askx两个居中相乘顺序问题,本文通过y右乘x得到代码结果

欢迎感兴趣的朋友关注我的微信订阅号"小院不小",或者点击下方的二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中。需要本文demo可以在公众号中回复matrix

维护了一个Coscos Creator 的游戏开发群,欢迎喜欢聊技术的朋友加入

闲来无事,采用cocos creator开发了一个小游戏【坦克侠】,感兴趣的朋友一个可以来玩玩

Cocos Creator 中 _worldMatrix 到底是什么(中)的更多相关文章

  1. Cocos Creator 中 _worldMatrix 到底是什么(上)

    Cocos Creator 中 _worldMatrix 到底是什么(上) 1. (矩阵)Matrix是什么,有什么用 (矩阵)Matrix一个神奇的存在?在开发过程中对里边各项值的含义是不是抓耳挠腮 ...

  2. CocosCreator中_worldMatrix到底是什么(下)

    Cocos Creator 中 _worldMatrix 到底是什么(下) 1. 摘要 上篇介绍了矩阵的基本知识以及对应图形变换矩阵推倒.中篇具体介介绍了对应矩阵转换成cocos creator代码的 ...

  3. Cocos Creator 通用框架设计 —— 资源管理

    如果你想使用Cocos Creator制作一些规模稍大的游戏,那么资源管理是必须解决的问题,随着游戏的进行,你可能会发现游戏的内存占用只升不降,哪怕你当前只用到了极少的资源,并且有使用cc.loade ...

  4. kbengine_js_plugins 在Cocos Creator中适配

    kbengine_js_plugins 改动(2017/7/6) 由于Cocos Creator使用严格模式的js,而原本的kbengine_js_plugins是非严格模式的,因此为了兼容和方 便C ...

  5. 在 Cocos Creator 中使用 Protobufjs(一)

    一. 环境准备 我一直在探索Cocos H5正确的开发姿势,目前做javascript项目已经离不开 nodejs.npm或grunt等脚手架工具了. 1.初始化package.json文件 npm ...

  6. Cocos Creator 中的动作系统那些事儿

    动作系统就是可以在一定的时间内实现位移.旋转.缩放.跳动等各种动作. 需要注意的是,动作系统跟 Cocos Creator 编译器的动画系统不同,动作系统是面向程序员的API接口,而动画系统是通过编译 ...

  7. Cocos Creator中按钮组件数组的使用

    Cocos Creator游戏开发中经常使用到按钮,特别是大量按钮的情况,此时使用数组来管理这些按钮就显得更具通用性.我大致走了一下官方的示例,好像没有发现有这个小内容(或者有,但我却是没有找到),于 ...

  8. 欢乐水杯(happy glass)中流体的一种实现!图文视频讲解 ! Cocos Creator!

    使用cocos creator v2.2.2 实现流体效果 ! 图文+视频讲解! 效果预览 实现原理 整体思路是参考论坛中的一个帖子 这款游戏中水的粘连效果在Construct3中利用图层很容易实现, ...

  9. JS中new到底发生了什么

    outline prototype 与 __proto__ function 与 object new 到底发生了什么 prototype 与 __proto__ 首先说下在JS中比较容易让人困惑的  ...

随机推荐

  1. MyBatis 3.5.2 新特性介绍

    1.MyBatis 最新版本 3.5.2 发布 MyBatis最新版本是:3.5.2,发布时间是:2019年7月15日 2.MyBatis 3.5.2 新特征介绍 我们知道,MyBatis 是支持定制 ...

  2. KeyValue对RDDs

    创建KeyValue对RDDs 使用函数map,返回keyvalue对 例如,包含数行数据的RDD,那每行的第一个单词作为keys. sparl.txt中的内容如下: hello ! hello wo ...

  3. HTML连载39-外边距合并现象、盒子模型以及宽度和高度

    一. 在默认布局的垂直方向上,默认情况下外边距是是不会叠加的,会出现合并现象,谁的外边距较大,就听谁的:但是在水平方向就不会出现这种状况,我们举个例子 span{ display: inline-bl ...

  4. 单元测试框架Uinttest一文详解

    一谈及unittest,大家都知道,unittest是Python中自带的单元测试框架,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作.unittest单元测试框架不仅可以适用于单元 ...

  5. JavaScript DOM 编程艺术

    最近把JavaScript DOM 编程艺术这本书看完了,觉得这本书很好 深入浅出地展示了渐进增强.平稳退化.结构和样式分离等编程思想,我对书中重要的知识进行了梳理总结. 一.网页 二.JavaScr ...

  6. ZIP压缩和解压字符串

    由于ZIP压缩会产生头信息, 所以当字符串长度没有达到一定规模的时候, 压缩后的长度可能比原来的还长 // 将一个字符串按照zip方式压缩和解压缩 public class ZipUtil { // ...

  7. Google Test入门教程:从下载到运行

    本文以VS2019为例,自己的工程使用Debug x64,多线程调试DLL(/MDd),用户可以根据自己需求更改配置,只要所有配置前后统一即可. 第一步:clone Google Test源码 打开h ...

  8. redis-自动补全

    自动补全实现方式有两种: 第一种:数据量非常小时,程序从redis中获取数据后,在程序中排序:redis只作为数据存储用: 第二种:数据量较大时,直接在redis中排序,并返回自动补全的数据. 第三种 ...

  9. yii中缓存(cache)详解 - 彼岸あ年華ツ

    缓存是用于提升网站性能的一种即简单又有效的途径.通过存储相对静态的数据至缓存以备所需,我们可以省去生成 这些数据的时间.在 Yii 中使用缓存主要包括配置和访问缓存组件 . 内部方法 一.缓存配置: ...

  10. php 正则判断是否是手机号码 最新

    php 正则判断是否是手机号码 最新 标签: php正则 2013-09-22 14:31 55076人阅读 评论(1) 收藏 举报  分类: php(42)  版权声明:本文为博主原创文章,若转载请 ...