ActionShadowDemo/assets/Script/ActionShadow.ts
2023-05-14 17:15:55 +08:00

220 lines
8.0 KiB
TypeScript

/*******************************************************************************
* 创建: 2022年11月27日
* 作者: 水煮肉片饭(27185709@qq.com)
* 描述: 动作残影
* 给节点添加残影,残影会跟随节点移动,并跟随节点播放动画。
* 使用举例:动作类游戏中,主角释放连击必杀
*******************************************************************************/
const { ccclass, property, executeInEditMode, playOnFocus, menu } = cc._decorator;
class ShadowData {
x: number = 0;
y: number = 0;
angle: number = 0;
scaleX: number = 1;
scaleY: number = 1;
scale: number = 1; //向尾部递减的缩放系数
actionName: string = '';
frameTime: number = 0;
}
@ccclass
@executeInEditMode
@playOnFocus
@menu('Comp/ActionShadow')
export default class ActionShadow extends cc.Component {
@property
private _active: boolean = true;
@property({ displayName: CC_DEV && '是否激活', tooltip: CC_DEV && '设置残影可见性\n激活会重置残影位置' })
get active() { return this._active; }
set active(value: boolean) {
this._active = value;
if (value) {
if (this.node.active && this.shadowNode) {
this.shadowNode.active = true;
this.shadowData.length && this.updateShadowData();
}
} else {
this.shadowNode.active = false;
}
}
@property()
private _shadowNum: number = 10;
@property({ type: cc.Integer, displayName: CC_DEV && '残影数量' })
private get shadowNum(): number { return this._shadowNum; }
private set shadowNum(value: number) {
this._shadowNum = Math.max(value, 0);
this.updateShadowNum();
this.updateDeltTime();
this.updateShadowData();
this.updateColor();
}
@property()
private _deltTime: number = 4;
@property({ type: cc.Integer, displayName: CC_DEV && '延迟帧数' })
private get deltTime(): number { return this._deltTime; }
private set deltTime(value: number) {
this._deltTime = Math.max(value, 1);
this.updateDeltTime();
this.updateShadowData();
}
@property()
private _shadowScale: number = 0.1;
@property({ min: 0, max: 1, step: 0.1, slide: true, displayName: CC_DEV && '尾部缩放系数' })
private get shadowScale(): number { return this._shadowScale; }
private set shadowScale(value: number) {
this._shadowScale = value;
this.updateShadowData();
}
@property()
private _shadowColor: cc.Color = cc.color(255, 255, 255);
@property({ displayName: CC_DEV && '残影颜色' })
private get shadowColor(): cc.Color { return this._shadowColor; }
private set shadowColor(value: cc.Color) {
this._shadowColor = value;
this.updateColor();
}
@property()
private _opacity: number = 50;
@property({ type: cc.Integer, min: 0, max: 255, slide: true, displayName: CC_DEV && '透明度' })
private get opacity(): number { return this._opacity; }
private set opacity(value: number) {
this._opacity = value;
this.updateOpacity();
}
private nodeOpacity: number = 255;
private model: cc.Animation = null;
private shadowNode: cc.Node = null;
private shadowData: ShadowData[] = [];
protected start() {
let shadowNodeName = 'ActionShadow_' + this.node.name;
this.shadowNode = this.node.parent.getChildByName(shadowNodeName)
if (!this.shadowNode) {
this.shadowNode = new cc.Node(shadowNodeName);
this.shadowNode.setParent(this.node.parent);
this.shadowNode.setSiblingIndex(this.node.getSiblingIndex());
this.shadowNode['_objFlags'] |= cc.Object['Flags'].HideInHierarchy;
this.shadowNode['_objFlags'] |= cc.Object['Flags'].LockedInEditor;
}
this.nodeOpacity = this.node.opacity;
this.model = this.node.getComponent(cc.Animation);
this.model && (this.model.currentClip = this.model.defaultClip);
this.updateShadowNum();
this.updateDeltTime();
this.updateShadowData();
this.updateColor();
this.updateOpacity();
}
protected onEnable() {
if (this.active && this.shadowNode) {
this.shadowNode.active = true;
this.shadowData.length && this.updateShadowData();
}
}
protected onDisable() {
this.shadowNode.active = false;
}
protected update() {
if (this.nodeOpacity !== this.node.opacity) {
this.nodeOpacity = this.node.opacity;
this.updateOpacity();
}
for (let i = this.shadowNum * this.deltTime; i > 0; --i) {
let cur = this.shadowData[i];
let prev = this.shadowData[i - 1];
cur.x = prev.x;
cur.y = prev.y;
cur.scaleX = prev.scaleX;
cur.scaleY = prev.scaleY;
cur.angle = prev.angle;
cur.actionName = prev.actionName;
cur.frameTime = prev.frameTime;
}
let data = this.shadowData[0];
data.x = this.node.x;
data.y = this.node.y;
data.scaleX = this.node.scaleX;
data.scaleY = this.node.scaleY;
data.angle = this.node.angle;
if (this.model !== null) {
data.actionName = this.model.currentClip.name;
data.frameTime = this.model.getAnimationState(data.actionName).time;
}
for (let i = this.shadowNum - 1; i >= 0; --i) {
let node = this.shadowNode.children[i];
data = this.shadowData[this.deltTime * (i + 1)];
node.x = data.x;
node.y = data.y;
node.scaleX = data.scaleX * data.scale;
node.scaleY = data.scaleY * data.scale;
node.angle = data.angle;
let model = node.getComponent(cc.Animation);
model !== null && model.play(data.actionName, data.frameTime);
}
}
private updateShadowNum() {
this.shadowNode.removeAllChildren();
this.shadowNode.destroyAllChildren();
for (let i = 0, len = this.shadowNum; i < len; ++i) {
let node = cc.instantiate(this.node);
node.name = 'Shadow' + i;
let cmps = node['_components'];
for (let j = cmps.length - 1; j >= 0; --j) {
if (cmps[j] instanceof cc.RenderComponent) continue;
if (cmps[j] instanceof cc.Animation) continue;
cmps[j].destroy();
}
node.setParent(this.shadowNode);
}
}
private updateDeltTime() {
this.shadowData = [];
for (let i = this.shadowNum * this.deltTime; i >= 0; --i) {
this.shadowData[i] = new ShadowData();
}
}
private updateShadowData() {
let scaleDelt = (1 - this.shadowScale) / (this.shadowNum * this.deltTime);
for (let i = this.shadowNum * this.deltTime; i >= 0; --i) {
let data = this.shadowData[i];
data.x = this.node.x;
data.y = this.node.y;
data.scaleX = this.node.scaleX;
data.scaleY = this.node.scaleY;
data.scale = 1 - i * scaleDelt;
data.angle = this.node.angle;
if (this.model !== null) {
data.actionName = this.model.currentClip.name;
data.frameTime = this.model.getAnimationState(data.actionName).time;
this.model.play(data.actionName, data.frameTime);
}
}
}
private setColor(node: cc.Node, color: cc.Color) {
node.color = color;
for (let i = node.childrenCount - 1; i >= 0; --i) {
this.setColor(node.children[i], color);
}
}
private updateColor() {
this.setColor(this.shadowNode, this.shadowColor);
}
private updateOpacity() {
this.shadowNode.opacity = this.opacity * this.node.opacity / 255;
}
protected onDestroy() {
if (cc.isValid(this.shadowNode)) {
this.shadowNode.removeFromParent();
this.shadowNode.destroy();
};
}
}