import Gamecfg from "../../common/gameCfg"; import { gameMethod } from "../../common/gameMethod"; import { xlsChapterLayout } from "../../common/xlsConfig"; import Config from "../../Config"; import GameDataCenter from "../../data/GameDataCenter"; import UEBase from "../../frameWork/compment/UEBase"; import { HcInfoGeziInfo, ResHcInfo } from "../../shared/hc/PtlHcInfo"; import { ResHcMerge } from "../../shared/hc/PtlHcMerge"; import AssetMgr from "../../utils/AssetMgr"; import { GridConstant } from "./GridConstant"; import { GridEvent } from "./GridEvent"; import UECell, { I_CellData } from "./UECell"; import UECube from "./UECube"; import UEMergeTip from "./UEMergeTip"; const { ccclass, property } = cc._decorator; const DRAG_THRESHOLD: number = 10; // 拖动判定阈值(像素) @ccclass export default class UEGridMap extends UEBase { static readonly BundleKey: string = "gridMap"; static readonly PrefabUrl: string = "UEGridMap"; static readonly CLS: string = "UEGridMap"; @property(cc.Prefab) cellPrefab: cc.Prefab = null!; @property(cc.Prefab) cubePrefab: cc.Prefab = null!; @property(cc.Node) gridLayer: cc.Node = null; @property(cc.Node) cellLayer: cc.Node = null; @property(cc.Node) cubeLayer: cc.Node = null; @property(cc.Node) tipLayer: cc.Node = null; @property(cc.Node) effectLayer: cc.Node = null; cellMap: { [gid: number]: UECell } = {}; private isDragging: boolean = false; private dragStartPos: cc.Vec2 = cc.v2(0, 0); private selectedCell: UECell = null!; private lastMoveCell: UECell = null!; private clickCnt: number = 0; private ueMergeTip: UEMergeTip; Init() { this.ueMergeTip = AssetMgr.instantiateUE(UEMergeTip); this.tipLayer.addChild(this.ueMergeTip.node); this.ueMergeTip.node.active = false; GameDataCenter.gridMap.Init(this); this.gridLayer.setContentSize(GridConstant.CELL_WIDTH * GridConstant.ROW, GridConstant.CELL_WIDTH * GridConstant.COL); // 计算缩放比例 // ratio为0表示屏幕高度等于标准高度1750 // ratio为1表示屏幕高度等于1334(需要缩放15%) const ratio = Math.max(0, (GridConstant.CELL_REAL_WIN_HEIGHT - cc.winSize.height) / (GridConstant.CELL_REAL_WIN_HEIGHT - Config.realHeight)); // 当ratio为1时,scale = 0.85(缩放15%) // 当ratio为0时,scale = 1(不缩放) const scale = 1 - (0.15 * ratio); this.node.scale = scale; this.InitEvent(); GameDataCenter.gridMap.sendHcInfo({}); } InitEvent(): void { this.gridLayer.on(cc.Node.EventType.TOUCH_START, this.OnTouchStart, this); this.gridLayer.on(cc.Node.EventType.TOUCH_MOVE, this.OnTouchMove, this); this.gridLayer.on(cc.Node.EventType.TOUCH_END, this.OnTouchEnd, this); this.gridLayer.on(cc.Node.EventType.TOUCH_CANCEL, this.OnTouchEnd, this); this.initEvent(GridEvent.TRIGGER_EMITTER, this.TriggerEmitter); this.initEvent(GridEvent.HC_INFO_RSP, this.LoadMapData); this.initEvent(GridEvent.HC_MERGE_RSP, this.OnHcMergeRsp); this.initEvent(GridEvent.HC_FIGHT_OVER, this.OnHcFightOver); } /** 加载地图数据 */ private LoadMapData(data: ResHcInfo) { this.cellMap = {}; for (let i = 0; i < GridConstant.COL; i++) { let rowCells = []; let rowCubes = []; for (let j = 0; j < GridConstant.ROW; j++) { let idx = (i + 1) * 10 + (j + 1); let cellData = data.list[idx]; if (cellData) { let cell = this.CreateCell(i, j); rowCells.push(cell); let cube = null; if (cellData.type > 0) { cube = this.CreateCube(i, j); rowCubes.push(cube); cube.Init({ type: cellData.type, id: cellData.correlationId, zIndex: idx }, idx) } cell.Init({ zIndex: idx, ueCube: cube, unlock: cellData.unlock }); this.cellMap[idx] = cell; } } } } private OnHcFightOver(data: { gzid: number, list: { [gzid: string]: HcInfoGeziInfo } }) { let cell = this.cellMap[data.gzid]; cell.ClearCube(); for (let key in data.list) { let geziData = data.list[key]; if (geziData.type != 0) { let curCell = this.cellMap[Number(key)]; if (curCell.IsLock()) { curCell.ChangeLockState(geziData.unlock); } let vec = GameDataCenter.gridMap.TranIdxToPos(Number(key)); let mergeCube = this.CreateCube(vec.y, vec.x); mergeCube.Init({ type: geziData.type, id: geziData.correlationId, zIndex: Number(key), }); curCell.SetCube(mergeCube); } } } private OnHcMergeRsp(data: { cell: UECell, cube: HcInfoGeziInfo }) { let vec = GameDataCenter.gridMap.TranIdxToPos(data.cell.GetZIndex()); let mergeCube = this.CreateCube(vec.y, vec.x); mergeCube.Init({ type: data.cube.type, id: data.cube.correlationId, zIndex: data.cell.GetZIndex(), }); data.cell.SetCube(mergeCube); //播放经验爆炸飞行动画 } /** 创建格子 */ private CreateCell(i: number, j: number) { let cell = cc.instantiate(this.cellPrefab).getComponent(UECell); this.cellLayer.addChild(cell.node); cell.node.width = GridConstant.CELL_WIDTH; cell.node.height = GridConstant.CELL_WIDTH; let pos = GameDataCenter.gridMap.GetPosByVec(i, j); cell.node.setPosition(pos); return cell; } /** 创建棋子 */ private CreateCube(i: number, j: number) { let cube = cc.instantiate(this.cubePrefab).getComponent(UECube); this.cubeLayer.addChild(cube.node); cube.node.width = GridConstant.CELL_WIDTH; cube.node.height = GridConstant.CELL_WIDTH; let pos = GameDataCenter.gridMap.GetPosByVec(i, j); cube.node.setPosition(pos); return cube; } private OnTouchStart(event: cc.Event.EventTouch): void { const touchPos = this.gridLayer.convertToNodeSpaceAR(event.getLocation()); const cell = this.GetCellByPos(touchPos); if (cell) { if (this.selectedCell) { this.selectedCell.SetSelect(false); } if (!cell.IsEmpty() && !cell.IsLock()) { if (cell.CanDrag()) { this.dragStartPos = touchPos; } if (this.selectedCell != cell) { this.clickCnt = 1; } this.selectedCell = cell; cell.SetSelect(true); } else { this.selectedCell = null; this.clickCnt = 0; } } else { this.clickCnt = 0; } } private OnTouchMove(event: cc.Event.EventTouch): void { if (!this.selectedCell || !this.dragStartPos) return; const touchPos = this.gridLayer.convertToNodeSpaceAR(event.getLocation()); const distance = touchPos.sub(this.dragStartPos).mag(); // 只有当移动距离超过阈值时才开始拖动 if (!this.isDragging && distance >= DRAG_THRESHOLD) { this.isDragging = true; this.selectedCell.GetCube().StartDrag(); this.selectedCell.SetSelect(false); } if (this.isDragging) { // 计算移动差值并应用到cube节点 const deltaPos = touchPos.sub(this.dragStartPos); const cube = this.selectedCell.GetCube(); const originalPos = cube.node.getPosition(); cube.node.setPosition(originalPos.x + deltaPos.x, originalPos.y + deltaPos.y); this.dragStartPos = touchPos; const targetCell = this.GetCellByPos(touchPos); this.lastMoveCell?.SetSelect(false); this.lastMoveCell = targetCell; targetCell?.SetSelect(true); if (targetCell && targetCell != this.selectedCell) { if (!targetCell.IsEmpty() && GameDataCenter.gridMap.CellCanPut(this.selectedCell, targetCell)) { this.ueMergeTip.node.setPosition(targetCell.node.getPosition()); this.ueMergeTip.Init({ idx: targetCell.GetZIndex(), ueCube1: this.selectedCell.GetCube(), ueCube2: targetCell.GetCube(), dir: targetCell.GetZIndex() % 10 > 4 ? 2 : 1 }); } else { this.ueMergeTip.Hide(); } } else { this.ueMergeTip.Hide(); } } } private OnTouchEnd(event: cc.Event.EventTouch): void { this.dragStartPos = null; this.ueMergeTip.Hide(); if (!this.selectedCell) return; const touchPos = this.gridLayer.convertToNodeSpaceAR(event.getLocation()); const targetCell = this.GetCellByPos(touchPos); if (!this.isDragging && this.selectedCell === targetCell) { //二次点击同一个格子 if (!targetCell.IsEmpty()) { targetCell.GetCube().PlayJellyAnim(); if (this.clickCnt >= 2) { console.log("二次点击同一个格子"); targetCell.GetCube().TriggerClick(); } else { this.clickCnt++; } } } else { if (!this.isDragging) return; if (targetCell && targetCell != this.selectedCell) { GameDataCenter.gridMap.TryMergeItems(this.selectedCell, targetCell); this.selectedCell.SetSelect(false); targetCell.SetSelect(true); } else { this.selectedCell.GetCube().BackToOriginalPos(true); } this.isDragging = false; this.selectedCell = targetCell; } } /** 根据像素坐标获取格子 */ private GetCellByPos(pos: cc.Vec2): UECell | null { const startX = -(GridConstant.ROW * GridConstant.CELL_WIDTH) / 2; const startY = (GridConstant.COL * GridConstant.CELL_WIDTH) / 2; const row = Math.floor((pos.x - startX) / GridConstant.CELL_WIDTH); const col = Math.floor((startY - pos.y) / GridConstant.CELL_WIDTH); if (col >= 0 && col < GridConstant.COL && row >= 0 && row < GridConstant.ROW) { let idx = (col + 1) * 10 + (row + 1); return this.cellMap[idx]; } return null; } /** 发射出新的物品 */ private TriggerEmitter(data: { idx: number, item: HcInfoGeziInfo, targetIdx: number }) { let targetCell = this.cellMap[data.targetIdx]; let startPos = GameDataCenter.gridMap.TranIdxToPos(data.idx); let mergeCube = this.CreateCube(startPos.y, startPos.x); mergeCube.Init({ type: data.item.type, id: data.item.correlationId, zIndex: data.idx, }); targetCell.SetCube(mergeCube); mergeCube.SetZIndex(1000); let targetPos = GameDataCenter.gridMap.TranIdxToPos(data.targetIdx); // 计算起点和终点 const startWorldPos = mergeCube.node.getPosition(); const endWorldPos = GameDataCenter.gridMap.GetPosByVec(targetPos.y, targetPos.x); // 计算方向向量和总距离 const moveVec = cc.v2(endWorldPos.x - startWorldPos.x, endWorldPos.y - startWorldPos.y); const totalDistance = moveVec.mag(); const normalizedDir = moveVec.normalize(); // 根据距离动态计算动画时间(距离越远,时间越长) const baseDuration = 0.5; // 基础动画时间 const distanceFactor = totalDistance / 300; // 假设300是标准距离 const duration = Math.min(baseDuration + distanceFactor * 0.3, 1.2); // 限制最大时间为1.2秒 // 设置最大高度(向上弹跳的高度) const maxHeight = 400; // 计算最后弹跳的起点(在终点前20%距离处) const bounceStartPos = cc.v3( endWorldPos.x - normalizedDir.x * (totalDistance * 0.2), endWorldPos.y - normalizedDir.y * (totalDistance * 0.2), 0 ); // 创建第一段抛物线(抛向空中然后落到弹跳起点) const bezier1 = []; const midPoint1 = cc.v3( startWorldPos.x + moveVec.x * 0.3, Math.max(startWorldPos.y, bounceStartPos.y) + maxHeight, 0 ); bezier1.push(cc.v2(startWorldPos.x, startWorldPos.y)); bezier1.push(cc.v2(midPoint1.x, midPoint1.y)); bezier1.push(cc.v2(bounceStartPos.x, bounceStartPos.y)); // 创建第二段弹跳(第一次弹跳) const bezier2 = []; const bounceHeight = totalDistance * 0.15; // 弹跳高度设为总距离的15% // 计算第一次弹跳的最高点和落点 const firstBounceTop = cc.v2( (bounceStartPos.x + endWorldPos.x) / 2, // 水平位置在中间 endWorldPos.y + bounceHeight // 垂直高度为弹跳高度 ); // 计算第二段距离(从bounceStartPos到终点的距离) const secondDistance = totalDistance * 0.2; // 最后20%的距离 const firstBounceEnd = cc.v2( bounceStartPos.x + normalizedDir.x * (secondDistance * 0.5), // 前进一半 bounceStartPos.y + normalizedDir.y * (secondDistance * 0.5) ); bezier2.push(cc.v2(bounceStartPos.x, bounceStartPos.y)); // 起跳点 bezier2.push(firstBounceTop); // 最高点 bezier2.push(firstBounceEnd); // 第一次落点 // 创建第三段弹跳(最后一次弹跳到终点) const bezier3 = []; const secondBounceTop = cc.v2( (firstBounceEnd.x + endWorldPos.x) / 2, endWorldPos.y + bounceHeight * 0.6 // 第二次弹跳高度为第一次的60% ); bezier3.push(firstBounceEnd); // 起跳点 bezier3.push(secondBounceTop); // 最高点 bezier3.push(cc.v2(endWorldPos.x, endWorldPos.y)); // 终点 // 创建动作序列 const bezierAction1 = cc.bezierTo(duration * 0.7, bezier1); const bezierAction2 = cc.bezierTo(duration * 0.18, bezier2); const bezierAction3 = cc.bezierTo(duration * 0.12, bezier3); const seq = cc.sequence( bezierAction1, bezierAction2, bezierAction3, cc.callFunc(() => { mergeCube.SetZIndex(data.targetIdx); }) ); // 执行动画 mergeCube.node.runAction(seq); } }