import { _decorator, Component, Node, Vec3, tween, Tween } from 'cc'; const { ccclass, property } = _decorator; /** * GameStartMove * * This component is expected to be attached to the main camera node. * It provides high-level animation methods for block selection mode transitions: * 1. enterBlockSelectionMode() – complete animation for entering block selection (camera down + UI slide up) * 2. exitBlockSelectionMode() – complete animation for exiting block selection (camera up + UI slide down) * * Also provides low-level methods for specific use cases: * - moveDownInstant() – instantly move the camera down by a fixed offset * - moveUpSmooth() – move the camera back up with a smooth tween animation * - slideUpFromBottom() – slide UI up from bottom * - slideDibanDownAndHide() – slide UI down and hide */ @ccclass('GameStartMove') export class GameStartMove extends Component { /** The camera node to move. Defaults to the node the script is attached to. */ @property({ type: Node, tooltip: 'Camera node to move. Leave empty to use the current node.' }) public cameraNode: Node = null; /** Offset (in pixels/units) for one step of movement. */ @property({ tooltip: 'Vertical offset for the camera movement.' }) public offset: number = 182; /** Tween duration for the smooth move-up animation. */ @property({ tooltip: 'Duration for the smooth move-up tween (seconds).' }) public moveDuration: number = 0.4; private _originalPos: Vec3 = new Vec3(); /** 需要下滑的 diban 节点(外部拖拽赋值) */ @property({ type: Node, tooltip: 'diban 节点' }) public dibanNode: Node = null; /** Gray遮罩节点 */ @property({ type: Node, tooltip: 'Canvas/GameLevelUI/Gray节点' }) public grayNode: Node = null; private _originalDibanPos: Vec3 = new Vec3(); onLoad () { // Use self node if cameraNode not assigned via the editor. if (!this.cameraNode) { this.cameraNode = this.node; } // Save initial position so we can always restore relative to it. this._originalPos.set(this.cameraNode.position); // Save diban original position if dibanNode is assigned if (this.dibanNode) { this._originalDibanPos.set(this.dibanNode.position); } } /** * Instantly move the camera down by `offset` units. */ public moveDownInstant () { if (!this.cameraNode) return; const pos = this.cameraNode.position.clone(); pos.y -= this.offset; this.cameraNode.setPosition(pos); console.log('GameStartMove.moveDownInstant 镜头下移,当前位置:', pos); // 镜头下移时显示Gray遮罩 if (this.grayNode) { this.grayNode.active = true; } } /** * Smoothly move the camera back up by `offset` units using a tween. */ public moveUpSmooth () { if (!this.cameraNode) return; const startPos = this.cameraNode.position.clone(); const targetPos = new Vec3(startPos.x, startPos.y + this.offset, startPos.z); // Stop any running tweens on this node to avoid conflicting animations. console.log('GameStartMove.moveUpSmooth 镜头上移,开始执行'); Tween.stopAllByTarget(this.cameraNode); tween(this.cameraNode) .to(this.moveDuration, { position: targetPos }, { easing: 'quadOut' }) .start(); } /* ========= 高级动画方法 ========= */ /** * 上滑 diban节点,进入备战状态。同时镜头下移。 * @param distance 向上移动距离,默认 300 * @param duration 动画时长,默认 0.3s */ public slideUpFromBottom(distance: number = 300, duration: number = 0.3) { console.log('GameStartMove.slideUpFromBottom 开始执行'); // 使用装饰器属性中的diban节点 if (!this.dibanNode) { console.warn('GameStartMove.slideUpFromBottom diban节点未设置'); return; } // 保存diban的原始位置(如果还没保存的话) if (this._originalDibanPos.equals(Vec3.ZERO)) { this._originalDibanPos.set(this.dibanNode.position); } // 设置diban初始位置(在屏幕下方) const startPos = this._originalDibanPos.clone(); startPos.y -= distance; this.dibanNode.setPosition(startPos); console.log('GameStartMove.slideUpFromBottom diban初始位置:', startPos); console.log('GameStartMove.slideUpFromBottom diban目标位置:', this._originalDibanPos); // 停止任何正在运行的动画 Tween.stopAllByTarget(this.dibanNode); // 执行上滑动画 tween(this.dibanNode) .to(duration, { position: this._originalDibanPos }, { easing: 'quadOut' }) .call(() => { console.log('GameStartMove.slideUpFromBottom diban上滑动画完成'); }) .start(); // 同时执行camera下移动画 this.moveDownInstant(); console.log('GameStartMove.slideUpFromBottom diban上滑动画已启动'); } /** * 下滑 diban节点,结束备战进入playing 状态。 * @param distance 向下移动距离,默认 300 * @param duration 动画时长,默认 0.3s */ public slideDibanDownAndHide(distance: number = 300, duration: number = 0.3) { console.log('GameStartMove.slideDibanDownAndHide 开始执行'); // 使用装饰器属性中的diban节点 if (!this.dibanNode) { console.warn('GameStartMove.slideDibanDownAndHide diban节点未设置'); return; } // 获取当前位置 const currentPos = this.dibanNode.position.clone(); console.log('GameStartMove.slideDibanDownAndHide diban当前位置:', currentPos); // 计算目标位置(向下移动) const targetPos = new Vec3(currentPos.x, currentPos.y - distance, currentPos.z); console.log('GameStartMove.slideDibanDownAndHide diban目标位置:', targetPos); // 停止任何正在运行的动画 Tween.stopAllByTarget(this.dibanNode); // 执行下滑动画 tween(this.dibanNode) .to(duration, { position: targetPos }, { easing: 'quadOut' }) .call(() => { console.log('GameStartMove.slideDibanDownAndHide diban下滑动画完成'); // 动画完成时隐藏Gray遮罩 if (this.grayNode) { this.grayNode.active = false; } }) .start(); // 同时执行camera上移动画 this.moveUpSmooth(); console.log('GameStartMove.slideDibanDownAndHide diban下滑动画已启动'); } }