mirror of
https://github.com/szrpf/ActionShadowDemo.git
synced 2025-10-09 16:45:43 +00:00
Signed-off-by: szrpf <27185709@qq.com>
This commit is contained in:
220
assets/Script/ActionShadow.ts
Normal file
220
assets/Script/ActionShadow.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
/*******************************************************************************
|
||||
* 创建: 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();
|
||||
};
|
||||
}
|
||||
}
|
10
assets/Script/ActionShadow.ts.meta
Normal file
10
assets/Script/ActionShadow.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "1a585efd-baca-4d06-a2e7-a6c756643273",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
21
assets/Script/Helloworld.ts
Normal file
21
assets/Script/Helloworld.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const {ccclass, property} = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class Helloworld extends cc.Component {
|
||||
heroNode: cc.Node = null;
|
||||
touchX: number = 0;
|
||||
touchY: number = 0;
|
||||
start () {
|
||||
this.heroNode = this.node.getChildByName('Enemy');
|
||||
this.heroNode.on(cc.Node.EventType.TOUCH_START, (event) => {
|
||||
let pos = event.getLocation();
|
||||
this.touchX = pos.x - this.heroNode.x;
|
||||
this.touchY = pos.y - this.heroNode.y;
|
||||
});
|
||||
this.heroNode.on(cc.Node.EventType.TOUCH_MOVE, (event) => {
|
||||
let pos = event.getLocation();
|
||||
this.heroNode.x = pos.x - this.touchX;
|
||||
this.heroNode.y = pos.y - this.touchY;
|
||||
});
|
||||
}
|
||||
}
|
10
assets/Script/Helloworld.ts.meta
Normal file
10
assets/Script/Helloworld.ts.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "e1b90feb-a217-4493-849d-9a611900d683",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
Reference in New Issue
Block a user