- 全部代码重写,改为拖拽合成(drag-and-drop merge) - 拖拽方块到相邻同等级方块上完成合成 - 拖到空格移动,拖到无效位置弹回 - Cocos Creator 3.8.8 全面适配 - 移除 GameManager.ts → 改为 GameController.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
117 lines
3.2 KiB
TypeScript
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,
|
|
);
|
|
}
|
|
}
|