3.x怎么实现类似2.x的skewX和skewY

项目中需要用skew做特效,但是3.x中移除了这个属性,想知道有没有大佬实现过分享下

1赞

类似这样

let skewX = 0, skewY = 0;
const wm = this.node.worldMatrix;
const tm = Mat4.toArray([], wm);
let a = tm[0], b = tm[1], c = tm[4], d = tm[5];
const ONE_DEGREE = Math.PI / 180;
let skx = Math.tan(skewX * ONE_DEGREE);
let sky = Math.tan(skewY * ONE_DEGREE);
if (skx === Infinity)
    skx = 99999999;
if (sky === Infinity)
    sky = 99999999;
tm[0] = a + c * sky;
tm[1] = b + d * sky;
tm[4] = c + a * skx;
tm[5] = d + b * skx;
Mat4.fromArray(wm, tm);
3赞

稍等,我试下

这个只能让节点自身3D翻转,我是希望节点及其所有子节点一起翻转。
所以需要skew配合scale来实现翻转效果

成功了,这个方法非常赞。

手工构建 skew矩阵,666.

有个瑕疵,这个只有在项目启动的时候生效。
如果在游戏中动态修改skewX和skewY就没有作用。
初步判断是节点初始化的时候会更新一次矩阵,但游戏中更新skewX和skewY没有通知底层节点矩阵更新了。
目前在解决这个问题。

如果要正式使用的话应该是类似这样

let skewX = 0, skewY = 0;

// 需要重置矩阵,否则重复 skew 时会在上次 skew 的基础上 skew
this.node._transformFlags |= TransformBit.SCALE;

// ...

// 标记当前帧的变动,通知 render component 更新位置
this.node.hasChangedFlags |= TransformBit.SCALE;

好,一会试试

自身能变换了,但是子节点不会跟着变换

尝试改完矩阵后,用 node.invalidateChildren(TransformBit.SCALE) 设置看看。

感谢回复,我刚试了下,结果是这样的
方案A:


自身和子节点都无效
方案B:

自身有效,子节点无效

这个是demo,目前是实现了自身变换,但子节点不会跟着一起变换。
assets.rar (380.7 KB)

1赞

收到,我排查下。

assets.rar (380.9 KB)
最新进展:
已实现skew,但是如果scale和skew一起改变,skew将失效
估计是两者都通过TransformBit.SCALE来标记,冲突了

@ccclass('HelloWorld')
export class HelloWorld extends Component {
    
    start() {
        let rootNode = this.node.getChildByName('RootNode');
        const PI180 = Math.PI/180;
        let skewX = 0,skewY = 0;
        this.schedule(()=>{
            skewY++;
            let skX = Math.tan(skewX*PI180);
            let skY = Math.tan(skewY*PI180);
            (rootNode as any)._transformFlags |= TransformBit.SCALE;
            rootNode.hasChangedFlags |= TransformBit.SCALE;
            rootNode.updateWorldTransform();
            let m = rootNode['_mat'],a = m.m00,b = m.m01,c = m.m04,d = m.m05;
            m.m00 = a+c*skY;
            m.m01 = b+d*skY;
            m.m04 = c+a*skX;
            m.m05 = d+b*skX;
            
            for (const child of rootNode.children) { // 一起变要加上更新子节点
                child.invalidateChildren(TransformBit.SCALE | TransformBit.ROTATION);
            }
        },0.1);
    }
}
1赞

因为 setScale 内部会设置local scale,在下一帧会重新更新 world matrix 导致 你hack 的 matrix 失效。

import { _decorator, Component, Node, TransformBit } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('HelloWorld')
export class HelloWorld extends Component {

    start() {
        let rootNode = this.node.getChildByName('RootNode');
        const PI180 = Math.PI / 180;
        let skewX = 0, skewY = 0;
        let scaleX = 1;
        this.schedule(() => {
            skewY++;
            let skX = Math.tan(skewX*PI180);
            let skY = Math.tan(skewY*PI180);
            //如果下面两句注释放开,skew的变更会失效,因为TransformBit.SCALE冲突了
            scaleX-=0.02;
            rootNode.setScale(scaleX,scaleX);
            
            (rootNode as any)._transformFlags |= TransformBit.SCALE;
            rootNode.hasChangedFlags |= TransformBit.SCALE;
            rootNode.updateWorldTransform();
            let m = rootNode['_mat'],a = m.m00,b = m.m01,c = m.m04,d = m.m05;
            m.m00 = a+c*skY;
            m.m01 = b+d*skY;
            m.m04 = c+a*skX;
            m.m05 = d+b*skX;
            
            for (const child of rootNode.children) { // 一起变要加上更新子节点
                child.invalidateChildren(TransformBit.SCALE | TransformBit.ROTATION);
            }
            
        }, 0.1);
    }
}

这样就可以了

1赞

还是存在我刚才说的这个问题:
只有skew单独改变有效
skew和scale一起变,skew会失效