GameStartMove.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import { _decorator, Component, Node, Vec3, tween, Tween } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * GameStartMove
  5. *
  6. * This component is expected to be attached to the main camera node.
  7. * It provides two public helper methods that other scripts can call:
  8. * 1. moveDownInstant() – instantly move the camera down by a fixed offset
  9. * 2. moveUpSmooth() – move the camera back up with a smooth tween animation
  10. */
  11. @ccclass('GameStartMove')
  12. export class GameStartMove extends Component {
  13. /** The camera node to move. Defaults to the node the script is attached to. */
  14. @property({
  15. type: Node,
  16. tooltip: 'Camera node to move. Leave empty to use the current node.'
  17. })
  18. public cameraNode: Node = null;
  19. /** Offset (in pixels/units) for one step of movement. */
  20. @property({ tooltip: 'Vertical offset for the camera movement.' })
  21. public offset: number = 182;
  22. /** Tween duration for the smooth move-up animation. */
  23. @property({ tooltip: 'Duration for the smooth move-up tween (seconds).' })
  24. public moveDuration: number = 0.4;
  25. private _originalPos: Vec3 = new Vec3();
  26. private _originalDibanPos: Vec3 = new Vec3();
  27. onLoad () {
  28. // Use self node if cameraNode not assigned via the editor.
  29. if (!this.cameraNode) {
  30. this.cameraNode = this.node;
  31. }
  32. // Save initial position so we can always restore relative to it.
  33. this._originalPos.set(this.cameraNode.position);
  34. // Save diban original position if available
  35. if (this.dibanNode) {
  36. this._originalDibanPos.set(this.dibanNode.position);
  37. }
  38. }
  39. /**
  40. * Instantly move the camera down by `offset` units.
  41. */
  42. public moveDownInstant () {
  43. if (!this.cameraNode) return;
  44. const pos = this.cameraNode.position.clone();
  45. pos.y -= this.offset;
  46. this.cameraNode.setPosition(pos);
  47. }
  48. /**
  49. * Smoothly move the camera back up by `offset` units using a tween.
  50. */
  51. public moveUpSmooth () {
  52. if (!this.cameraNode) return;
  53. const startPos = this.cameraNode.position.clone();
  54. const targetPos = new Vec3(startPos.x, startPos.y + this.offset, startPos.z);
  55. // Stop any running tweens on this node to avoid conflicting animations.
  56. Tween.stopAllByTarget(this.cameraNode);
  57. tween(this.cameraNode)
  58. .to(this.moveDuration, { position: targetPos }, { easing: 'quadOut' })
  59. .start();
  60. }
  61. /* ========= BlockSelectionUI 动画 ========= */
  62. /** BlockSelectionUI 根节点(外部在编辑器拖拽赋值) */
  63. @property({ type: Node, tooltip: 'BlockSelectionUI 根节点' })
  64. public blockSelectionUI: Node = null;
  65. /** BlockSelectionUI 中需要下滑的 diban 节点(外部拖拽赋值) */
  66. @property({ type: Node, tooltip: 'BlockSelectionUI 中的 diban 节点' })
  67. public dibanNode: Node = null;
  68. /**
  69. * 下滑 diban 并在动画结束后隐藏 BlockSelectionUI。
  70. * @param distance 向下移动距离,默认 300
  71. * @param duration 动画时长,默认 0.3s
  72. */
  73. public slideDibanDownAndHide(distance: number = 300, duration: number = 0.3) {
  74. console.log('GameStartMove.slideDibanDownAndHide 开始执行');
  75. if (!this.dibanNode || !this.blockSelectionUI) {
  76. console.log('GameStartMove.slideDibanDownAndHide 条件检查失败:', {
  77. dibanNode: !!this.dibanNode,
  78. blockSelectionUI: !!this.blockSelectionUI
  79. });
  80. return;
  81. }
  82. // 使用存储的原始位置,如果没有存储则使用当前位置
  83. let originalPos: Vec3;
  84. if (this._originalDibanPos.equals(Vec3.ZERO)) {
  85. // 如果没有存储原始位置,使用当前位置并存储它
  86. originalPos = this.dibanNode.position.clone();
  87. this._originalDibanPos = originalPos.clone();
  88. console.log('GameStartMove.slideDibanDownAndHide 首次使用,存储原始位置:', this._originalDibanPos);
  89. } else {
  90. // 使用存储的原始位置
  91. originalPos = this._originalDibanPos.clone();
  92. console.log('GameStartMove.slideDibanDownAndHide 使用存储的原始位置:', originalPos);
  93. }
  94. // 获取当前位置作为动画起始位置
  95. const currentPos = this.dibanNode.position.clone();
  96. console.log('GameStartMove.slideDibanDownAndHide 当前位置:', currentPos);
  97. // 停止现有 tween
  98. Tween.stopAllByTarget(this.dibanNode);
  99. const targetPos = new Vec3(currentPos.x, currentPos.y - distance, currentPos.z);
  100. console.log('GameStartMove.slideDibanDownAndHide 目标位置:', targetPos);
  101. tween(this.dibanNode)
  102. .to(duration, { position: targetPos }, { easing: 'quadIn' })
  103. .call(() => {
  104. console.log('GameStartMove.slideDibanDownAndHide 动画完成回调');
  105. // 先隐藏BlockSelectionUI,然后立即重置diban位置到存储的原始位置
  106. // 这样用户就看不到位置重置的过程
  107. this.blockSelectionUI.active = false;
  108. this.dibanNode.setPosition(originalPos);
  109. console.log('GameStartMove.slideDibanDownAndHide UI已隐藏,位置已重置到:', originalPos);
  110. })
  111. .start();
  112. console.log('GameStartMove.slideDibanDownAndHide 动画已启动');
  113. }
  114. /**
  115. * 从底部上滑显示 diban
  116. * @param distance 从下方移动的距离,默认 300
  117. * @param duration 动画时长,默认 0.3s
  118. */
  119. public slideUpFromBottom(distance: number = 300, duration: number = 0.3) {
  120. console.log('GameStartMove.slideUpFromBottom 开始执行');
  121. if (!this.dibanNode || !this.blockSelectionUI) {
  122. console.log('GameStartMove.slideUpFromBottom 条件检查失败:', {
  123. dibanNode: !!this.dibanNode,
  124. blockSelectionUI: !!this.blockSelectionUI
  125. });
  126. return;
  127. }
  128. // 确保BlockSelectionUI可见
  129. this.blockSelectionUI.active = true;
  130. // 使用存储的原始位置作为目标位置
  131. let targetPos: Vec3;
  132. if (this._originalDibanPos.equals(Vec3.ZERO)) {
  133. // 如果没有存储原始位置,使用当前位置并存储它
  134. targetPos = this.dibanNode.position.clone();
  135. this._originalDibanPos = targetPos.clone();
  136. console.log('GameStartMove.slideUpFromBottom 首次使用,存储原始位置:', this._originalDibanPos);
  137. } else {
  138. // 使用存储的原始位置
  139. targetPos = this._originalDibanPos.clone();
  140. console.log('GameStartMove.slideUpFromBottom 使用存储的原始位置:', targetPos);
  141. }
  142. // 设置初始位置(在目标位置下方)
  143. const startPos = new Vec3(targetPos.x, targetPos.y - distance, targetPos.z);
  144. this.dibanNode.setPosition(startPos);
  145. console.log('GameStartMove.slideUpFromBottom 起始位置:', startPos);
  146. // 停止现有 tween
  147. Tween.stopAllByTarget(this.dibanNode);
  148. // 播放上滑动画到目标位置
  149. tween(this.dibanNode)
  150. .to(duration, { position: targetPos }, { easing: 'quadOut' })
  151. .call(() => {
  152. console.log('GameStartMove.slideUpFromBottom 动画完成');
  153. })
  154. .start();
  155. console.log('GameStartMove.slideUpFromBottom 动画已启动');
  156. }
  157. /**
  158. * 更新保存的diban原始位置(当diban节点在运行时改变时调用)
  159. */
  160. public updateDibanOriginalPosition() {
  161. if (this.dibanNode) {
  162. this._originalDibanPos.set(this.dibanNode.position);
  163. console.log('GameStartMove.updateDibanOriginalPosition 存储原始位置:', this._originalDibanPos);
  164. } else {
  165. console.log('GameStartMove.updateDibanOriginalPosition dibanNode为空,无法存储位置');
  166. }
  167. }
  168. }