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

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開發了一個小遊戲【坦克俠】,感興趣的朋友一個可以來玩玩

小遊戲坦克俠

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章