BlockItemView.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * @Author: mojunshou 1637302775@qq.com
  3. * @Date: 2025-03-27 15:04:15
  4. * @LastEditors: mojunshou 1637302775@qq.com
  5. * @LastEditTime: 2025-03-27 15:21:52
  6. * @Description:
  7. */
  8. import { tween, Color, Sprite, UITransform, UIOpacity, v3 } from 'cc';
  9. import { _decorator, EventTouch, Prefab, SpriteFrame, Vec3, Node } from 'cc';
  10. import { GameComponent } from "db://oops-framework/module/common/GameComponent";
  11. const { ccclass, property } = _decorator;
  12. // 定义方块类型
  13. export enum BlockColorType {
  14. RED = 0,
  15. BLUE = 1,
  16. GREEN = 2,
  17. YELLOW = 3,
  18. PURPLE = 4
  19. }
  20. // 定义方块形状模板(每种形状的格子偏移位置)
  21. export const BLOCK_SHAPES = [
  22. // I形状
  23. [[0, 0], [0, 1], [0, 2]],
  24. // L形状
  25. [[0, 0], [0, 1], [1, 1]],
  26. // 反L形状
  27. [[0, 0], [0, 1], [-1, 1]],
  28. // T形状
  29. [[0, 0], [-1, 0], [1, 0], [0, 1]],
  30. // 方形
  31. [[0, 0], [1, 0], [0, 1], [1, 1]],
  32. // Z形状
  33. [[0, 0], [1, 0], [0, 1], [-1, 1]],
  34. // S形状
  35. [[0, 0], [-1, 0], [0, 1], [1, 1]]
  36. ];
  37. /** 显示对象控制 */
  38. @ccclass('BlockItemView')
  39. export class BlockItemView extends GameComponent {
  40. @property([SpriteFrame])
  41. blockSprites: SpriteFrame[] = [];
  42. @property(Prefab)
  43. cellPrefab: Prefab = null!;
  44. // 方块当前类型
  45. private blockType: BlockColorType = BlockColorType.RED;
  46. // 方块形状索引
  47. private shapeIndex: number = 0;
  48. // 方块图标索引
  49. private iconIndex: number = 0;
  50. // 方块单元格尺寸
  51. private cellSize: number = 82;
  52. // 方块单元格节点数组
  53. private cells: Node[] = [];
  54. // 方块当前形状表示(格子坐标偏移)
  55. private currentShape: number[][] = [];
  56. // 是否可以旋转
  57. private isRotatable: boolean = false;
  58. // 是否可以拖拽
  59. private isDraggable: boolean = true;
  60. // 是否正在拖拽
  61. private isDragging: boolean = false;
  62. // 原始位置(拖拽失败时返回)
  63. private originalPosition: Vec3 = null!;
  64. protected start() {
  65. this.originalPosition = this.node.position.clone();
  66. this.initEventListeners();
  67. }
  68. /**
  69. * 初始化事件监听
  70. */
  71. initEventListeners() {
  72. this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
  73. this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
  74. this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
  75. this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
  76. }
  77. /**
  78. * 触摸开始事件
  79. */
  80. onTouchStart(event: EventTouch) {
  81. console.log("onTouchStart");
  82. if (!this.isDraggable) return;
  83. this.isDragging = true;
  84. this.originalPosition = this.node.position.clone();
  85. // 通知游戏组件方块被选中
  86. this.node.emit('block_selected', this);
  87. }
  88. /**
  89. * 触摸移动事件
  90. */
  91. onTouchMove(event: EventTouch) {
  92. if (!this.isDragging) return;
  93. const delta = event.getDelta();
  94. this.node.setPosition(
  95. this.node.position.x + delta.x,
  96. this.node.position.y + delta.y,
  97. this.node.position.z
  98. );
  99. // 通知游戏组件方块正在移动
  100. this.node.emit('block_moving', {
  101. block: this,
  102. position: this.node.position
  103. });
  104. }
  105. /**
  106. * 触摸结束事件
  107. */
  108. onTouchEnd(event: EventTouch) {
  109. if (!this.isDragging) return;
  110. this.isDragging = false;
  111. // 通知游戏组件方块放置
  112. this.node.emit('block_placed', {
  113. block: this,
  114. position: this.node.position
  115. });
  116. }
  117. /**
  118. * 返回原始位置
  119. */
  120. returnToOriginalPosition() {
  121. tween(this.node)
  122. .to(0.3, { position: this.originalPosition })
  123. .start();
  124. }
  125. /**
  126. * 移动到指定位置
  127. */
  128. moveTo(position: Vec3) {
  129. tween(this.node)
  130. .to(0.3, { position: position })
  131. .start();
  132. }
  133. /**
  134. * 高亮显示(可放置提示)
  135. */
  136. highlight(isValid: boolean) {
  137. const color = isValid ? new Color(150, 255, 150, 255) : new Color(255, 150, 150, 255);
  138. for (const cell of this.cells) {
  139. const sprite = cell.getComponent(Sprite);
  140. if (sprite) {
  141. sprite.color = color;
  142. }
  143. }
  144. }
  145. /**
  146. * 取消高亮
  147. */
  148. resetHighlight() {
  149. for (const cell of this.cells) {
  150. const sprite = cell.getComponent(Sprite);
  151. if (sprite) {
  152. sprite.color = Color.WHITE;
  153. }
  154. }
  155. }
  156. /**
  157. * 获取当前方块形状中所有单元格的世界坐标
  158. */
  159. getCellWorldPositions(): Vec3[] {
  160. const positions: Vec3[] = [];
  161. for (const cell of this.cells) {
  162. const transform = this.node.getComponent(UITransform);
  163. if (transform) {
  164. positions.push(transform.convertToWorldSpaceAR(cell.position));
  165. }
  166. }
  167. return positions;
  168. }
  169. /**
  170. * 获取当前方块类型
  171. */
  172. getBlockType(): BlockColorType {
  173. return this.blockType;
  174. }
  175. /**
  176. * 获取当前方块形状
  177. */
  178. getCurrentShape(): number[][] {
  179. return this.currentShape;
  180. }
  181. /**
  182. * 执行消除动画
  183. */
  184. playEliminateAnimation(callback?: Function) {
  185. // 播放消除动画
  186. const duration = 0.3;
  187. for (const cell of this.cells) {
  188. // 确保有 UIOpacity 组件
  189. let uiOpacity = cell.getComponent(UIOpacity);
  190. if (!uiOpacity) {
  191. uiOpacity = cell.addComponent(UIOpacity);
  192. }
  193. tween(cell)
  194. .to(duration, { scale: v3(0, 0, 1) })
  195. .start();
  196. tween(uiOpacity)
  197. .to(duration, { opacity: 0 })
  198. .start();
  199. }
  200. // 动画结束后执行回调
  201. this.scheduleOnce(() => {
  202. if (callback) callback();
  203. }, duration);
  204. }
  205. /**
  206. * 设置是否可拖拽
  207. */
  208. setDraggable(draggable: boolean) {
  209. this.isDraggable = draggable;
  210. }
  211. /**
  212. * 父节点点击事件
  213. */
  214. onParentNodeClicked(event: EventTouch) {
  215. if (!this.isDraggable) return;
  216. this.isDragging = true;
  217. this.originalPosition = this.node.position.clone();
  218. // 通知游戏组件方块被选中
  219. this.node.emit('block_selected', this);
  220. }
  221. /**
  222. * 设置this.cellPrefab下边icon 的样式
  223. *
  224. */
  225. setBlockSpriteFrame(spriteFrame: SpriteFrame) {
  226. for (const cell of this.cells) {
  227. const sprite = cell.getChildByName("icon")?.getComponent(Sprite);
  228. if (sprite) {
  229. sprite.spriteFrame = spriteFrame;
  230. }
  231. }
  232. }
  233. }