看我上一条回复。把 rootNode.setScale 放在 updateWorldTransform 之前。就 ok 了。
但我是想把skewX和skewY hack到Node里啊,可以直接在一个Tween里设置skew和scale来实现动画
这是另外一个话题了吧?
我提供的 skewX,skewY 与 Scale 一起变化的解决方案还有问题吗?
目前这个 hack 改动,依赖顺序,skew hack 世界矩阵,必须在世界矩阵更新之后。
你可能可以通过 修改 updateWorldTransform 的原型来保证你的 hack 代码在最后执行。
问题是不能通用,我封装好setSkew后,如果先调用setSkew后调用setScale呢
这个我试试
下个版本能加入skew么,很多效果需要用到的,比如skewY和scaleX一起变可以实现整个页面翻转的动画
这个后续看看吧,看是否如此迫切。需要这个功能的可以这里回复。
import { _decorator, Component, Node, TransformBit } from 'cc';
const { ccclass, property } = _decorator;
const oldUpdateWorldTransform = Node.prototype.updateWorldTransform;
(Node.prototype as any)._skewX = 0.0;
(Node.prototype as any)._skewY = 0.0;
Node.prototype.updateWorldTransform = function() {
if (this._skewX || this._skewY) {
this.invalidateChildren(TransformBit.SCALE);
oldUpdateWorldTransform.call(this);
let m = this['_mat'],a = m.m00,b = m.m01,c = m.m04,d = m.m05;
m.m00 = a+c*this._skewY;
m.m01 = b+d*this._skewY;
m.m04 = c+a*this._skewX;
m.m05 = d+b*this._skewX;
for (const child of this.children) { // 一起变要加上更新子节点
child.invalidateChildren(TransformBit.SCALE | TransformBit.ROTATION);
}
} else {
oldUpdateWorldTransform.call(this);
}
}
Object.defineProperty(Node.prototype, 'skewX', {
get() {
return this._skewX;
},
set(v: number) {
this._skewX = v;
this.invalidateChildren(TransformBit.SCALE);
},
configurable: true,
enumerable: true,
});
Object.defineProperty(Node.prototype, 'skewY', {
get() {
return this._skewY;
},
set(v: number) {
this._skewY = v;
this.invalidateChildren(TransformBit.SCALE);
},
configurable: true,
enumerable: true,
});
@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 as any).skewX = skX;
(rootNode as any).skewY = skY;
rootNode.setScale(scaleX, scaleX);
}, 0.1);
}
}
这样可行了,不依赖顺序。你可以考虑把 skewX, skewY 封装为 skew 对象,这样跟 position, rotation, scale 比较统一。
import { _decorator, Component, Node, TransformBit, tween, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
const oldUpdateWorldTransform = Node.prototype.updateWorldTransform;
(Node.prototype as any)._skewX = 0.0;
(Node.prototype as any)._skewY = 0.0;
Node.prototype.updateWorldTransform = function() {
if (this._skewX || this._skewY) {
this.invalidateChildren(TransformBit.SCALE);
oldUpdateWorldTransform.call(this);
let m = this['_mat'],a = m.m00,b = m.m01,c = m.m04,d = m.m05;
m.m00 = a+c*this._skewY;
m.m01 = b+d*this._skewY;
m.m04 = c+a*this._skewX;
m.m05 = d+b*this._skewX;
for (const child of this.children) { // 一起变要加上更新子节点
child.invalidateChildren(TransformBit.SCALE | TransformBit.ROTATION);
}
} else {
oldUpdateWorldTransform.call(this);
}
}
Object.defineProperty(Node.prototype, 'skewX', {
get() {
return this._skewX;
},
set(v: number) {
this._skewX = v;
this.invalidateChildren(TransformBit.SCALE);
},
configurable: true,
enumerable: true,
});
Object.defineProperty(Node.prototype, 'skewY', {
get() {
return this._skewY;
},
set(v: number) {
this._skewY = v;
this.invalidateChildren(TransformBit.SCALE);
},
configurable: true,
enumerable: true,
});
@ccclass('HelloWorld')
export class HelloWorld extends Component {
start() {
let rootNode = this.node.getChildByName('RootNode');
const PI180 = Math.PI / 180;
let skew = { x: 0, y: 0};
tween(rootNode)
.parallel(
tween(rootNode).by(2, { scale: new Vec3(-0.7, -0.7, 0) }),
tween(skew).by(2, { y: 30}, {
onUpdate(target, ratio) {
(rootNode as any).skewY = Math.tan(target.y*PI180);
},
} )
).id(1)
.reverse(1)
.union()
.repeatForever()
.start();
}
}
这是使用 tween 的代码
大佬牛批~~~
hack只是补救办法,还是希望新版本能把skew加上,这个本来就是常规功能
我整理了一下代码,你现在可以这样用了:
tween(rootNode)
.by(2, {
scale: new Vec3(-0.7, -0.7, 0),
// @ts-expect-error
skew: new Vec2(0, 30),
})
完整代码:
import { _decorator, Component, Node, TransformBit, tween, Vec3, Vec2 } from 'cc';
const { ccclass, property } = _decorator;
const oldUpdateWorldTransform = Node.prototype.updateWorldTransform;
(Node.prototype as any)._skew = new Vec2(0, 0);
const PI180 = Math.PI / 180;
Node.prototype.updateWorldTransform = function() {
if (this._skew.x || this._skew.y) {
const skewX = Math.tan(this._skew.x * PI180);
const skewY = Math.tan(this._skew.y * PI180);
this.invalidateChildren(TransformBit.SCALE);
oldUpdateWorldTransform.call(this);
let m = this['_mat'],a = m.m00,b = m.m01,c = m.m04,d = m.m05;
m.m00 = a+c*skewY;
m.m01 = b+d*skewY;
m.m04 = c+a*skewX;
m.m05 = d+b*skewX;
for (const child of this.children) { // 一起变要加上更新子节点
child.invalidateChildren(TransformBit.SCALE | TransformBit.ROTATION);
}
} else {
oldUpdateWorldTransform.call(this);
}
}
Object.defineProperty(Node.prototype, 'skew', {
get() {
return this._skew;
},
set(v: Vec2) {
this._skew = v;
this.invalidateChildren(TransformBit.SCALE);
},
configurable: true,
enumerable: true,
});
@ccclass('HelloWorld')
export class HelloWorld extends Component {
start() {
let rootNode = this.node.getChildByName('RootNode');
tween(rootNode)
.by(2, {
scale: new Vec3(-0.7, -0.7, 0),
// @ts-expect-error
skew: new Vec2(0, 30),
}).id(1)
.reverse(1)
.union()
.repeatForever()
.start();
}
}
现在简洁多了,我尝试整合到我的节点扩展里
可惜 native 上无效, 应该是因为都是直接调用的 C++ 方法, 没有再从 js 走一遍了, 没有这样 hack 的余地
对啊,skew属于常规功能,在2D项目中很有用的,希望官方下一版本能加上。
请问一下,移除skew是出于性能考虑吗?2D里面实现skew真的很常规
这个我也不确定早期移除的原因。但应该跟性能关系不大。
有可能是 skew 在 3D 里面比较少用,当时就没考虑在 3.x 中加。
可以请求在3.x的2D里面加上吗?2D真的很需要这个属性,我相信现在用3.x开发2D游戏的还是非常多的
去 github 上建个 issue 吧。