CC 3.8.8 中 UI 渲染管线要求 Graphics 节点自身必须有 UITransform 组件, 否则不会提交渲染。所有 Graphics 节点(方块/棋盘/UI)均已补充。
124 lines
3.0 KiB
TypeScript
124 lines
3.0 KiB
TypeScript
/**
|
|
* 方块组件 — Graphics 绘制色块,不依赖外部纹理
|
|
*/
|
|
import { _decorator, Component, Node, Graphics, 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 _gfx!: Graphics;
|
|
private _label!: Label;
|
|
private _selected = false;
|
|
|
|
onLoad() {
|
|
this.node.addComponent(UITransform).setContentSize(CELL, CELL);
|
|
|
|
// 用 Graphics 画圆角矩形色块
|
|
const g = new Node('gfx');
|
|
this._gfx = g.addComponent(Graphics);
|
|
g.addComponent(UITransform).setContentSize(CELL, CELL);
|
|
this.node.addChild(g);
|
|
|
|
// 等级文字
|
|
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, CELL);
|
|
this.node.addChild(lb);
|
|
|
|
this.draw();
|
|
}
|
|
|
|
/** 初始化 */
|
|
init(row: number, col: number, level: number) {
|
|
this.row = row;
|
|
this.col = col;
|
|
this.level = level;
|
|
this.isMerging = false;
|
|
this._selected = false;
|
|
this.draw();
|
|
this.playSpawnAnim();
|
|
}
|
|
|
|
/** 合并升级 */
|
|
upgradeTo(lv: number) {
|
|
this.level = lv;
|
|
this.isMerging = false;
|
|
this.draw();
|
|
this.playMergeAnim();
|
|
}
|
|
|
|
/** 高亮/取消高亮 */
|
|
setHighlight(on: boolean) {
|
|
this._selected = on;
|
|
this.draw();
|
|
}
|
|
|
|
private draw() {
|
|
const c = conf(this.level).color;
|
|
const col = this._hexToColor(c);
|
|
|
|
this._gfx.clear();
|
|
const s = CELL - 4;
|
|
const h = s / 2;
|
|
const r = 6;
|
|
|
|
// 填充
|
|
this._gfx.fillColor = col;
|
|
this._gfx.roundRect(-h, -h, s, s, r);
|
|
this._gfx.fill();
|
|
|
|
// 选中高亮边框
|
|
if (this._selected) {
|
|
this._gfx.strokeColor = Color.WHITE;
|
|
this._gfx.lineWidth = 3;
|
|
this._gfx.roundRect(-h - 1, -h - 1, s + 2, s + 2, r + 1);
|
|
this._gfx.stroke();
|
|
}
|
|
|
|
this._gfx.fillColor = new Color(255, 255, 255, 50);
|
|
this._gfx.roundRect(-h + 4, -h + 4, s - 8, s / 3, 4);
|
|
this._gfx.fill();
|
|
|
|
this._label.string = `${this.level}`;
|
|
this._label.fontSize = 24 + Math.min(this.level * 2, 10);
|
|
}
|
|
|
|
// ---- 动画 ----
|
|
|
|
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 _hexToColor(hex: string): Color {
|
|
return new Color(
|
|
parseInt(hex.slice(1, 3), 16),
|
|
parseInt(hex.slice(3, 5), 16),
|
|
parseInt(hex.slice(5, 7), 16),
|
|
255,
|
|
);
|
|
}
|
|
}
|