GameStartMove.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { _decorator, Component, Node, Vec3, tween, Tween, UITransform, Camera } 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 high-level animation methods for block selection mode transitions:
  8. * 1. enterBlockSelectionMode() – complete animation for entering block selection (camera down + UI slide up)
  9. * 2. exitBlockSelectionMode() – complete animation for exiting block selection (camera up + UI slide down)
  10. *
  11. * Also provides low-level methods for specific use cases:
  12. * - moveDownInstant() – instantly move the camera down by a fixed offset
  13. * - moveUpSmooth() – move the camera back up with a smooth tween animation
  14. * - slideUpFromBottom() – slide UI up from bottom
  15. * - slideDibanDownAndHide() – slide UI down and hide
  16. */
  17. @ccclass('GameStartMove')
  18. export class GameStartMove extends Component {
  19. /** The camera node to move. Defaults to the node the script is attached to. */
  20. @property({
  21. type: Node,
  22. tooltip: 'Camera node to move. Leave empty to use the current node.'
  23. })
  24. public cameraNode: Node = null;
  25. /** Reference line node for camera positioning. When set, camera will move to show this line at the bottom. */
  26. @property({
  27. type: Node,
  28. tooltip: 'Reference line node. Camera will move to position this line at the bottom of the view.'
  29. })
  30. public referenceLineNode: Node = null;
  31. /** Tween duration for the smooth move-up animation. */
  32. @property({ tooltip: 'Duration for the smooth move-up tween (seconds).' })
  33. public moveDuration: number = 0.4;
  34. private _originalPos: Vec3 = new Vec3();
  35. /** 需要下滑的 diban 节点(外部拖拽赋值) */
  36. @property({ type: Node, tooltip: 'diban 节点' })
  37. public dibanNode: Node = null;
  38. /** Gray遮罩节点 */
  39. @property({ type: Node, tooltip: 'Canvas/GameLevelUI/Gray节点' })
  40. public grayNode: Node = null;
  41. private _originalDibanPos: Vec3 = new Vec3();
  42. onLoad () {
  43. // Use self node if cameraNode not assigned via the editor.
  44. if (!this.cameraNode) {
  45. this.cameraNode = this.node;
  46. }
  47. // Save initial position so we can always restore relative to it.
  48. this._originalPos.set(this.cameraNode.position);
  49. // Save diban original position if dibanNode is assigned
  50. if (this.dibanNode) {
  51. this._originalDibanPos.set(this.dibanNode.position);
  52. }
  53. }
  54. /**
  55. * Instantly move the camera down to show the reference line at bottom.
  56. * Requires referenceLineNode to be set.
  57. */
  58. public moveDownInstant () {
  59. if (!this.cameraNode) return;
  60. const pos = this.cameraNode.position.clone();
  61. // Calculate movement based on reference line position
  62. const moveDistance = this.calculateMoveDistanceToShowLine();
  63. if (moveDistance > 0) {
  64. pos.y -= moveDistance;
  65. this.cameraNode.setPosition(pos);
  66. console.log('GameStartMove.moveDownInstant 镜头下移,当前位置:', pos);
  67. }
  68. // 镜头下移时显示Gray遮罩
  69. if (this.grayNode) {
  70. this.grayNode.active = true;
  71. }
  72. }
  73. /**
  74. * Smoothly move the camera back up using a tween to original position.
  75. * Requires referenceLineNode to be set.
  76. */
  77. public moveUpSmooth () {
  78. if (!this.cameraNode) return;
  79. const startPos = this.cameraNode.position.clone();
  80. // Calculate movement based on reference line position
  81. const moveDistance = this.calculateMoveDistanceToShowLine();
  82. if (moveDistance <= 0) return;
  83. const targetPos = new Vec3(startPos.x, startPos.y + moveDistance, startPos.z);
  84. // Stop any running tweens on this node to avoid conflicting animations.
  85. console.log('GameStartMove.moveUpSmooth 镜头上移,开始执行');
  86. Tween.stopAllByTarget(this.cameraNode);
  87. tween(this.cameraNode)
  88. .to(this.moveDuration, { position: targetPos }, { easing: 'quadOut' })
  89. .start();
  90. }
  91. /* ========= 私有辅助方法 ========= */
  92. /**
  93. * Calculate the distance camera needs to move to show the reference line at the bottom of the view.
  94. * @returns The movement distance in world units
  95. */
  96. private calculateMoveDistanceToShowLine(): number {
  97. if (!this.referenceLineNode || !this.cameraNode) {
  98. console.warn('GameStartMove.calculateMoveDistanceToShowLine: referenceLineNode or cameraNode not set');
  99. return 0;
  100. }
  101. // Get the camera component
  102. const cameraComponent = this.cameraNode.getComponent(Camera);
  103. if (!cameraComponent) {
  104. console.warn('GameStartMove.calculateMoveDistanceToShowLine: Camera component not found');
  105. return 0;
  106. }
  107. // Get the reference line's world position
  108. const lineWorldPos = this.referenceLineNode.getWorldPosition();
  109. // Get camera's current world position
  110. const cameraWorldPos = this.cameraNode.getWorldPosition();
  111. // Calculate camera's view height in world units
  112. // For orthographic camera, orthoHeight represents half of the view height
  113. const cameraViewHalfHeight = cameraComponent.orthoHeight;
  114. // Calculate the distance to move camera so that the line appears at the bottom
  115. // We want: cameraWorldPos.y - cameraViewHalfHeight = lineWorldPos.y
  116. // So: moveDistance = cameraWorldPos.y - lineWorldPos.y - cameraViewHalfHeight
  117. const moveDistance = cameraWorldPos.y - lineWorldPos.y - cameraViewHalfHeight;
  118. console.log('GameStartMove.calculateMoveDistanceToShowLine 计算移动距离:', {
  119. lineWorldPos: lineWorldPos,
  120. cameraWorldPos: cameraWorldPos,
  121. cameraViewHalfHeight: cameraViewHalfHeight,
  122. moveDistance: moveDistance
  123. });
  124. return Math.max(0, moveDistance); // Ensure we don't move in wrong direction
  125. }
  126. /**
  127. * 获取参考线在diban父节点坐标系中的位置和diban高度
  128. * @returns 包含referenceLineLocalPos和dibanHeight的对象,如果节点未设置则返回null
  129. */
  130. private getReferenceLinePositionAndDibanHeight(): { referenceLineLocalPos: Vec3, dibanHeight: number } | null {
  131. if (!this.dibanNode || !this.referenceLineNode) {
  132. return null;
  133. }
  134. // 获取参考线的世界位置
  135. const referenceLineWorldPos = this.referenceLineNode.getWorldPosition();
  136. // 将参考线的世界位置转换为diban父节点的本地坐标
  137. const referenceLineLocalPos = new Vec3();
  138. this.dibanNode.parent.getComponent(UITransform).convertToNodeSpaceAR(referenceLineWorldPos, referenceLineLocalPos);
  139. // 获取diban高度
  140. const dibanUITransform = this.dibanNode.getComponent(UITransform);
  141. const dibanHeight = dibanUITransform ? dibanUITransform.height : 0;
  142. return { referenceLineLocalPos, dibanHeight };
  143. }
  144. /* ========= 高级动画方法 ========= */
  145. /**
  146. * 上滑 diban节点,进入备战状态。同时镜头下移。
  147. * @param duration 动画时长,默认 0.3s
  148. */
  149. public slideUpFromBottom(duration: number = 0.3) {
  150. // 显示diban节点
  151. this.dibanNode.active = true;
  152. // 获取参考线位置和diban高度
  153. const positionData = this.getReferenceLinePositionAndDibanHeight();
  154. if (!positionData) {
  155. return;
  156. }
  157. const { referenceLineLocalPos, dibanHeight } = positionData;
  158. // 计算diban的初始位置:diban顶部与参考线对齐
  159. const startPos = new Vec3(this.dibanNode.position.x, referenceLineLocalPos.y - dibanHeight / 2, this.dibanNode.position.z);
  160. // 计算目标位置:diban底部与参考线对齐
  161. const targetPos = new Vec3(this.dibanNode.position.x, referenceLineLocalPos.y + dibanHeight / 2, this.dibanNode.position.z);
  162. // 设置diban初始位置
  163. this.dibanNode.setPosition(startPos);
  164. // 停止任何正在运行的动画
  165. Tween.stopAllByTarget(this.dibanNode);
  166. // 执行上滑动画
  167. tween(this.dibanNode)
  168. .to(duration, { position: targetPos }, { easing: 'quadInOut' })
  169. .call(() => {
  170. const animationEndTime = Date.now();
  171. })
  172. .start();
  173. // 同时执行camera下移动画
  174. this.moveDownInstant();
  175. }
  176. /**
  177. * 下滑 diban节点,结束备战进入playing 状态。
  178. * @param duration 动画时长,默认 0.3s
  179. */
  180. public slideDibanDownAndHide(duration: number = 0.3) {
  181. // 使用装饰器属性中的diban节点
  182. if (!this.dibanNode) {
  183. console.warn('GameStartMove.slideDibanDownAndHide diban节点未设置');
  184. return;
  185. }
  186. // 检查参考线节点
  187. if (!this.referenceLineNode) {
  188. console.warn('GameStartMove.slideDibanDownAndHide 参考线节点未设置');
  189. return;
  190. }
  191. // 获取当前位置
  192. const currentPos = this.dibanNode.position.clone();
  193. console.log('GameStartMove.slideDibanDownAndHide diban当前位置:', currentPos);
  194. // 获取参考线位置和diban高度
  195. const positionData = this.getReferenceLinePositionAndDibanHeight();
  196. if (!positionData) {
  197. return;
  198. }
  199. const { referenceLineLocalPos, dibanHeight } = positionData;
  200. // 计算目标位置:diban顶部与参考线对齐(下滑到初始位置)
  201. const targetPos = new Vec3(currentPos.x, referenceLineLocalPos.y - dibanHeight / 2, currentPos.z);
  202. console.log('GameStartMove.slideDibanDownAndHide diban目标位置:', targetPos);
  203. // 停止任何正在运行的动画
  204. Tween.stopAllByTarget(this.dibanNode);
  205. // 执行下滑动画
  206. tween(this.dibanNode)
  207. .to(duration, { position: targetPos }, { easing: 'quadOut' })
  208. .call(() => {
  209. console.log('GameStartMove.slideDibanDownAndHide diban下滑动画完成');
  210. // 动画完成时隐藏Gray遮罩
  211. if (this.grayNode) {
  212. this.grayNode.active = false;
  213. }
  214. // 动画完成后隐藏diban节点
  215. this.dibanNode.active = false;
  216. })
  217. .start();
  218. // 同时执行camera上移动画
  219. this.moveUpSmooth();
  220. console.log('GameStartMove.slideDibanDownAndHide diban下滑动画已启动');
  221. }
  222. }