消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
6.8 KiB

1 week ago
import { _decorator, CCString, Color, Component, Graphics, Node, UITransform, v3 } from 'cc';
import { MapGrid } from './MapGrid';
const { ccclass, property, executeInEditMode } = _decorator;
@ccclass('MapGridStatusDebug')
@executeInEditMode
export class MapGridStatusDebug extends Component {
@property({ type: [Color], displayName: '状态颜色数组(按状态字段顺序)' })
colorArr: Color[] = [
new Color(0x20, 0xEC, 0x1B, 0xFF), // 20EC1B
new Color(0xFF, 0xFF, 0xFF, 0xFF), // FFFFFF
new Color(0xF3, 0x41, 0x29, 0xC8), // F34129C8
new Color(0x2B, 0xC8, 0xFC, 0xC8), // 2BC8FCC8
new Color(0xFF, 0x99, 0x00, 0xC8), // FF9900C8
new Color(0xF3, 0xD4, 0x58, 0xC8), // F3D458C8
new Color(0x00, 0x00, 0x00, 0xFF), // 000000
];
@property({ type: [CCString], displayName: '状态字段数组(可扩展)' })
statusKeys: string[] = [
'isCanWalk',
'isCanPutObstacle',
'isCanRemove',
'isCanBeThunder',
'isBestPlace',
'isFreePlace',
'isBroken',
];
@property({ displayName: '单个色块尺寸' })
blockSize: number = 8;
@property({ displayName: '色块间距' })
blockGap: number = 1;
@property({ displayName: '格子内边距X' })
insetX: number = 2;
@property({ displayName: '格子内边距Y' })
insetY: number = 2;
private _graphics: Graphics = null;
private _selfTf: UITransform = null;
private _gridRoot: Node = null;
private _grids: MapGrid[] = [];
private _gridTfs: UITransform[] = [];
private _lastMasks: number[] = [];
private _lastWorldX: number[] = [];
private _lastWorldY: number[] = [];
private _lastGridW: number[] = [];
private _lastGridH: number[] = [];
private _lastConfigSignature = '';
private _tmpWorld = v3();
private _tmpLocal = v3();
onLoad(): void {
this._graphics = this.node.getComponent(Graphics);
this._selfTf = this.node.getComponent(UITransform);
if (!this._graphics) {
this._graphics = this.node.addComponent(Graphics);
}
if (!this._selfTf) {
this._selfTf = this.node.addComponent(UITransform);
}
this.rebuildGridCache();
this.updateStatus(true);
}
protected update(dt: number): void {
this.updateStatus();
}
private rebuildGridCache() {
this._gridRoot = this.node.parent?.getChildByName("格子") || null;
this._grids = this._gridRoot ? this._gridRoot.getComponentsInChildren(MapGrid) : [];
this._gridTfs = new Array(this._grids.length);
this._lastMasks = new Array(this._grids.length);
this._lastWorldX = new Array(this._grids.length);
this._lastWorldY = new Array(this._grids.length);
this._lastGridW = new Array(this._grids.length);
this._lastGridH = new Array(this._grids.length);
for (let i = 0; i < this._grids.length; i++) {
this._gridTfs[i] = this._grids[i]?.node?.getComponent(UITransform) || null;
this._lastMasks[i] = -1;
this._lastWorldX[i] = NaN;
this._lastWorldY[i] = NaN;
this._lastGridW[i] = NaN;
this._lastGridH[i] = NaN;
}
}
private getConfigSignature() {
return `${this.statusKeys.join('|')}|${this.blockSize}|${this.blockGap}|${this.insetX}|${this.insetY}|${this.colorArr.length}`;
}
private getGridMask(grid: MapGrid) {
let mask = 0;
const maxBits = Math.min(31, this.statusKeys.length);
for (let i = 0; i < maxBits; i++) {
if ((grid as any)[this.statusKeys[i]]) {
mask |= (1 << i);
}
}
return mask;
}
updateStatus(force = false) {
if (!this._graphics || !this._selfTf) return;
if (!this._gridRoot || !this._gridRoot.isValid) {
this.rebuildGridCache();
}
if (this._grids.length <= 0 || this.statusKeys.length <= 0) {
this._graphics.clear();
return;
}
const configSignature = this.getConfigSignature();
let dirty = force || (configSignature !== this._lastConfigSignature);
const count = this.statusKeys.length;
const blockW = this.blockSize;
const blockH = this.blockSize;
const step = blockW + this.blockGap;
for (let i = 0; i < this._grids.length; i++) {
const grid = this._grids[i];
if (!grid || !grid.node?.isValid) continue;
const gridTf = this._gridTfs[i];
if (!gridTf) continue;
const world = grid.node.worldPosition;
const mask = this.getGridMask(grid);
const gridW = gridTf.width;
const gridH = gridTf.height;
if (mask !== this._lastMasks[i]
|| world.x !== this._lastWorldX[i]
|| world.y !== this._lastWorldY[i]
|| gridW !== this._lastGridW[i]
|| gridH !== this._lastGridH[i]) {
dirty = true;
}
this._lastMasks[i] = mask;
this._lastWorldX[i] = world.x;
this._lastWorldY[i] = world.y;
this._lastGridW[i] = gridW;
this._lastGridH[i] = gridH;
}
if (!dirty) return;
this._graphics.clear();
this._lastConfigSignature = configSignature;
for (let i = 0; i < this._grids.length; i++) {
const grid = this._grids[i];
if (!grid || !grid.node?.isValid) continue;
const gridTf = this._gridTfs[i];
if (!gridTf) continue;
const world = grid.node.worldPosition;
this._tmpWorld.set(world.x, world.y, world.z);
this._selfTf.convertToNodeSpaceAR(this._tmpWorld, this._tmpLocal);
const gridW = gridTf.width;
const gridH = gridTf.height;
const startX = this._tmpLocal.x - gridW / 2 + this.insetX;
const startY = this._tmpLocal.y + gridH / 2 - this.insetY - blockH;
const contentW = Math.max(1, gridW - this.insetX * 2);
const maxCols = Math.max(1, Math.floor((contentW + this.blockGap) / step));
const mask = this._lastMasks[i];
for (let j = 0; j < count; j++) {
const value = j < 31 ? ((mask & (1 << j)) !== 0) : !!(grid as any)[this.statusKeys[j]];
if (!value) continue;
const color = this.colorArr[j] || this.colorArr[this.colorArr.length - 1] || Color.WHITE;
const col = j % maxCols;
const row = Math.floor(j / maxCols);
const x = startX + col * step;
const y = startY - row * step;
this._graphics.fillColor = color;
this._graphics.rect(x, y, blockW, blockH);
this._graphics.fill();
}
}
}
}