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.
 
 
 
 
AdLike2048/assets/script/ui/BattleGame/MapGridStatusDebug.ts

368 lines
14 KiB

import { _decorator, CCString, Color, Component, Graphics, Label, 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), // isCanWalk 通行
new Color(0xFF, 0xFF, 0xFF, 0xFF), // isCanPutText 放文字
new Color(0x9B, 0x59, 0xB6, 0xC8), // isCanPutGrid 摆格子
new Color(0x8B, 0x5A, 0x2B, 0xC8), // isCanBeDig 可开挖
new Color(0xF3, 0x41, 0x29, 0xC8), // isCanRemove 可铲除
new Color(0x2B, 0xC8, 0xFC, 0xC8), // isCanBeThunder 可雷击
new Color(0xFF, 0x99, 0x00, 0xC8), // isBestPlace 最佳点
new Color(0xF3, 0xD4, 0x58, 0xC8), // isFreePlace 免费推荐点
];
/** 与 MapGrid 可配置布尔字段一一对应(不含 CampAreaID,阵营走铺色/数字) */
@property({ type: [CCString], displayName: '状态字段数组(可扩展)' })
statusKeys: string[] = [
'isCanWalk',
'isCanPutText',
'isCanPutGrid',
'isCanBeDig',
'isCanRemove',
'isCanBeThunder',
'isBestPlace',
'isFreePlace',
];
@property({ displayName: '显示阵营区域' })
showCampArea = true;
@property({ displayName: '显示阵营数字' })
showCampIdLabel = true;
/** 下标 = CampAreaID;0 预留;1 己方蓝 / 2 敌方红,其余可扩展 */
@property({ type: [Color], displayName: '阵营颜色(下标=阵营ID)' })
campAreaColors: Color[] = [
new Color(0, 0, 0, 0),
new Color(0x2B, 0x8C, 0xFF, 0x55), // 1 己方
new Color(0xFF, 0x4D, 0x4D, 0x55), // 2 敌方
new Color(0xA8, 0x55, 0xF7, 0x55), // 3
new Color(0x22, 0xC5, 0x5E, 0x55), // 4
new Color(0xFB, 0x92, 0x3C, 0x55), // 5
new Color(0xEC, 0x48, 0x99, 0x55), // 6
new Color(0x14, 0xB8, 0xA6, 0x55), // 7
new Color(0xEA, 0xB3, 0x08, 0x55), // 8
];
@property({ displayName: '阵营描边宽度' })
campStrokeWidth = 2;
@property({ displayName: '阵营数字字号' })
campIdFontSize = 22;
@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 _lastCampIds: number[] = [];
private _lastWorldX: number[] = [];
private _lastWorldY: number[] = [];
private _lastGridW: number[] = [];
private _lastGridH: number[] = [];
private _lastConfigSignature = '';
private _tmpWorld = v3();
private _tmpLocal = v3();
private _campLabelRoot: Node = null;
private _campLabels: Label[] = [];
private _strokeColor = new Color();
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.ensureCampLabelRoot();
this.rebuildGridCache();
this.updateStatus(true);
}
protected onDisable(): void {
this.hideCampLabels();
if (this._graphics) {
this._graphics.clear();
}
}
protected update(dt: number): void {
this.updateStatus();
}
private ensureCampLabelRoot() {
this._campLabelRoot = this.node.getChildByName('CampIdLabels');
if (!this._campLabelRoot) {
this._campLabelRoot = new Node('CampIdLabels');
this._campLabelRoot.parent = this.node;
}
}
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._lastCampIds = 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._lastCampIds[i] = Number.NaN;
this._lastWorldX[i] = NaN;
this._lastWorldY[i] = NaN;
this._lastGridW[i] = NaN;
this._lastGridH[i] = NaN;
}
this.rebuildCampLabels();
}
private rebuildCampLabels() {
this.ensureCampLabelRoot();
while (this._campLabels.length < this._grids.length) {
const n = new Node(`CampId_${this._campLabels.length}`);
n.parent = this._campLabelRoot;
n.addComponent(UITransform);
const label = n.addComponent(Label);
label.overflow = Label.Overflow.NONE;
label.horizontalAlign = Label.HorizontalAlign.CENTER;
label.verticalAlign = Label.VerticalAlign.CENTER;
label.isBold = true;
label.enableOutline = true;
label.outlineColor = Color.BLACK;
label.outlineWidth = 2;
label.color = Color.WHITE;
this._campLabels.push(label);
}
for (let i = 0; i < this._campLabels.length; i++) {
const label = this._campLabels[i];
if (!label?.node?.isValid) continue;
label.node.active = i < this._grids.length && this.showCampArea && this.showCampIdLabel;
}
}
private hideCampLabels() {
for (let i = 0; i < this._campLabels.length; i++) {
const label = this._campLabels[i];
if (label?.node?.isValid) {
label.node.active = false;
}
}
}
private getConfigSignature() {
return [
this.statusKeys.join('|'),
this.blockSize,
this.blockGap,
this.insetX,
this.insetY,
this.colorArr.length,
this.showCampArea ? 1 : 0,
this.showCampIdLabel ? 1 : 0,
this.campStrokeWidth,
this.campIdFontSize,
this.campAreaColors.length,
].join('|');
}
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;
}
private getCampColor(campId: number): Color {
const colors = this.campAreaColors;
if (!colors || colors.length <= 0) {
return Color.MAGENTA;
}
if (campId >= 0 && campId < colors.length && colors[campId]) {
return colors[campId];
}
// 超出配置时在 1..N-1 循环,避免落回透明的 0 号
const usable = Math.max(1, colors.length - 1);
const idx = ((Math.abs(campId) - 1) % usable) + 1;
return colors[idx] || Color.MAGENTA;
}
private ensureReady() {
if (!this._graphics) {
this._graphics = this.node.getComponent(Graphics) || this.node.addComponent(Graphics);
}
if (!this._selfTf) {
this._selfTf = this.node.getComponent(UITransform) || this.node.addComponent(UITransform);
}
this.ensureCampLabelRoot();
}
updateStatus(force = false) {
this.ensureReady();
if (!this._graphics || !this._selfTf) return;
if (!this._gridRoot || !this._gridRoot.isValid || this._grids.length <= 0) {
this.rebuildGridCache();
}
if (this._grids.length <= 0) {
this._graphics.clear();
this.hideCampLabels();
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 campId = grid.CampAreaID;
const gridW = gridTf.width;
const gridH = gridTf.height;
if (mask !== this._lastMasks[i]
|| campId !== this._lastCampIds[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._lastCampIds[i] = campId;
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;
if (this._campLabels.length < this._grids.length) {
this.rebuildCampLabels();
}
// 1) 阵营整格铺色 + 描边:连成区域,策划一眼能看出分界
if (this.showCampArea) {
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 x = this._tmpLocal.x - gridW / 2;
const y = this._tmpLocal.y - gridH / 2;
const campId = this._lastCampIds[i];
const fill = this.getCampColor(campId);
this._graphics.fillColor = fill;
this._graphics.rect(x, y, gridW, gridH);
this._graphics.fill();
if (this.campStrokeWidth > 0) {
this._strokeColor.set(fill.r, fill.g, fill.b, Math.min(255, fill.a + 100));
this._graphics.strokeColor = this._strokeColor;
this._graphics.lineWidth = this.campStrokeWidth;
this._graphics.rect(x, y, gridW, gridH);
this._graphics.stroke();
}
}
}
// 2) 状态小色块(叠在阵营铺色之上)
if (count > 0) {
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();
}
}
}
// 3) 中央阵营数字:同色区域里仍能精确读出 ID
const showLabel = this.showCampArea && this.showCampIdLabel;
for (let i = 0; i < this._campLabels.length; i++) {
const label = this._campLabels[i];
if (!label?.node?.isValid) continue;
if (!showLabel || i >= this._grids.length) {
label.node.active = false;
continue;
}
const grid = this._grids[i];
if (!grid || !grid.node?.isValid) {
label.node.active = false;
continue;
}
const world = grid.node.worldPosition;
this._tmpWorld.set(world.x, world.y, world.z);
this._selfTf.convertToNodeSpaceAR(this._tmpWorld, this._tmpLocal);
label.node.active = true;
label.node.setPosition(this._tmpLocal.x, this._tmpLocal.y, 0);
label.fontSize = this.campIdFontSize;
label.string = String(this._lastCampIds[i]);
}
}
}