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.
368 lines
14 KiB
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]);
|
|
}
|
|
}
|
|
}
|
|
|