import { Component, EventTouch, Node, Prefab, UIOpacity, UITransform, Vec3, Widget, _decorator, easing, instantiate, tween, v3 } from 'cc'; const { ccclass, requireComponent } = _decorator; @ccclass('RewardFly') @requireComponent(UITransform) export class RewardFly extends Component { protected onLoad() { //this.node.on(Node.EventType.TOUCH_START, function (event: EventTouch) { //event.propagationStopped = true; //event.preventSwallow = true; //}, this, true); } protected onDisable() { this.node.destroyAllChildren(); } private convertToNodeSpaceAR(target: Node | Vec3) { if (target instanceof Node) { return this.node.getComponent(UITransform).convertToNodeSpaceAR(target.getComponent(UITransform).convertToWorldSpaceAR(Vec3.ZERO)); } return this.node.getComponent(UITransform).convertToNodeSpaceAR(target); } public add(opts: { /**物品 */ item: Prefab | Node, /**物品数量 */ count: number, /**起始位置(节点或世界坐标) */ start: Node | Vec3, /**终点位置(节点或世界坐标) */ end: Node | Vec3, /**起点炸开的范围[x, y], 默认[100, 100] */ startRange?: [number, number], /**终点偏移的范围[x, y], 默认[0, 0] */ endOffset?: [number, number], /**开始接触 */ onBegin?: (nodeCopy: Node | null) => any, /**持续接触(包括开始) */ onContact?: (nodeCopy: Node | null) => any, /**动画结束 */ onFinish?: (nodeCopy: Node | null) => any /**拷贝highlightArea或end至当前节点下, 优先highlightArea */ highlight?: boolean, /**高亮区域,依赖highlight开启 */ highlightArea?: Node, /**高亮位置偏移[x, y],依赖highlight开启, 默认[0, 0] */ highlightOffset?: [number, number], }) { const startPos = this.convertToNodeSpaceAR(opts.start); const endPos = this.convertToNodeSpaceAR(opts.end); if (opts.endOffset) { endPos.add3f(opts.endOffset[0], opts.endOffset[1], 0); } let nodeCopy: Node = null; if (opts.highlight) { if (opts.highlightOffset) { endPos.add3f(opts.highlightOffset[0], opts.highlightOffset[1], 0); } if (opts.highlightArea) { const endAreaPos = this.convertToNodeSpaceAR(opts.highlightArea); if (opts.highlightOffset) { endAreaPos.add3f(opts.highlightOffset[0], opts.highlightOffset[1], 0); } nodeCopy = instantiate(opts.highlightArea); nodeCopy.getComponent(Widget)?.destroy(); nodeCopy.setParent(this.node); nodeCopy.setPosition(endAreaPos); } else if (Node.isNode(opts.end)) { nodeCopy = instantiate(opts.end); nodeCopy.getComponent(Widget)?.destroy(); nodeCopy.setParent(this.node); nodeCopy.setPosition(endPos); } } for (let index = 0; index < opts.count; index++) { const aniNode = instantiate(opts.item) as Node; aniNode.setPosition(startPos); aniNode.setParent(this.node); // 炸开位置 const midPos = v3(startPos.x, startPos.y, 0); midPos.x += (Math.random() * 2 - 1) * (opts.startRange ? opts.startRange[0] : 100); midPos.y += (Math.random() * 2 - 1) * (opts.startRange ? opts.startRange[1] : 100); // 执行动画 (aniNode.getComponent(UIOpacity) || aniNode.addComponent(UIOpacity)).opacity = 0; tween(aniNode) .delay(index * 0.025) .call(() => { aniNode.getComponent(UIOpacity).opacity = 200; tween(aniNode.getComponent(UIOpacity)) .to(0.4, { opacity: 255 }, { easing: easing.expoOut }) .start(); }) .to(0.4, { position: midPos }, { easing: easing.expoOut }) .call(() => { tween(aniNode.getComponent(UIOpacity)) .delay(0.5) .to(0.1, { opacity: 70 }) .start(); }) .to(0.6, { position: endPos }, { easing: easing.expoIn }) .call(() => { if (index === 0) { // 结束节点缩放 if (nodeCopy) { const scale = v3(nodeCopy.scale); tween(nodeCopy) .to(0.1, { scale: v3(scale.x * 1.3, scale.y * 1.3) }) .delay(0.025 * Math.max(opts.count, 4)) .to(0.1, { scale: scale }) .call(() => { // 结束节点透明销毁 const uiOpacity = nodeCopy.getComponent(UIOpacity) || nodeCopy.addComponent(UIOpacity); tween(uiOpacity) .to(0.1, { opacity: uiOpacity.opacity * 0.3 }) .call(() => nodeCopy.destroy()) .start(); }) .start(); } opts.onBegin && opts.onBegin(nodeCopy); } opts.onContact && opts.onContact(nodeCopy); }) .delay(0.01) .call(() => { aniNode.destroy(); if (index === opts.count - 1) { opts.onFinish && opts.onFinish(nodeCopy); } }).start(); } } }