| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- import { _decorator, Component, Event, EventTouch, Node, SpriteFrame, UITransform, v3, Sprite, director, Color, tween, Prefab, instantiate, Vec3, Animation, Quat, sp, SkeletalAnimation, Skeleton, assetManager } from 'cc';
- import { BlockLink } from './BlockLink';
- import { LvData, LvDir } from '../../LvData/LvData';
- import { blockMap } from './blockMap';
- import { gui } from 'db://assets/core/ui/ui';
- import ch_audio from 'db://assets/ch/audio/audio';
- import { audioManager } from '../../../Audio/AudioManager';
- const { ccclass, property } = _decorator;
- export enum BlockState {
- Normal = "normal", // 默认
- Selected = "selected", // 被选中
- Matched = "matched", // 已匹配
- Unmatched = "unmatched" // 未匹配
- }
- @ccclass('Block')
- export class Block extends Component {
- @property(Node)
- icon: Node | null = null;
- @property(Prefab)
- BOOM: Prefab | null = null;
- @property(Node)
- XZ: Node;
- @property(Node)
- BoomNode: Node
- @property(Sprite)
- BlockSprite: Sprite;
- @property(sp.Skeleton)
- biu: sp.Skeleton;
- //当前方块状态
- public stateBlock: BlockState = BlockState.Normal;
-
-
- //第一次匹配的方块con
- private static firstSelectedBlock: Block | null = null;
- private isSpecialEffectHandled: boolean = false;
- mapConfig: blockMap = new blockMap();
- private blockLink: BlockLink | null = null;
- start() {
- this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
- this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
- this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
- this.biu = this.node.getChildByName('biu').getComponent(sp.Skeleton)
- this.biu.node.active = false;
- this.blockLink = this.node.parent.getComponent(BlockLink);
- }
- onTouchStart(event: EventTouch) {
- console.log('touch start');
- if (this.blockLink.isMatching || this.blockLink.isMoving) return; // 正在匹配时拒绝新点击||正在移动时拒绝新点击
- audioManager.playOneShot('sound/onblcok');
- gui.scale_anim(this.node, 0.2, 0.3, 1);
- gui.scale_shake_anim(this.node, 0.2, 0.3, 2, 1);
- this.blockLink.sleeBlock = this.node;
- // 允许在选中状态下再次点击自身
- if (this.stateBlock === BlockState.Selected && Block.firstSelectedBlock === this) {
- // 点击已选中的自身方块,取消选中
- this.stateBlock = BlockState.Normal;
- this.applyStateStyle();
- Block.firstSelectedBlock = null;
- this.blockLink.sleeBlock = null;
- return;
- }
- if (this.stateBlock
- !== BlockState.Normal) return;
- this.stateBlock
- = BlockState.Selected;
- this.applyStateStyle();
- if (Block.firstSelectedBlock) {
- this.attemptMatch(Block.firstSelectedBlock);
- } else {
- Block.firstSelectedBlock = this;
- }
- }
- onTouchEnd(event: EventTouch) {
- }
- onTouchCancel(event: EventTouch) {
- // 触摸取消,恢复状态
- if (this.stateBlock
- === BlockState.Selected) {
- this.stateBlock
- = BlockState.Normal;
- this.applyStateStyle();
- Block.firstSelectedBlock = null;
- // this.blockLink.isMatching=false
- }
- }
- attemptMatch(otherBlock: Block) {
- if (!this.isMatch(otherBlock)) {
- this.handleMatchFailed(otherBlock);
- return;
- }
- // 获取BlockLink组件
- const comp = this.node.parent.getComponent(BlockLink);
- if (!comp) {
- console.error('BlockLink component not found!');
- this.handleMatchFailed(otherBlock);
- return;
- }
- // 判断连接成功or失败
- if (comp.isConnected(this.node, otherBlock.node)) {
- this.handleMatchSuccess(otherBlock);
- } else {
- this.handleMatchFailed(otherBlock);
- }
- }
- //连接失败
- handleMatchFailed(otherBlock: Block) {
- // this.blockLink.isMatching = true;
- Block.firstSelectedBlock = this;
- this.stateBlock
- = BlockState.Selected;
- otherBlock.stateBlock
- = BlockState.Unmatched;
- // 0.5秒后恢复普通状态
- // this.scheduleOnce(() => {
- if (otherBlock.node && otherBlock.node.isValid) { // 检查 otherBlock.node 是否存在
- otherBlock.stateBlock
- = BlockState.Normal;
- otherBlock.applyStateStyle();
- }
- // }, 0.5);
- }
- onDestroy() {
- this.unscheduleAllCallbacks(); // 取消所有定时器
- }
- //连接成功
- handleMatchSuccess(otherBlock: Block) {
- // 检查两个方块是否都是特殊方块
- const isSpecialBlock = this.isSpecialBlock() && otherBlock.isSpecialBlock();
- if (isSpecialBlock) {
- // 执行特殊效果
- this.handleSpecialEffect(otherBlock);
- Block.firstSelectedBlock = null;
- this.blockLink.isMatching = false;
- return;
- }
- // 非特殊方块的匹配逻辑保持不变
- this.blockLink.isMatching = true;
- this.stateBlock = BlockState.Matched;
- otherBlock.stateBlock = BlockState.Matched;
- this.applyStateStyle();
- // 更新游戏数据
- const comp = this.node.parent.getComponent(BlockLink);
- const pos1 = comp.getRowCol(this.node.position);
- const pos2 = comp.getRowCol(otherBlock.node.position);
- BlockLink.blockArry[pos1.i][pos1.j] = null;
- BlockLink.blockArry[pos2.i][pos2.j] = null;
- // 延迟移除节点,等待动画完成
- this.scheduleOnce(() => {
- audioManager.playOneShot('sound/offblock');
- this.node.destroy();
- otherBlock.node.destroy();
- Block.firstSelectedBlock = null;
- this.blockLink.isMatching = false;
- const dir = LvData.instance.dir;
- if (dir !== LvDir.none) {
- comp.moveByLevelDirection(dir);
- }
- // 动画完成后重新开启全局触摸
- // Block.setGlobalTouchEnabled(true);
- }, 0.5);
- BlockLink.remainingBlocks--;
- }
- //选中方块效果
- applyStateStyle() {
- if (!this.biu || !this.biu.node) return;
- const spriteComp = this.node?.getComponent(Sprite);
- const biuanim = this.node.getChildByName('biu').getComponent(sp.Skeleton)
- if (!spriteComp) return;
- switch (this.stateBlock
- ) {
- case BlockState.Selected:
- this.biu.node.active = true;
- biuanim.setAnimation(0, "animation", true);
- // 选中效果
- // spriteComp.color = new Color(255, 255, 0); // 黄色高亮
- break;
- case BlockState.Unmatched:
- // 不匹配效果
- this.biu.node.active = false;
- biuanim.setAnimation(0, "animation", false);
- // spriteComp.color = new Color(255, 0, 0)
- break;
- case BlockState.Normal:
- if (this.biu && this.biu.node) {
- this.biu.node.active = false;
- biuanim.setAnimation(0, "animation", false);
- }
- break;
- }
- }
- //图片对比
- isMatch(otherBlock: Block): boolean {
- // 防御性检查
- if (!this.icon || !otherBlock.icon) return false;
- const currentSprite = this.icon.getComponent(Sprite);
- const otherSprite = otherBlock.icon.getComponent(Sprite);
- if (!currentSprite || !otherSprite) return false;
- return currentSprite.spriteFrame === otherSprite.spriteFrame;
- }
- // 重置方块状态
- public resetState() {
- this.stateBlock
- = BlockState.Normal;
- this.applyStateStyle();
- }
- // 判断特殊方块
- private isSpecialBlock(): boolean {
- if (!this.icon) return false;
- const spriteComp = this.icon.getComponent(Sprite);
- if (!spriteComp || !spriteComp.spriteFrame) return false;
- return spriteComp.spriteFrame.name.includes("zd");
- }
- //高亮显示
- highlight(node1: Node, node2: Node) {
- if (!node1 || !node1.isValid) return;
- node1.getComponent(Sprite).color = new Color(0, 255, 0);
- node2.getComponent(Sprite).color = new Color(0, 255, 0);
- }
- // 点击炸弹处理
- private handleSpecialEffect(otherBlock: Block) {
- if (!this.node.isValid) return;
- if (this.isSpecialEffectHandled) {
- return; // 如果已经处理过,则直接返回
- }
- this.isSpecialEffectHandled = true; // 标记为已处理
- const comp = this.node.parent.getComponent(BlockLink);
- if (!comp) return;
- console.log("处理炸弹特殊效果");
- this.handleBombEffect(otherBlock);
- // 延迟执行移动操作,确保当前动画完成
- this.scheduleOnce(() => {
- const dir = LvData.instance.dir;
- if (dir !== LvDir.none) {
- comp.moveByLevelDirection(dir);
- }
- }, 0.2); // 稍长的延迟确保所有动画完成
- }
- private findMatchPair(): { blockA: Block, blockB: Block } | null {
- const comp = this.node.parent.getComponent(BlockLink);
- if (!comp) return null;
- // 收集所有普通方块
- const normalBlocks: Block[] = [];
- BlockLink.blockArry.forEach(row => {
- row.forEach(blockNode => {
- if (blockNode && blockNode.isValid) {
- const block = blockNode.getComponent(Block);
- if (block && !block.isSpecialBlock()) {
- normalBlocks.push(block);
- }
- }
- });
- });
- // 寻找一对匹配的方块
- for (let i = 0; i < normalBlocks.length; i++) {
- for (let j = i + 1; j < normalBlocks.length; j++) {
- if (normalBlocks[i].isMatch(normalBlocks[j])) {
- return {
- blockA: normalBlocks[i],
- blockB: normalBlocks[j]
- };
- }
- }
- }
- return null;
- }
- // 消除炸弹方块
- private eliminateBombBlock(block: Block) {
- const comp = block.node.parent.getComponent(BlockLink);
- if (!comp) return;
- // 更新游戏数据
- const pos = comp.getRowCol(block.node.position);
- BlockLink.blockArry[pos.i][pos.j] = null;
- BlockLink.remainingBlocks--;
- // 方块消失动画
- tween(block.node)
- .to(0.2, { scale: v3(1.5, 1.5, 1) })
- .to(0.2, { scale: v3(0, 0, 1) })
- .call(() => {
- if (block.node && block.node.isValid) {
- block.node.destroy();
- }
- })
- .start();
- }
- // // 消除匹配的方块对
- private eliminateMatchPair(blockA: Block, blockB: Block) {
- debugger
- const comp = blockA.node.parent.getComponent(BlockLink);
- if (!comp) return;
- blockA.node.getChildByName("xuanz").active = true;
- blockB.node.getChildByName("xuanz").active = true;
- this.scheduleOnce(() => {
- // 更新游戏数据
- const posA = comp.getRowCol(blockA.node.position);
- const posB = comp.getRowCol(blockB.node.position);
- BlockLink.blockArry[posA.i][posA.j] = null;
- BlockLink.blockArry[posB.i][posB.j] = null;
- BlockLink.remainingBlocks -= 2;
- // 播放消除动画
- this.animateBlockDisappearance(blockA);
- this.animateBlockDisappearance(blockB);
- }, 0.2);
- if (comp) {
- const dir = LvData.instance.dir;
- if (dir !== LvDir.none) {
- comp.moveByLevelDirection(dir);
- }
- }
- }
- // 方块消失动画
- private animateBlockDisappearance(block: Block) {
- tween(block.node)
- .to(0.4, { scale: v3(1.2, 1.2, 1) })
- .to(0.4, { scale: v3(0, 0, 1) })
- .call(() => {
- if (block.node && block.node.isValid) {
- block.node.destroy();
- }
- })
- .start();
- }
- // 随机消除一对普通方块
- private eliminateRandomPair() {
- debugger
- const comp = this.node.parent.getComponent(BlockLink);
- if (!comp) return;
- // 收集所有普通方块
- const normalBlocks: Block[] = [];
- BlockLink.blockArry.forEach(row => {
- row.forEach(blockNode => {
- if (blockNode && blockNode.isValid) {
- const block = blockNode.getComponent(Block);
- if (block && !block.isSpecialBlock()) {
- normalBlocks.push(block);
- }
- }
- });
- });
- // 随机选择两个方块消除
- if (normalBlocks.length >= 2) {
- const index1 = Math.floor(Math.random() * normalBlocks.length);
- let index2;
- do {
- index2 = Math.floor(Math.random() * normalBlocks.length);
- } while (index2 === index1);
- this.eliminateMatchPair(normalBlocks[index1], normalBlocks[index2]);
- }
- // if (comp) {
- // const dir = LvData.instance.dir;
- // if (dir !== LvDir.none) {
- // comp.moveByLevelDirection(dir); // 或者调用 refreshLayout()
- // }
- // }
- }
- private handleBombEffect(otherBlock: Block) {
- const comp = this.node.parent.getComponent(BlockLink);
- if (!comp) return;
- let boomA = instantiate(this.BOOM);
- boomA.setPosition(this.node.position);
- this.node.parent.addChild(boomA);
- boomA.setSiblingIndex(999)
- const boomB = instantiate(this.BOOM);
- boomB.setPosition(otherBlock.node.position);
- this.node.parent.addChild(boomB);
- boomA.setSiblingIndex(998)
- const initialHeight = 100; // 向上飞行高度
- // 1. 消除两个炸弹方块
- this.eliminateBombBlock(this);
- this.eliminateBombBlock(otherBlock);
- // 2. 寻找并消除一对匹配的普通方块
- const matchPair = this.findMatchPair();
- if (matchPair) {
- matchPair.blockA.node.getChildByName("xuanz").active = true
- matchPair.blockB.node.getChildByName("xuanz").active = true
- const comp = this.node.parent.getComponent(BlockLink);
- const worldPosA = matchPair.blockA.node.getWorldPosition();
- const worldPosB = matchPair.blockB.node.getWorldPosition();
- // 2. 延迟方块消除(直到炸弹到达)
- this.scheduleOnce(() => {
- this.eliminateMatchPair(matchPair.blockA, matchPair.blockB);
- }, 0.8); // 等待飞行动画完成
- // 3. 使用正确的位置
- this.flyBoomInitialUp(boomA, initialHeight, () => {
- this.flyBoomToTarget(boomA, worldPosA, () => {
- this.playBoomAnimation(boomA); // 播放动画
- });
- });
- this.flyBoomInitialUp(boomB, initialHeight, () => {
- this.flyBoomToTarget(boomB, worldPosB, () => {
- this.playBoomAnimation(boomB); // 播放动画
- });
- });
- this.eliminateMatchPair(matchPair.blockA, matchPair.blockB);
- }
- else {
- // 如果没有找到匹配对,随机选择两个方块消除
- this.eliminateRandomPair();
- }
- // 3. 延迟执行移动操作
- this.scheduleOnce(() => {
- const dir = LvData.instance.dir;
- if (dir !== LvDir.none) {
- comp.moveByLevelDirection(dir);
- }
- }, 0.8); // 稍长的延迟确保所有动画完成
- }
- private playBoomAnimation(boom: Node) {
- audioManager.playOneShot("sound/zdBong")
- const boomChild = boom.getChildByName("boom");
- if (!boomChild) return;
- // 1. 启用动画组件
- boomChild.active = true;
- this.schedule(() => {
- boom.destroy();
- }, 0.3)
- }
- // 初始向上飞行动画
- private flyBoomInitialUp(boom: Node, height: number, onComplete: Function) {
- audioManager.playOneShot("sound/zdfle1")
- // 计算目标位置(当前位置上方)
- const currentPos = boom.position.clone();
- const targetPos = new Vec3(currentPos.x, currentPos.y + height, currentPos.z);
- // 向上飞行动画
- tween(boom)
- .to(0.2, {
- position: targetPos,
- scale: v3(1.2, 1.2, 1), // 飞行过程中变大
- }, {
- easing: "sineOut",
- onComplete: () => onComplete()
- })
- .start();
- }
- private flyBoomToTarget(boom: Node, worldPos: Vec3, onComplete: Function) {
- // 转换世界坐标到本地坐标
- const localPos = this.node.parent.inverseTransformPoint(new Vec3(), worldPos);
- tween(boom)
- .to(0.4, {
- position: localPos,
- scale: v3(1.5, 1.5, 1) // 添加缩放效果
- }, { easing: "quadOut" })
- .call(() => {
- onComplete();
- })
- .start();
- }
- }
|