Files
cocos-hehe/assets/Scripts/Block.ts
leiyuwei e9e6ddb8a7 重构: 合成大作战改为拖拽合成机制
- 全部代码重写,改为拖拽合成(drag-and-drop merge)
- 拖拽方块到相邻同等级方块上完成合成
- 拖到空格移动,拖到无效位置弹回
- Cocos Creator 3.8.8 全面适配
- 移除 GameManager.ts → 改为 GameController.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 12:12:39 +08:00

117 lines
3.2 KiB
TypeScript

/**
* 方块组件 — 视觉、动画、拖拽状态
*/
import { _decorator, Component, Node, Sprite, Color, UITransform, Label, tween, v3 } from 'cc';
import { CELL, conf } from './BlockConfig';
const { ccclass } = _decorator;
@ccclass('Block')
export class Block extends Component {
// ---- 网格位置 ----
public row = -1;
public col = -1;
public level = 1;
public isMerging = false;
// ---- 内部 ----
private _sprite!: Sprite;
private _label!: Label;
// ============================================================
// 生命周期
// ============================================================
onLoad() {
this.node.addComponent(UITransform).setContentSize(CELL, CELL);
// 方块底色
const bg = new Node('bg');
this._sprite = bg.addComponent(Sprite);
this._sprite.type = Sprite.Type.SIMPLE;
bg.addComponent(UITransform).setContentSize(CELL - 4, CELL - 4);
this.node.addChild(bg);
// 等级文字
const lb = new Node('lb');
this._label = lb.addComponent(Label);
this._label.fontSize = 24;
this._label.lineHeight = 28;
this._label.color = Color.WHITE.clone();
lb.addComponent(UITransform).setContentSize(CELL - 4, CELL - 4);
this.node.addChild(lb);
this.applyVisuals();
}
// ============================================================
// 公开方法
// ============================================================
/** 设置网格位置和等级 */
init(row: number, col: number, level: number) {
this.row = row;
this.col = col;
this.level = level;
this.isMerging = false;
this.applyVisuals();
this.playSpawnAnim();
}
/** 合并升级 */
upgradeTo(lv: number) {
this.level = lv;
this.isMerging = false;
this.applyVisuals();
this.playMergeAnim();
}
/** 高亮(拖拽选中) */
setHighlight(on: boolean) {
const c = this._color();
if (on) c.a = 160;
this._sprite.color = c;
}
// ============================================================
// 动画
// ============================================================
private playSpawnAnim() {
this.node.setScale(v3(0, 0, 1));
tween(this.node).to(0.2, { scale: v3(1, 1, 1) }, { easing: 'backOut' }).start();
}
private playMergeAnim() {
this.node.setScale(v3(1.4, 1.4, 1));
tween(this.node).to(0.35, { scale: v3(1, 1, 1) }, { easing: 'elasticOut' }).start();
}
/** 弹回动画(拖拽到无效位置) */
playSnapBack(from: { x: number; y: number }, to: { x: number; y: number }) {
this.node.setPosition(from.x, from.y, 0);
tween(this.node).to(0.18, { position: v3(to.x, to.y, 0) }, { easing: 'sineOut' }).start();
}
// ============================================================
// 内部
// ============================================================
private applyVisuals() {
const c = this._color();
this._sprite.color = c;
this._label.string = `${this.level}`;
this._label.fontSize = 24 + Math.min(this.level * 2, 10);
}
private _color(): Color {
const hex = conf(this.level).color;
return new Color(
parseInt(hex.slice(1, 3), 16),
parseInt(hex.slice(3, 5), 16),
parseInt(hex.slice(5, 7), 16),
255,
);
}
}