DataBoardDemo/assets/Script/DataBoard.ts
2024-02-07 12:57:15 +08:00

322 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*******************************************************************************
* 创建: 2022年11月23日
* 作者: 水煮肉片饭(27185709@qq.com)
* 描述: 数据看板
* 节点挂上该组件,就可以在游戏运行过程中,直观看到节点任意属性(包括节点脚本中的属性)
* 可以图形化展示以下四种数据:
* 轮廓盒子: 随节点旋转,代表节点的实时矩形
* 碰撞盒子: 不随节点旋转,一般代表碰撞范围
* 自定义参数: 节点自身属性,以及节点任意脚本中的属性
* 锚点: 锚点位置会显示一个小红点
* 自定义参数(配置想监控的数据):
* wp 世界坐标
* radian 节点弧度(单位:π)
* matrix: 变换矩阵
* 自身属性: x,y,parent,children等
* 脚本属性: 脚本实例对象的属性
* ↓↓参数可以用3种分隔符隔开↓↓
* 英文逗号、英文冒号、空格
* ————————————————————————举个栗子————————————————————————
* 脚本: Hero
* 参数: wp,scale,angle,#angle,#hp
* 显示结果:世界坐标,节点scale,节点angleHero对象的angle,Hero对象的hp
* ————————————————————————温馨提示————————————————————————
* 初始化的时候设置全局变量window['DATABOARD'] = false可屏蔽本项目所有DataBoard不会产生任何额外开销\n
*******************************************************************************/
window['DATABOARD'] = true;
const ANCHOR_SIZE = 20; //锚点的大小
const LINEHEIGHT = 1.2; //行高是字体大小的多少倍
const { ccclass, property, executeInEditMode, menu } = cc._decorator;
@ccclass
@executeInEditMode
@menu('Comp/DataBoard')
export default class DataBoard extends cc.Component {
@property
private _isOutlineBoxActive: boolean = false;
@property({ displayName: CC_DEV && '轮廓盒子', tooltip: CC_DEV && '随节点旋转,代表实时轮廓' })
private get isOutlineBoxActive() { return this._isOutlineBoxActive };
private set isOutlineBoxActive(value: boolean) {
this._isOutlineBoxActive = value;
this.outlineBoxNode.active = value;
}
@property
private _outlineBoxColor: cc.Color = new cc.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255);
@property({ displayName: CC_DEV && '······颜色', visible() { return this.isOutlineBoxActive } })
private get outlineBoxColor() { return this._outlineBoxColor };
private set outlineBoxColor(value: cc.Color) {
this._outlineBoxColor = value;
this.outlineBoxNode.color = value;
}
@property
private _outlineBoxOpacity: number = 100;
@property({ min: 0, max: 255, step: 1, slide: true, displayName: CC_DEV && '······透明度', visible() { return this.isOutlineBoxActive } })
private get outlineBoxOpacity() { return this._outlineBoxOpacity };
private set outlineBoxOpacity(value: number) {
this._outlineBoxOpacity = value;
this.outlineBoxNode.opacity = value;
}
@property
private _isCollideBoxActive: boolean = true;
@property({ displayName: CC_DEV && '碰撞盒子', tooltip: CC_DEV && '不随节点旋转,代表碰撞范围' })
private get isCollideBoxActive() { return this._isCollideBoxActive };
private set isCollideBoxActive(value: boolean) {
this._isCollideBoxActive = value;
this.collideBoxNode.active = value;
}
@property
private _collideBoxColor: cc.Color = new cc.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255);
@property({ displayName: CC_DEV && '······颜色', visible() { return this.isCollideBoxActive } })
private get collideBoxColor() { return this._collideBoxColor };
private set collideBoxColor(value: cc.Color) {
this._collideBoxColor = value;
this.collideBoxNode.color = value;
}
@property
private _collideBoxOpacity: number = 100;
@property({ min: 0, max: 255, step: 1, slide: true, displayName: CC_DEV && '······透明度', visible() { return this.isCollideBoxActive } })
private get collideBoxOpacity() { return this._collideBoxOpacity };
private set collideBoxOpacity(value: number) {
this._collideBoxOpacity = value;
this.collideBoxNode.opacity = value;
}
@property
private _isCustomLabelActive: boolean = true;
@property({ displayName: CC_DEV && '自定义参数', tooltip: CC_DEV && '配置显示的属性内容' })
private get isCustomLabelActive() { return this._isCustomLabelActive };
private set isCustomLabelActive(value: boolean) {
this._isCustomLabelActive = value;
this.customLabelNode.active = value;
}
@property
private _customComponentName: string = '';
@property({ displayName: CC_DEV && '······脚本', tooltip: CC_DEV && '监控哪个脚本', visible() { return this.isCustomLabelActive; } })
private get customComponentName() { return this._customComponentName };
private set customComponentName(value: string) {
this._customComponentName = value;
value ||= this.node.name;
value && (this.monitorComp = this.node.getComponent(value));
}
@property
private _customLabelString: string = 'x,y';
@property({ multiline: true, displayName: CC_DEV && '······参数', tooltip: CC_DEV && "—————支持的参数————\nwp世界坐标\nradian节点弧度单位π\nmatrix变换矩阵\n自身属性x,y,parent,children等\n脚本属性脚本实例对象的属性\n↓↓参数可以用3种分隔符隔开↓↓\n英文逗号、英文冒号、空格\n————举个栗子————\n脚本Hero\n参数wp,scale,angle,#angle,#hp\n显示结果\n世界坐标,节点scale,节点angleHero对象的angle,Hero对象的hp\n————温馨提示————\n初始化的时候设置全局变量\nwindow['DATABOARD'] = false\n可屏蔽本项目所有DataBoard不会产生任何额外开销", visible() { return this.isCustomLabelActive } })
private get customLabelString() { return this._customLabelString };
private set customLabelString(value: string) {
this._customLabelString = value;
this.customLabelStringSplit = value
.replace(/,/g, '_~_').replace(/:/g, '_!_').replace(/ /g, '_@_')
.replace(/_*\n_*/g, '_\n_').split('_');
}
@property
private _customLabelOffset: cc.Vec2 = cc.v2(0, 100);
@property({ displayName: CC_DEV && '······偏移', visible() { return this.isCustomLabelActive } })
private get customLabelOffset() { return this._customLabelOffset };
private set customLabelOffset(value: cc.Vec2) {
this._customLabelOffset = value;
this.customLabelNode.x = value.x;
this.customLabelNode.y = value.y;
}
@property
private _customLabelColor: cc.Color = new cc.Color(255, 255, 0);
@property({ displayName: CC_DEV && '······颜色', visible() { return this.isCustomLabelActive } })
private get customLabelColor() { return this._customLabelColor };
private set customLabelColor(value: cc.Color) {
this._customLabelColor = value;
this.customLabelNode.color = value;
}
@property
private _customLabelSize: number = 60;
@property({ displayName: CC_DEV && '······大小', visible() { return this.isCustomLabelActive } })
private get customLabelSize() { return this._customLabelSize };
private set customLabelSize(value: number) {
this._customLabelSize = value;
this.customLabel.fontSize = value;
this.customLabel.lineHeight = value * LINEHEIGHT;
this.customLabelNode.getComponent(cc.LabelOutline).width = value * 0.1;
}
@property
private _customLabelDigit: number = 0;
@property({ min: 0, max: 10, step: 1, slide: true, displayName: CC_DEV && '······小数位数', visible() { return this.isCustomLabelActive } })
private get customLabelDigit() { return this._customLabelDigit };
private set customLabelDigit(value: number) {
this._customLabelDigit = value;
}
private boardNode: cc.Node = null;
private outlineBoxNode: cc.Node = null;
private collideBoxNode: cc.Node = null;
private anchorPointNode: cc.Node = null;
private customLabelNode: cc.Node = null;
private customLabel: cc.Label = null;
private customLabelStringSplit: string[] = null;
private monitorComp: cc.Component = null;
protected start() {
if (!CC_EDITOR && !window['DATABOARD']) {
this.destroy();
return;
}
this.boardNode = this.node.getChildByName('DataBoard');
if (cc.isValid(this.boardNode)) {
this.boardNode.destroy();
this.boardNode.removeFromParent();
}
let texture = new cc.Texture2D();
texture.initWithData(new Uint8Array([255, 255, 255]), cc.Texture2D.PixelFormat.RGB888, 1, 1);
this.boardNode = new cc.Node('DataBoard');
this.boardNode.setParent(this.node);
this.boardNode.zIndex = cc.macro.MAX_ZINDEX;
this.boardNode['_objFlags'] |= cc.Object['Flags'].HideInHierarchy;
this.boardNode['_objFlags'] |= cc.Object['Flags'].LockedInEditor;
this.outlineBoxNode = new cc.Node('OutlineBox');
this.outlineBoxNode.setParent(this.boardNode);
this.outlineBoxNode.addComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
this.outlineBoxNode.active = this.isOutlineBoxActive;
this.outlineBoxNode.color = this.outlineBoxColor;
this.outlineBoxNode.opacity = this.outlineBoxOpacity;
this.collideBoxNode = new cc.Node('CollideBox');
this.collideBoxNode.setParent(this.boardNode);
this.collideBoxNode.addComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
this.collideBoxNode.active = this.isCollideBoxActive;
this.collideBoxNode.color = this.collideBoxColor;
this.collideBoxNode.opacity = this.collideBoxOpacity;
this.anchorPointNode = new cc.Node('AnchorPoint');
this.anchorPointNode.setParent(this.boardNode);
this.anchorPointNode.addComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
this.anchorPointNode.color = cc.color(255, 0, 0);
this.anchorPointNode.width = ANCHOR_SIZE;
this.anchorPointNode.height = ANCHOR_SIZE;
this.customLabelNode = new cc.Node('CustomLabel');
this.customLabelNode.setParent(this.boardNode);
this.customLabel = this.customLabelNode.addComponent(cc.Label);
this.customLabelNode.addComponent(cc.LabelOutline).color = cc.color(0, 0, 0);
this.customLabelNode.active = this.isCustomLabelActive;
this.customLabelString = this._customLabelString;
this.customLabelNode.x = this.customLabelOffset.x;
this.customLabelNode.y = this.customLabelOffset.y;
this.customLabelNode.color = this.customLabelColor;
this.updateAngle();
this.updateScale();
this.updateAnchor();
this.updateSize();
this.node.on(cc.Node.EventType.ROTATION_CHANGED, this.updateAngle, this);
this.node.on(cc.Node.EventType.SCALE_CHANGED, this.updateScale, this);
this.node.on(cc.Node.EventType.ANCHOR_CHANGED, this.updateAnchor, this);
this.node.on(cc.Node.EventType.SIZE_CHANGED, this.updateSize, this);
cc.director.once(cc.Director.EVENT_AFTER_DRAW, () => {
this.customLabelSize = this._customLabelSize;
this.customComponentName = this._customComponentName;
}, this);
}
private updateAngle() {
this.collideBoxNode.angle = -this.node.angle;
this.customLabelNode.angle = -this.node.angle;
}
private updateScale() {
this.boardNode.scaleX = 1 / this.node.scaleX;
this.boardNode.scaleY = 1 / this.node.scaleY;
this.outlineBoxNode.scaleX = this.node.scaleX;
this.outlineBoxNode.scaleY = this.node.scaleY;
this.collideBoxNode.scaleX = this.node.scaleX;
this.collideBoxNode.scaleY = this.node.scaleY;
}
private updateAnchor() {
this.outlineBoxNode.anchorX = this.node.anchorX;
this.outlineBoxNode.anchorY = this.node.anchorY;
this.collideBoxNode.anchorX = this.node.anchorX;
this.collideBoxNode.anchorY = this.node.anchorY;
}
private updateSize() {
this.outlineBoxNode.width = this.node.width;
this.outlineBoxNode.height = this.node.height;
this.collideBoxNode.width = this.node.width;
this.collideBoxNode.height = this.node.height;
}
protected update() {
if (!this.isCustomLabelActive) return;
if (!this.customLabelStringSplit) return;
let strs = this.customLabelStringSplit;
let str = '';
for (let i = 0, len = strs.length; i < len; ++i) {
let tmp = null;
switch (strs[i]) {
case 'wp':
let matrix = this.node['_worldMatrix'].m;
tmp = `${matrix[12].toFixed(this.customLabelDigit)},\t${matrix[13].toFixed(this.customLabelDigit)}`;
break;
case 'angle':
tmp = this.node.angle.toFixed(this.customLabelDigit) + '°';
break;
case 'radian':
tmp = (this.node.angle / 180).toFixed(this.customLabelDigit) + 'π';
break;
case 'matrix':
matrix = this.node['_worldMatrix'].m;
tmp = '';
for (let i = 0; i < 4; ++i) {
for (let j = 0; j < 4; ++j) {
let m = matrix[(j << 2) + i];
tmp += (m < 0 ? '\t\t' : '\t\t\t') + m.toFixed(this.customLabelDigit);
}
i !== 3 && (tmp += '\n');
}
break;
case 'children':
tmp = '';
for (let i = 0, len = this.node.childrenCount; i < len; ++i) {
tmp += `\t\t\t${i}${this.node.children[i].name}`;
i !== len - 1 && (tmp += '\n');
}
break;
case '~': tmp = ',\t'; break;
case '!': tmp = ':\t'; break;
case '@': tmp = '\t\t'; break;
default:
if (this.node[strs[i]] !== undefined) {
tmp = this.node[strs[i]];
} else if (strs[i].startsWith('#') && this.monitorComp !== null) {
tmp = this.parseString(strs[i].substring(1));
} else {
tmp = strs[i];
}
if (typeof tmp === 'number') {
tmp = tmp.toFixed(this.customLabelDigit);
} else if (tmp.name) {
tmp = tmp.name;
}
break;
}
str += tmp;
}
this.customLabel.string = str;
}
private parseString(str: string) {
let strs = str.split('.');
let ret = this.monitorComp[strs[0]] ?? `#${strs[0]}`;
for (let i = 1, len = strs.length; i < len; ++i) {
if (ret[strs[i]] === undefined) {
return `${ret.name ?? ret}.${strs[i]}`;
}
ret = ret[strs[i]];
}
return ret;
}
protected onDestroy() {
if (cc.isValid(this.boardNode)) {
this.boardNode.destroy();
this.boardNode.removeFromParent();
}
this.node.targetOff(this);
}
}