在上篇中主要作了三件事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開發了一個小遊戲【坦克俠】,感興趣的朋友一個能夠來玩玩