diff --git a/cc-inspector/src/core/types.ts b/cc-inspector/src/core/types.ts index 0522b97..10cba96 100644 --- a/cc-inspector/src/core/types.ts +++ b/cc-inspector/src/core/types.ts @@ -102,6 +102,14 @@ export enum Msg { * 获取页面ID */ GetTabID = "GetTabID", + /** + * 鼠标滑过节点 + */ + HoverNode = "hover-node", + /** + * 选中节点 + */ + SelectNode = "select-node", /** * 更新页面的frame */ diff --git a/cc-inspector/src/scripts/inject/hint.ts b/cc-inspector/src/scripts/inject/hint.ts new file mode 100644 index 0000000..21475e1 --- /dev/null +++ b/cc-inspector/src/scripts/inject/hint.ts @@ -0,0 +1,268 @@ +declare const cc: any; +class Point { + x: number; + y: number; + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} +class RectPoints { + points: Point[] = []; + get len() { + return this.points.length; + } + at(index: number) { + return this.points[index]; + } + test(width: number, height: number) { + this.points.push(new Point(0, 0)); + this.points.push(new Point(width, 0)); + this.points.push(new Point(width, height)); + this.points.push(new Point(0, height)); + } +} +interface DrawOptions { + fill: boolean; + fillColor: string; + stroke: boolean; + strokeColor: string; +} +export class Hint { + private draw = null; + public engineVersion: string = ""; + private get isEngineV2() { + return this.engineVersion.startsWith("2."); + } + private get isEngineV3() { + return this.engineVersion.startsWith("3."); + } + public cleanHover() { + this.hoverNodes = []; + if (this.draw) { + this.draw.clear(); + } + } + public cleanSelected() { + this.selectedNodes = []; + if (this.draw) { + this.draw.clear(); + } + } + private initDrawNode() { + if (this.draw && !this.draw.isValid) { + this.draw = null; + } + if (this.draw) { + return; + } + const scene = cc.director.getScene(); + if (!scene) { + return; + } + let node = new cc.Node("draw-node"); + const canvas = cc.find("Canvas"); + if (canvas) { + // 3.x 需要放到canvas下边 + if (canvas.layer) { + node.layer = canvas.layer; + } + canvas.addChild(node); + } else { + scene.addChild(node); + } + this.draw = node.addComponent(cc.Graphics); + } + private hoverNodes = []; + private selectedNodes = []; + private resetIndex() { + if (this.isEngineV3) { + const len = this.draw.node.parent.children.length; + this.draw.node.setSiblingIndex(len); + } else if (this.isEngineV2) { + } + } + public update() { + this.initDrawNode(); + if (!this.draw) { + return; + } + this.draw.clear(); + this.resetIndex(); + // this.testRect(); + this.hoverNodes.forEach((node) => { + if (node.isValid) { + this.hintNode(node, { + fill: true, + fillColor: "#00ff0055", + stroke: true, + strokeColor: "#ffffff44", + }); + } + }); + this.selectedNodes.forEach((node) => { + if (node.isValid) { + this.hintNode(node, { + fill: false, + fillColor: "#ff0000", + stroke: true, + strokeColor: "#ff0000", + }); + } + }); + } + private testRect() { + const points = new RectPoints(); + points.test(100, 100); + this.drawRect(points, { + fill: true, + fillColor: "#00ff0099", + stroke: true, + strokeColor: "#ff000099", + }); + } + public setHover(node: any) { + this.hoverNodes = [node]; + } + public setSelected(node: any) { + this.selectedNodes = [node]; + } + private getRectPoints(node: any): RectPoints | null { + if (this.isEngineV2) { + const points: RectPoints = new RectPoints(); + return points; + } else if (this.isEngineV3) { + // v3版本 + if (!node.worldPosition) { + return null; + } + const transform = cc.UITransformComponent || cc.UITransform; + if (!transform) { + return null; + } + const tr = node.getComponent(transform); + if (!tr) { + return null; + } + const { anchorPoint, width, height } = tr; + const points: RectPoints = new RectPoints(); + const x = -anchorPoint.x * width; + const y = -anchorPoint.y * height; + const m = node.worldMatrix; + + [ + new Point(x, y), // + new Point(x + width, y), + new Point(x + width, y + height), + new Point(x, y + height), + ].forEach(({ x, y }: Point) => { + let rhw = m.m03 * x + m.m07 * y + m.m15; + rhw = rhw ? 1 / rhw : 1; + let worldX = (m.m00 * x + m.m04 * y + m.m12) * rhw; + let worldY = (m.m01 * x + m.m05 * y + m.m13) * rhw; + let worldZ = (m.m02 * x + m.m06 * y + m.m14) * rhw; + const pos = this.draw.getComponent(transform).convertToNodeSpaceAR(cc.v3(worldX, worldY, worldZ)); + points.points.push(new Point(pos.x, pos.y)); + }); + + return points; + } + return null; + } + /** + * 尝试实现wireframe模式 + */ + private getTrangles(node: any) { + const comps = node._components; + if (comps && Array.isArray(comps)) { + const m = node.worldMatrix; + comps.forEach((comp) => { + const { renderData } = comp; + if (!renderData) { + return; + } + const { data, _vc: VertexCount, _ic: IndexCount, chunk } = renderData; + if (!data) { + return; + } + + // 获取indexBuffer的索引顺序 + const ib = chunk.meshBuffer.iData; + let indexOffset = chunk.meshBuffer.indexOffset; + const vidOrigin = chunk.vertexOffset; + const arr = []; + for (let i = 0; i < IndexCount; i++) { + const index = ib[indexOffset + i] - vidOrigin; + arr.push(index); + } + + for (let j = 0; j < data.length; j++) { + const item = data[j]; + const { x, y, z, u, v, color } = item; + let rhw = m.m03 * x + m.m07 * y + m.m15; + rhw = rhw ? 1 / rhw : 1; + + let worldX = (m.m00 * x + m.m04 * y + m.m12) * rhw; + let worldY = (m.m01 * x + m.m05 * y + m.m13) * rhw; + let worldZ = (m.m02 * x + m.m06 * y + m.m14) * rhw; + // const pos = this.draw.getComponent(transform).convertToNodeSpaceAR(cc.v3(worldX, worldY, worldZ)); + // points.points.push(new Point(pos.x, pos.y)); + } + }); + } + } + private getBox(node: any) { + if (node.getBoundingBoxToWorld) { + return node.getBoundingBoxToWorld(); + } + + if (cc.UITransformComponent) { + const tr = node.getComponent(cc.UITransformComponent); + if (tr && tr.getBoundingBoxToWorld) { + return tr.getBoundingBoxToWorld(); + } + } + return null; + } + private hintNode(node: any, opts: DrawOptions) { + const points = this.getRectPoints(node); + if (!points) { + return; + } + this.drawRect(points, opts); + } + + private drawRect(points: RectPoints, opts: DrawOptions) { + this.draw.lineWidth = 2; + for (let i = 0; i < points.len; i++) { + const p = points.at(i); + if (i === 0) { + this.draw.moveTo(p.x, p.y); + } else { + this.draw.lineTo(p.x, p.y); + } + } + this.draw.close(); + if (opts.stroke) { + this.draw.strokeColor = new cc.Color().fromHEX(opts.strokeColor); + this.draw.stroke(); + } + if (opts.fill) { + this.draw.fillColor = new cc.Color().fromHEX(opts.fillColor); + this.draw.fill(); + } + } + private fillRect(points: RectPoints, color: string) { + this.draw.fillColor = new cc.Color().fromHEX(color); + for (let i = 0; i < points.len; i++) { + const p = points.at(i); + if (i === 0) { + this.draw.moveTo(p.x, p.y); + } else { + this.draw.lineTo(p.x, p.y); + } + } + this.draw.close(); + this.draw.fill(); + } +} diff --git a/cc-inspector/src/scripts/inject/inspector.ts b/cc-inspector/src/scripts/inject/inspector.ts index c730f60..6eca7e2 100644 --- a/cc-inspector/src/scripts/inject/inspector.ts +++ b/cc-inspector/src/scripts/inject/inspector.ts @@ -3,6 +3,7 @@ import { uniq } from "lodash"; import { Msg, PluginEvent, RequestLogData, RequestNodeInfoData, RequestSetPropertyData, ResponseGameInfoData, ResponseNodeInfoData, ResponseSetPropertyData, ResponseSupportData, ResponseTreeInfoData } from "../../core/types"; import { ArrayData, BoolData, ColorData, DataType, EngineData, Group, ImageData, Info, InvalidData, NodeInfoData, NumberData, ObjectCircleData, ObjectData, Property, StringData, TreeData, Vec2Data, Vec3Data, Vec4Data } from "../../views/devtools/data"; import { InjectEvent } from "./event"; +import { Hint } from "./hint"; import { injectView } from "./inject-view"; import { getValue, trySetValueWithConfig } from "./setValue"; import { BuildArrayOptions, BuildImageOptions, BuildObjectOptions, BuildVecOptions } from "./types"; @@ -11,6 +12,7 @@ declare const cc: any; export class Inspector extends InjectEvent { inspectorGameMemoryStorage: Record = {}; + private hint: Hint = new Hint(); private getAtlasViewFunction() { // 之前只有v2版本支持 @@ -104,6 +106,34 @@ export class Inspector extends InjectEvent { } break; } + case Msg.HoverNode: { + const uuid: string = pluginEvent.data; + if (uuid) { + const node = this.inspectorGameMemoryStorage[uuid]; + if (node.isValid) { + this.hint.setHover(node); + } else { + this.hint.cleanHover(); + } + } else { + this.hint.cleanHover(); + } + break; + } + case Msg.SelectNode: { + const uuid: string = pluginEvent.data; + if (uuid) { + const node = this.inspectorGameMemoryStorage[uuid]; + if (node.isValid) { + this.hint.setSelected(node); + } else { + this.hint.cleanSelected(); + } + } else { + this.hint.cleanSelected(); + } + break; + } case Msg.RequestDestroy: { const uuid: string = pluginEvent.data; const node = this.inspectorGameMemoryStorage[uuid]; @@ -124,14 +154,18 @@ export class Inspector extends InjectEvent { if (b) { clearInterval(timer); injectView.init(); + cc.director.on(cc.Director.EVENT_AFTER_DRAW, () => { + this.hint.update(); + }); const version = this.getEngineVersion(); + this.hint.engineVersion = version; if (b && version) { this.uploadEngineVersion(version); } } }, 300); } - private getEngineVersion() { + private getEngineVersion(): string { if (this._isCocosGame()) { return cc.ENGINE_VERSION || ""; } else { @@ -180,44 +214,6 @@ export class Inspector extends InjectEvent { } } - // @ts-ignore - draw: cc.Graphics = null; - - _drawRect(node: any) { - let draw = this.draw; - - if (!draw) { - // @ts-ignore - let node = new cc.Node("draw-node"); - // @ts-ignore - cc.director.getScene().addChild(node); - // @ts-ignore - draw = this.draw = node.addComponent(cc.Graphics); - } - draw.clear(); - draw.lineWidth = 10; - // @ts-ignore - draw.strokeColor = new cc.Color().fromHEX("#ff0000"); - const { anchorX, anchorY, width, height, x, y } = node; - let halfWidth = width / 2; - let halfHeight = height / 2; - let leftBottom = node.convertToWorldSpaceAR(cc.v2(-halfWidth, -halfHeight)); - let leftTop = node.convertToWorldSpaceAR(cc.v2(-halfWidth, halfHeight)); - let rightBottom = node.convertToWorldSpaceAR(cc.v2(halfWidth, -halfHeight)); - let rightTop = node.convertToWorldSpaceAR(cc.v2(halfWidth, halfHeight)); - - function line(began: any, end: any) { - draw.moveTo(began.x, began.y); - draw.lineTo(end.x, end.y); - } - - line(leftBottom, rightBottom); - line(rightBottom, rightTop); - line(rightTop, leftTop); - line(leftTop, leftBottom); - this.draw.stroke(); - } - // 收集节点信息 getNodeChildren(node: any, data: TreeData) { data.id = node.uuid; diff --git a/cc-inspector/src/views/devtools/hierarchy.vue b/cc-inspector/src/views/devtools/hierarchy.vue index f5c54eb..d99bd0c 100644 --- a/cc-inspector/src/views/devtools/hierarchy.vue +++ b/cc-inspector/src/views/devtools/hierarchy.vue @@ -10,7 +10,7 @@ - + @@ -171,6 +171,7 @@ export default defineComponent({ function updateSelect(uuid: string | null) { selectedUUID = uuid; Bus.emit(BusMsg.SelectNode, uuid); + bridge.send(Msg.SelectNode, uuid); } const freshAuto = ref(config.value.refreshHirarchy); return { @@ -192,6 +193,16 @@ export default defineComponent({ handleNodeUnclick() { updateSelect(null); }, + handleNodeEnter(data: TreeData | null) { + if (data) { + bridge.send(Msg.HoverNode, data.id); + } + }, + handleNodeLeave(data: TreeData | null) { + if (data) { + bridge.send(Msg.HoverNode, null); + } + }, handleNodeClick(data: TreeData | null) { if (data) { updateSelect(data.id);