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(); } } } }