import { _decorator, BoxCollider, Camera, Component, director, EventTouch, find, Game, geometry, Layers, Layout, Node, PhysicsSystem, Quat, RigidBody, Size, sp, tween, UITransform, v3, Vec3 } from 'cc'; import { gui } from '../../core/ui/ui'; import { UI_Idioms } from '../ui/UI_Idioms/UI_Idioms'; import { UI_Main } from '../ui/UI_Main/UI_Main'; import { Container_Manager } from './Container_Manager'; import { Cube_Infor, Cube_State } from './Cube_Infor'; import { UI_LatticeFull } from '../ui/UI_LatticeFull/UI_LatticeFull'; import { UI_Lock } from '../ui/UI_Lock/UI_Lock'; import { Layout_Main } from '../ui/UI_Main/Layout_Main'; import { Toast } from '../../core/util_class/Toast'; import ch_audio from '../../ch/audio/audio'; import { Hall } from '../hall/Hall'; import { ch } from '../../ch/ch'; const { ccclass, property } = _decorator; @ccclass('GameCtl') export class GameCtl extends Component { static instance: GameCtl = null; @property(Camera) camera: Camera = null; @property([Node]) Ani: Node[] = []; @property(Node) Mid: Node[] = []; Container: Container_Manager; onLoad() { GameCtl.instance = this; this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.Container = find('Container').getComponent(Container_Manager); } update(deltaTime: number) { } onTouchStart(event: EventTouch) { } onTouchMove(event: EventTouch) { } onTouchEnd(event: EventTouch) { if (this.Container.canTouch) { this.shootRay(event); } } shootRay(event: EventTouch) { if (!this.camera) return; let ray = new geometry.Ray(); this.camera.screenPointToRay(event.getLocationX(), event.getLocationY(), ray); if (PhysicsSystem.instance.raycast(ray)) { const results = PhysicsSystem.instance.raycastResults; // 筛选出不在 "IGNORE_RAYCAST" 层的物体 let filteredResults = results.filter(e => e.collider.node.layer !== (1 << Layers.nameToLayer('IGNORE_RAYCAST'))); // 根据距离排序,确保第 0 个是最近的物体 filteredResults.sort((a, b) => a.distance - b.distance); console.log(filteredResults); if (filteredResults.length > 0) { const collider = filteredResults[0].collider; const node = collider.node; // 检查物体是否可消除 if (node.getComponent(Cube_Infor)?.state === Cube_State.live) { ch_audio.getInstance().playOneShot('sound/click_Cube') if (Hall.getInstance().firstEnter) { if (this.Container.nodeReferences.length > 4) { gui.get(UI_Main).getLayout().Hands[this.Container.nodeReferences.indexOf(node)].active = false; } } this.entryContainer(node); } else if (node.name === 'Lock') { this.UnLock(node); } } } console.log('发射了射线'); } entryContainer(node: Node) { // 判断容器剩余容量 let startIndex = -1; let targetPos = new Vec3(); let txt_length = node.getComponent(Cube_Infor).Text.length; console.log(txt_length); // 判断字长 并 判断是否还有空间可放置 switch (txt_length) { case 1: for (let i = 0; i < this.Container.unlock_Num; i++) { if (this.Container.node_isIdiom[i] === false) { startIndex = i; // 找到连续的三个 false,记录起始位置 break; // 找到第一个符合条件的位置后停止 } } if (startIndex !== -1) { // 如果找到了一个值为 false 的元素 targetPos = this.Container.nodes[startIndex].getWorldPosition().clone(); this.Container.node_isIdiom[startIndex] = true; console.log(targetPos); } else { Toast.makeText(gui.getLayerNode(5), "剩余槽位无法放下该词块").show(); } break; case 2: { for (let i = 0; i < this.Container.unlock_Num - 1; i++) { if (this.Container.node_isIdiom[i] === false && this.Container.node_isIdiom[i + 1] === false) { startIndex = i; // 找到连续的两个 false,记录起始位置 break; // 找到第一个符合条件的位置后停止 } } if (startIndex !== -1) { console.log("找到连续的两个 false,起始索引是:", startIndex); // 可以在此使用 startIndex 进行后续操作,例如: let pos1 = this.Container.nodes[startIndex].getWorldPosition(); let pos2 = this.Container.nodes[startIndex + 1].getWorldPosition(); this.Container.node_isIdiom[startIndex] = true; this.Container.node_isIdiom[startIndex + 1] = true; targetPos.set( (pos1.x + pos2.x) / 2, (pos1.y + pos2.y) / 2, (pos1.z + pos2.z) / 2 ); console.log(targetPos); } else { Toast.makeText(gui.getLayerNode(5), "剩余槽位无法放下该词块").show(); } break; } case 3: { for (let i = 0; i < this.Container.unlock_Num - 2; i++) { if (this.Container.node_isIdiom[i] === false && this.Container.node_isIdiom[i + 1] === false && this.Container.node_isIdiom[i + 2] === false) { startIndex = i; // 找到连续的三个 false,记录起始位置 break; // 找到第一个符合条件的位置后停止 } } if (startIndex !== -1) { // 找到连续三个 false,startIndex 即为最前面的索引 console.log("找到连续的三个 false,起始索引是:", startIndex); // 可以在此使用 startIndex 进行后续操作,例如: let pos1 = this.Container.nodes[startIndex].getWorldPosition(); let pos2 = this.Container.nodes[startIndex + 1].getWorldPosition(); let pos3 = this.Container.nodes[startIndex + 2].getWorldPosition(); this.Container.node_isIdiom[startIndex] = true; this.Container.node_isIdiom[startIndex + 1] = true; this.Container.node_isIdiom[startIndex + 2] = true; targetPos.set( (pos1.x + pos2.x + pos3.x) / 3, (pos1.y + pos2.y + pos3.y) / 3, (pos1.z + pos2.z + pos3.z) / 3 ); console.log(targetPos); } else { Toast.makeText(gui.getLayerNode(5), "剩余槽位无法放下该词块").show(); } break; } default: return; } if (startIndex !== -1) { // node.getComponent(Cube_Infor).lock = true; //生成一个新方块在场上剩余方块的方块的下方 this.Container.instantiateNewCube(); node.getComponent(Cube_Infor).state = Cube_State.wait; node.getComponent(Cube_Infor).rigidbody.type = RigidBody.Type.STATIC; // 禁用重力 let targetRotation = new Quat(); let rotationX = node.eulerAngles.x; // 由于你初始是绕 X 轴旋转了 -90度,所以判断区间可以参考这个旋转角度 if (rotationX >= -180 && rotationX < 0) { Quat.fromEuler(targetRotation, -90, 0, 0); } else { Quat.fromEuler(targetRotation, 90, 0, 0); } this.Container.canTouch = false; tween(node) .to(0.02, { position: new Vec3(node.position.x, 5, node.position.z), rotation: targetRotation }) .to(0.2, { position: new Vec3(targetPos.x, targetPos.y, targetPos.z + 0.4), rotation: targetRotation }) .call(() => { this.Container.idiom_combine.set(node.getComponent(Cube_Infor), startIndex); // 执行判断成语合成逻辑 let matchedCubes: Cube_Infor[] = []; let flag = this.Container.checkIdiom_Combine(node.getComponent(Cube_Infor), matchedCubes); if (flag) { // 执行合成动画 console.log("匹配的成语方块:", matchedCubes); this.combine_ani(matchedCubes[0], matchedCubes[1]); } else { //高亮 gui.get(UI_Idioms).light_Show(node.getComponent(Cube_Infor)); //判断槽子满了吗 let count = 0; for (let element of this.Container.node_isIdiom) { if (element == true) { count++; } } if (count == this.Container.unlock_Num) { gui.show(UI_LatticeFull); } else if (count >= this.Container.unlock_Num - 2 && this.Container.unlock_Num != 9) { if (!this.Container.is_Show_UI_Lock) { gui.show(UI_Lock); this.Container.is_Show_UI_Lock = true; } } this.Container.canTouch = true; } }) .start(); } } //合成动画 combine_ani(cube1: Cube_Infor, cube2: Cube_Infor) { this.Container.canTouch = true; for (let i = this.Container.idiom_combine.get(cube1); i < this.Container.idiom_combine.get(cube1) + cube1.Text.length; i++) { this.Container.node_isIdiom[i] = false; } this.Container.idiom_combine.delete(cube1); for (let i = this.Container.idiom_combine.get(cube2); i < this.Container.idiom_combine.get(cube2) + cube2.Text.length; i++) { this.Container.node_isIdiom[i] = false; } this.Container.idiom_combine.delete(cube2); // 取消相关字的高亮显示 console.log(this.Container.node_isIdiom); gui.get(UI_Idioms).light_Hide(cube1, cube2); gui.get(UI_Main).evt.emit(gui.get(UI_Main).evt.key.update_remain); this.adjustContainer(); let index1 = 0; if (cube1.Text.length == 1) { index1 = 0; } else if (cube1.Text.length == 2) { index1 = 2; } else if (cube1.Text.length == 3) { index1 = 4; } let index2 = 0; if (cube2.Text.length == 1) { index2 = 5; } else if (cube2.Text.length == 2) { index2 = 3; } else if (cube2.Text.length == 3) { index2 = 1; } // 检测容器中哪些字需要高亮显示 console.log(this.Container.node_isIdiom); // 在所有动画结束后执行 const str = this.Container.idioms.filter(c => c.idiom !== cube1.Text + cube2.Text); this.Container.idioms=str; // 创建第一个 tween 动画 ch_audio.getInstance().playOneShot('sound/fly_Cube') const tween1 = tween(cube1.node) .to(0.25, { position: new Vec3(this.Mid[0].position.x, this.Mid[0].position.y, this.Mid[0].position.z) }).call(() => { cube1.node.layer = 1 << Layers.nameToLayer('Cube2'); cube1.node.walk((child) => { child.layer = 1 << Layers.nameToLayer('Cube2'); }) this.playAnim(); ch_audio.getInstance().playOneShot('sound/eliminate') // this.Container.skeleton2.getComponent(sp.Skeleton).setAnimation(0, 'anim2', false); }) .to(0.3, { position: new Vec3(this.Ani[index1].position.x, this.Ani[index1].position.y, this.Ani[index1].position.z + 0.4) }) .call(() => { setTimeout(() => { cube1.state = Cube_State.dead; this.Container.recycleCube(cube1.node); }, 600.0); }); // 创建第二个 tween 动画 const tween2 = tween(cube2.node) .to(0.3, { position: new Vec3(this.Mid[1].position.x, this.Mid[1].position.y, this.Mid[1].position.z) }).call(() => { cube2.node.layer = 1 << Layers.nameToLayer('Cube2'); cube2.node.walk((child) => { child.layer = 1 << Layers.nameToLayer('Cube2'); }) }) .to(0.25, { position: new Vec3(this.Ani[index2].position.x, this.Ani[index2].position.y, this.Ani[index2].position.z + 0.4) }) .call(() => { setTimeout(() => { cube2.state = Cube_State.dead; this.Container.recycleCube(cube2.node); }, 600.0); }); // 使用 tween 的并行组合功能 tween(cube1.node) .parallel(tween1, tween2) // 并行执行 tween1 和 tween2 .call(() => { Hall.getInstance().player.set_combine_num(1); if (Hall.getInstance().firstEnter) { if (this.Container.nodeReferences.length == 4) { this.Container.nodeReferences.forEach(node => { node.getComponent(Cube_Infor).state=Cube_State.live; }); } } }) .start(); } adjustContainer() { const container = this.Container; // 新的 node_isIdiom 状态数组 const newNodeIsIdiom = Array(container.node_isIdiom.length).fill(false); // 新的 idiom_combine 映射表 const newIdiomCombine = new Map(); // 遍历 idiom_combine,重新计算位置 for (const [cube, startIndex] of container.idiom_combine.entries()) { const txtLength = cube.Text.length; // 获取字长 let targetIndex = -1; // 目标位置起始索引 let targetPos = new Vec3(); switch (txtLength) { case 1: { // 找到一个空位 for (let i = 0; i < container.unlock_Num; i++) { if (!newNodeIsIdiom[i]) { targetIndex = i; break; } } if (targetIndex !== -1) { targetPos = container.nodes[targetIndex].getWorldPosition().clone(); newNodeIsIdiom[targetIndex] = true; // 占用位置 } break; } case 2: { // 找到连续两个空位 for (let i = 0; i < container.unlock_Num - 1; i++) { if (!newNodeIsIdiom[i] && !newNodeIsIdiom[i + 1]) { targetIndex = i; break; } } if (targetIndex !== -1) { const pos1 = container.nodes[targetIndex].getWorldPosition(); const pos2 = container.nodes[targetIndex + 1].getWorldPosition(); targetPos.set( (pos1.x + pos2.x) / 2, (pos1.y + pos2.y) / 2, (pos1.z + pos2.z) / 2 ); newNodeIsIdiom[targetIndex] = true; newNodeIsIdiom[targetIndex + 1] = true; } break; } case 3: { // 找到连续三个空位 for (let i = 0; i < container.unlock_Num - 2; i++) { if ( !newNodeIsIdiom[i] && !newNodeIsIdiom[i + 1] && !newNodeIsIdiom[i + 2] ) { targetIndex = i; break; } } if (targetIndex !== -1) { const pos1 = container.nodes[targetIndex].getWorldPosition(); const pos2 = container.nodes[targetIndex + 1].getWorldPosition(); const pos3 = container.nodes[targetIndex + 2].getWorldPosition(); targetPos.set( (pos1.x + pos2.x + pos3.x) / 3, (pos1.y + pos2.y + pos3.y) / 3, (pos1.z + pos2.z + pos3.z) / 3 ); newNodeIsIdiom[targetIndex] = true; newNodeIsIdiom[targetIndex + 1] = true; newNodeIsIdiom[targetIndex + 2] = true; } break; } default: return; } // 移动 Cube_Infor 到目标位置 if (targetIndex !== -1) { tween(cube.node) .to(0.3, { position: new Vec3(targetPos.x, targetPos.y, targetPos.z + 0.4) }) .start(); newIdiomCombine.set(cube, targetIndex); // 更新新映射 } } // 更新 container 状态 container.node_isIdiom = newNodeIsIdiom; container.idiom_combine = newIdiomCombine; console.log("调整后的容器状态:", container.node_isIdiom); console.log("更新后的 idiom_combine:", [...container.idiom_combine.entries()]); } async UnLock(node: Node) { const res = await chsdk.playRewardAd('解锁槽位'); if (res) { for (let i = 0; i < 2; i++) { if (this.Container.Lock_node[i].active) { this.Container.Lock_node[i].active = false; this.Container.unlock_Num += 1; gui.get(UI_Main).evt.emit(gui.get(UI_Main).evt.key.resume); return; } } ch.audio.resume(); } } playAnim() { const skeleton = this.Container.skeleton1.getComponent(sp.Skeleton); // 播放 anim_2, anim_3, anim_4,同时使用不同的轨道 skeleton.setAnimation(0, 'anim_2', false); skeleton.setAnimation(1, 'anim_3', false); skeleton.setAnimation(2, 'anim_4', false); // 监听所有轨道完成,最后播放 anim_1 let completedTracks = new Set(); skeleton.setCompleteListener((trackEntry: sp.spine.TrackEntry) => { completedTracks.add(trackEntry.trackIndex); // 将完成的轨道加入记录 console.log(`轨道 ${trackEntry.trackIndex} 的动画完成`); // 如果轨道 0、1、2 都完成,则播放 anim_1 if (completedTracks.has(0) && completedTracks.has(1) && completedTracks.has(2)) { console.log('所有动画完成,播放 anim_1'); // 播放 anim_1 并监听其完成事件 skeleton.setAnimation(0, 'anim_1', false); // 非循环播放 anim_1 skeleton.setCompleteListener((trackEntry: sp.spine.TrackEntry) => { if (trackEntry.animation.name === 'anim_1') { console.log('anim_1 播放完成'); skeleton.setCompleteListener(null); // 移除监听器,防止重复触发 } }); } }); } }