| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- import { _decorator, Component, Node, Vec3, tween, Tween, UITransform, Camera } 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;
- /** Reference line node for camera positioning. When set, camera will move to show this line at the bottom. */
- @property({
- type: Node,
- tooltip: 'Reference line node. Camera will move to position this line at the bottom of the view.'
- })
- public referenceLineNode: Node = null;
- /** 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 to show the reference line at bottom.
- * Requires referenceLineNode to be set.
- */
- public moveDownInstant () {
- if (!this.cameraNode) return;
-
- const pos = this.cameraNode.position.clone();
-
- // Calculate movement based on reference line position
- const moveDistance = this.calculateMoveDistanceToShowLine();
- if (moveDistance > 0) {
- pos.y -= moveDistance;
- this.cameraNode.setPosition(pos);
- console.log('GameStartMove.moveDownInstant 镜头下移,当前位置:', pos);
- }
-
- // 镜头下移时显示Gray遮罩
- if (this.grayNode) {
- this.grayNode.active = true;
- }
- }
- /**
- * Smoothly move the camera back up using a tween to original position.
- * Requires referenceLineNode to be set.
- */
- public moveUpSmooth () {
- if (!this.cameraNode) return;
-
- const startPos = this.cameraNode.position.clone();
-
- // Calculate movement based on reference line position
- const moveDistance = this.calculateMoveDistanceToShowLine();
- if (moveDistance <= 0) return;
-
- const targetPos = new Vec3(startPos.x, startPos.y + moveDistance, 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();
- }
- /* ========= 私有辅助方法 ========= */
- /**
- * Calculate the distance camera needs to move to show the reference line at the bottom of the view.
- * @returns The movement distance in world units
- */
- private calculateMoveDistanceToShowLine(): number {
- if (!this.referenceLineNode || !this.cameraNode) {
- console.warn('GameStartMove.calculateMoveDistanceToShowLine: referenceLineNode or cameraNode not set');
- return 0;
- }
- // Get the camera component
- const cameraComponent = this.cameraNode.getComponent(Camera);
- if (!cameraComponent) {
- console.warn('GameStartMove.calculateMoveDistanceToShowLine: Camera component not found');
- return 0;
- }
- // Get the reference line's world position
- const lineWorldPos = this.referenceLineNode.getWorldPosition();
-
- // Get camera's current world position
- const cameraWorldPos = this.cameraNode.getWorldPosition();
-
- // Calculate camera's view height in world units
- // For orthographic camera, orthoHeight represents half of the view height
- const cameraViewHalfHeight = cameraComponent.orthoHeight;
-
- // Calculate the distance to move camera so that the line appears at the bottom
- // We want: cameraWorldPos.y - cameraViewHalfHeight = lineWorldPos.y
- // So: moveDistance = cameraWorldPos.y - lineWorldPos.y - cameraViewHalfHeight
- const moveDistance = cameraWorldPos.y - lineWorldPos.y - cameraViewHalfHeight;
-
- console.log('GameStartMove.calculateMoveDistanceToShowLine 计算移动距离:', {
- lineWorldPos: lineWorldPos,
- cameraWorldPos: cameraWorldPos,
- cameraViewHalfHeight: cameraViewHalfHeight,
- moveDistance: moveDistance
- });
-
- return Math.max(0, moveDistance); // Ensure we don't move in wrong direction
- }
- /**
- * 获取参考线在diban父节点坐标系中的位置和diban高度
- * @returns 包含referenceLineLocalPos和dibanHeight的对象,如果节点未设置则返回null
- */
- private getReferenceLinePositionAndDibanHeight(): { referenceLineLocalPos: Vec3, dibanHeight: number } | null {
- if (!this.dibanNode || !this.referenceLineNode) {
- return null;
- }
-
- // 获取参考线的世界位置
- const referenceLineWorldPos = this.referenceLineNode.getWorldPosition();
-
- // 将参考线的世界位置转换为diban父节点的本地坐标
- const referenceLineLocalPos = new Vec3();
- this.dibanNode.parent.getComponent(UITransform).convertToNodeSpaceAR(referenceLineWorldPos, referenceLineLocalPos);
-
- // 获取diban高度
- const dibanUITransform = this.dibanNode.getComponent(UITransform);
- const dibanHeight = dibanUITransform ? dibanUITransform.height : 0;
-
- return { referenceLineLocalPos, dibanHeight };
- }
- /* ========= 高级动画方法 ========= */
- /**
- * 上滑 diban节点,进入备战状态。同时镜头下移。
- * @param duration 动画时长,默认 0.3s
- */
- public slideUpFromBottom(duration: number = 0.3) {
-
- // 显示diban节点
- this.dibanNode.active = true;
-
- // 获取参考线位置和diban高度
- const positionData = this.getReferenceLinePositionAndDibanHeight();
- if (!positionData) {
- return;
- }
-
- const { referenceLineLocalPos, dibanHeight } = positionData;
-
- // 计算diban的初始位置:diban顶部与参考线对齐
- const startPos = new Vec3(this.dibanNode.position.x, referenceLineLocalPos.y - dibanHeight / 2, this.dibanNode.position.z);
-
- // 计算目标位置:diban底部与参考线对齐
- const targetPos = new Vec3(this.dibanNode.position.x, referenceLineLocalPos.y + dibanHeight / 2, this.dibanNode.position.z);
-
- // 设置diban初始位置
- this.dibanNode.setPosition(startPos);
-
- // 停止任何正在运行的动画
- Tween.stopAllByTarget(this.dibanNode);
-
- // 执行上滑动画
- tween(this.dibanNode)
- .to(duration, { position: targetPos }, { easing: 'quadInOut' })
- .call(() => {
- const animationEndTime = Date.now();
- })
- .start();
-
- // 同时执行camera下移动画
- this.moveDownInstant();
- }
- /**
- * 下滑 diban节点,结束备战进入playing 状态。
- * @param duration 动画时长,默认 0.3s
- */
- public slideDibanDownAndHide(duration: number = 0.3) {
- // 使用装饰器属性中的diban节点
- if (!this.dibanNode) {
- console.warn('GameStartMove.slideDibanDownAndHide diban节点未设置');
- return;
- }
-
- // 检查参考线节点
- if (!this.referenceLineNode) {
- console.warn('GameStartMove.slideDibanDownAndHide 参考线节点未设置');
- return;
- }
-
- // 获取当前位置
- const currentPos = this.dibanNode.position.clone();
- console.log('GameStartMove.slideDibanDownAndHide diban当前位置:', currentPos);
-
- // 获取参考线位置和diban高度
- const positionData = this.getReferenceLinePositionAndDibanHeight();
- if (!positionData) {
- return;
- }
-
- const { referenceLineLocalPos, dibanHeight } = positionData;
-
- // 计算目标位置:diban顶部与参考线对齐(下滑到初始位置)
- const targetPos = new Vec3(currentPos.x, referenceLineLocalPos.y - dibanHeight / 2, 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;
- }
- // 动画完成后隐藏diban节点
- this.dibanNode.active = false;
- })
- .start();
-
- // 同时执行camera上移动画
- this.moveUpSmooth();
-
- console.log('GameStartMove.slideDibanDownAndHide diban下滑动画已启动');
- }
- }
|