From 46e70a84f03c1fdc1bfbe24ed7ceac753900af83 Mon Sep 17 00:00:00 2001 From: Zhou Zhenglong Date: Thu, 7 May 2026 20:29:16 +0800 Subject: [PATCH] Revert "Rename interface to match its intended usage (#535)" This reverts commit 5336da06eb0ed5863f3c033cba620704eab49c98. Revert "Replace any with specific types for better type safety (#532)" This reverts commit 2699582bd4122eebeffaff7886ff235bddfe2b3f. Revert "Reapply "Enhance dump-related interfaces and add node-related interfaces (#521)" (#533) (#538)" This reverts commit 1df32d61eee04a07feef3d4d00f02cfa56e5b1f3. --- .../cocos-cli-types/__tests__/cli.test.ts | 4 +- src/api/scene/component.ts | 10 +- src/api/scene/node.ts | 12 +- src/core/scene/common/component.ts | 70 +- src/core/scene/common/editor/scene.ts | 18 +- src/core/scene/common/node.ts | 195 +- src/core/scene/common/prefab/prefab-info.ts | 8 - src/core/scene/main-process/index.ts | 7 +- .../main-process/proxy/component-proxy.ts | 43 +- .../scene/main-process/proxy/node-proxy.ts | 24 +- .../scene/scene-process/service/component.ts | 87 +- .../scene-process/service/component/index.ts | 43 +- .../service/core/base-service.ts | 5 +- .../scene-process/service/dump/decode.ts | 396 +--- .../scene-process/service/dump/encode.ts | 289 +-- .../scene/scene-process/service/dump/index.ts | 35 +- src/core/scene/scene-process/service/node.ts | 295 ++- .../scene/scene-process/service/node/index.ts | 1833 ----------------- .../scene-process/service/node/node-utils.ts | 27 - .../scene-process/service/service-manager.ts | 4 +- .../scene/test/component-proxy.testcase.ts | 453 ++-- .../test/editor-proxy-prefab.testcase.ts | 10 +- .../scene/test/editor-proxy-scene.testcase.ts | 6 +- src/core/scene/test/engine-proxy.testcase.ts | 12 +- .../scene/test/node-dump-proxy.testcase.ts | 491 ----- src/core/scene/test/node-proxy.testcase.ts | 152 +- src/core/scene/test/prefab-proxy.testcase.ts | 80 +- src/core/scene/test/scene.test.ts | 1 - 28 files changed, 716 insertions(+), 3894 deletions(-) delete mode 100644 src/core/scene/scene-process/service/node/index.ts delete mode 100644 src/core/scene/test/node-dump-proxy.testcase.ts diff --git a/packages/cocos-cli-types/__tests__/cli.test.ts b/packages/cocos-cli-types/__tests__/cli.test.ts index 22eb3d52f..00724e272 100644 --- a/packages/cocos-cli-types/__tests__/cli.test.ts +++ b/packages/cocos-cli-types/__tests__/cli.test.ts @@ -30,13 +30,13 @@ describe('cocos-cli-types: cli', () => { it('IServiceManager.Node should have CRUD methods', () => { type NodeKeys = keyof IServiceManager['Node']; - const nodeMethods: NodeKeys[] = ['createByType', 'createByAsset', 'delete', 'update', 'query', 'queryNodeTree']; + const nodeMethods: NodeKeys[] = ['createNodeByType', 'createNodeByAsset', 'deleteNode', 'updateNode', 'queryNode', 'queryNodeTree']; expect(nodeMethods.length).toBeGreaterThan(0); }); it('IServiceManager.Component should have component methods', () => { type CompKeys = keyof IServiceManager['Component']; - const compMethods: CompKeys[] = ['add', 'remove', 'setProperty', 'query', 'queryAll']; + const compMethods: CompKeys[] = ['addComponent', 'removeComponent', 'setProperty', 'queryComponent', 'queryAllComponent']; expect(compMethods.length).toBeGreaterThan(0); }); diff --git a/src/api/scene/component.ts b/src/api/scene/component.ts index ec1adf1db..b16e9fcba 100644 --- a/src/api/scene/component.ts +++ b/src/api/scene/component.ts @@ -30,7 +30,7 @@ export class ComponentApi { @result(SchemaComponentResult) async addComponent(@param(SchemaAddComponentInfo) addComponentInfo: TAddComponentInfo): Promise> { try { - const component = await Scene.Component.add({ nodePath: addComponentInfo.nodePath, component: addComponentInfo.component }); + const component = await Scene.addComponent({ nodePathOrUuid: addComponentInfo.nodePath, component: addComponentInfo.component }); return { code: COMMON_STATUS.SUCCESS, data: component @@ -52,7 +52,7 @@ export class ComponentApi { @result(SchemaBooleanResult) async removeComponent(@param(SchemaRemoveComponent) component: TRemoveComponentOptions): Promise> { try { - const result = await Scene.Component.remove(component); + const result = await Scene.removeComponent(component); return { code: COMMON_STATUS.SUCCESS, data: result @@ -74,7 +74,7 @@ export class ComponentApi { @result(SchemaComponentResult) async queryComponent(@param(SchemaQueryComponent) component: TQueryComponentOptions): Promise> { try { - const componentInfo = await Scene.Component.query(component); + const componentInfo = await Scene.queryComponent(component); if (!componentInfo) { throw new Error(`component not found: ${component.path}`); } @@ -99,7 +99,7 @@ export class ComponentApi { @result(SchemaBooleanResult) async setProperty(@param(SchemaSetPropertyOptions) setPropertyOptions?: TSetPropertyOptions): Promise> { try { - const result = await Scene.Component.setProperty(setPropertyOptions as ISetPropertyOptions); + const result = await Scene.setProperty(setPropertyOptions as ISetPropertyOptions); return { code: COMMON_STATUS.SUCCESS, data: result @@ -121,7 +121,7 @@ export class ComponentApi { @result(SchemaQueryAllComponentResult) async queryAllComponent(): Promise> { try { - const components = await Scene.Component.queryAll(); + const components = await Scene.queryAllComponent(); return { code: COMMON_STATUS.SUCCESS, data: components, diff --git a/src/api/scene/node.ts b/src/api/scene/node.ts index fc6d9dc46..5dbcef6d4 100644 --- a/src/api/scene/node.ts +++ b/src/api/scene/node.ts @@ -18,7 +18,7 @@ import { } from './node-schema'; import { description, param, result, title, tool } from '../decorator/decorator.js'; import { COMMON_STATUS, CommonResultType } from '../base/schema-base'; -import { ICreateByNodeTypeParams, INode, Scene } from '../../core/scene'; +import { ICreateByNodeTypeParams, Scene } from '../../core/scene'; export class NodeApi { @@ -35,7 +35,7 @@ export class NodeApi { data: undefined, }; try { - const resultNode = await Scene.Node.createByType(options as ICreateByNodeTypeParams); + const resultNode = await Scene.createNodeByType(options as ICreateByNodeTypeParams); if (resultNode) { ret.data = resultNode; } @@ -62,7 +62,7 @@ export class NodeApi { data: undefined, }; try { - const resultNode = await Scene.Node.createByAsset(options); + const resultNode = await Scene.createNodeByAsset(options); if (resultNode) { ret.data = resultNode; } @@ -90,7 +90,7 @@ export class NodeApi { }; try { - const result = await Scene.Node.delete(options); + const result = await Scene.deleteNode(options); if (!result) throw new Error(`node not found at path: ${options.path}`); ret.data = { path: result.path, @@ -114,7 +114,7 @@ export class NodeApi { @result(SchemaNodeUpdateResult) async updateNode(@param(SchemaNodeUpdate) options: TUpdateNodeOptions): Promise> { try { - const data = await Scene.Node.update(options); + const data = await Scene.updateNode(options); return { data: data, code: COMMON_STATUS.SUCCESS, @@ -142,7 +142,7 @@ export class NodeApi { }; try { - const result = await Scene.Node.query(options) as INode | null; + const result = await Scene.queryNode(options); if (!result) throw new Error(`node not found at path: ${options.path}`); ret.data = result; } catch (e) { diff --git a/src/core/scene/common/component.ts b/src/core/scene/common/component.ts index 54580642d..4ad283d01 100644 --- a/src/core/scene/common/component.ts +++ b/src/core/scene/common/component.ts @@ -41,7 +41,7 @@ export interface IComponentForEditor extends IProperty { * 添加/创建组件的选项 */ export interface IAddComponentOptions { - nodePath: string; + nodePathOrUuid: string; component: string; } @@ -51,10 +51,6 @@ export interface IAddComponentOptions { export interface IRemoveComponentOptions { path: string; } -export interface IRemovedComponentInfo { - name: string; - fileID: string; -} /** * 查询组件的选项 @@ -67,7 +63,7 @@ export interface IQueryComponentOptions { * CLI 设置组件属性的选项 */ export interface ISetPropertyOptions { - componentPath: string; // 修改属性的节点路径 + componentPath: string; // 修改属性的对象的 uuid // key: string; // 属性的 key properties: { [key: string]: null | undefined | number | boolean | string | object | Array; @@ -79,7 +75,7 @@ export interface ISetPropertyOptions { * 编辑器设置组件属性的选项 */ export interface ISetPropertyOptionsForEditor { - nodePath: string; // 修改属性的节点路径 + uuid: string; // 修改属性的对象的 uuid path: string; // 属性挂载对象的搜索路径 // key: string; // 属性的 key dump: IProperty; // 属性 dump 出来的数据 @@ -90,7 +86,7 @@ export interface ISetPropertyOptionsForEditor { * 执行组件方法的选项 */ export interface IExecuteComponentMethodOptions { - path: string; // 组件路径,如 'Canvas/cc.Label_1' + uuid: string; name: string; args: any[]; } @@ -108,12 +104,12 @@ export interface IQueryClassesOptions { */ export interface IComponentEvents extends INodeEvents { 'component:add': [Component]; + 'component:before-remove': [Component]; 'component:remove': [Component]; 'component:set-property': [Component, IChangeNodeOptions]; 'component:added': [Component]; 'component:removed': [Component]; 'component:before-add-component': [string, Node]; - 'component:before-remove-component': [Component]; } /** @@ -121,13 +117,7 @@ export interface IComponentEvents extends INodeEvents { */ export interface IPublicComponentService extends Omit { } /** @@ -137,33 +127,33 @@ export interface IComponentService extends IServiceEvents { /** * 添加组件到指定节点,返回添加后的组件信息 * @param params - 添加组件选项 - * @param params.nodePath - 目标节点路径 + * @param params.nodePathOrUuid - 目标节点路径或 UUID * @param params.component - 组件类名,支持精确匹配('cc.Label')和模糊匹配('label') * @returns 添加成功后的组件信息 * * @example * ```ts * // 通过节点路径 + 精确组件名 - * const comp = await add({ nodePath: 'Canvas/MyNode', component: 'cc.Label' }); + * const comp = await addComponent({ nodePathOrUuid: 'Canvas/MyNode', component: 'cc.Label' }); * - * // 通过节点路径 + 模糊组件名 - * const comp = await add({ nodePath: 'Canvas/MyNode', component: 'label' }); + * // 通过节点 UUID + 模糊组件名 + * const comp = await addComponent({ nodePathOrUuid: 'abc-123-uuid', component: 'label' }); * ``` */ - add(params: IAddComponentOptions): Promise; + addComponent(params: IAddComponentOptions): Promise; /** * 删除指定组件 * @param params - 删除组件选项 - * @param params.path - 组件路径 + * @param params.path - 组件路径,支持路径、UUID 或资源 URL * @returns 删除成功返回 true,失败返回 false */ - remove(params: IRemoveComponentOptions): Promise; + removeComponent(params: IRemoveComponentOptions): Promise; /** * 设置组件属性 * - CLI 调用时传入 ISetPropertyOptions,通过 componentPath 定位,属性为扁平键值对 - * - 编辑器调用时传入 ISetPropertyOptionsForEditor,通过节点路径 + dump 路径定位,属性为 IProperty 格式 + * - 编辑器调用时传入 ISetPropertyOptionsForEditor,通过节点 UUID + dump 路径定位,属性为 IProperty 格式 * * @param params - 设置属性选项,根据调用方不同传入不同类型 * @returns 设置成功返回 true,失败返回 false @@ -176,9 +166,9 @@ export interface IComponentService extends IServiceEvents { * properties: { string: 'Hello', fontSize: 32 }, * }); * - * // 编辑器方式:通过节点路径 + dump 路径定位,传 IProperty 格式 + * // 编辑器方式:通过节点 UUID + dump 路径定位,传 IProperty 格式 * await setProperty({ - * nodePath: 'Canvas/MyNode', + * uuid: 'node-uuid', * path: '__comps__.0.string', * dump: { value: 'Hello', type: 'String' }, * }); @@ -197,38 +187,38 @@ export interface IComponentService extends IServiceEvents { * @example * ```ts * CLI 模式:返回 IComponent(扁平属性) - * const comp = await query({ path: 'Canvas/cc.Label_1' }) as IComponent; + * const comp = await queryComponent({ path: 'Canvas/cc.Label_1' }) as IComponent; * * 编辑器模式:直接传 string,这里是uuid,因为与cli重复了,也支持 path 和 url - * const comp = await query('uuid') as IComponentForEditor; + * const comp = await queryComponent('uuid') as IComponentForEditor; * ``` */ - query(params: IQueryComponentOptions | string): Promise; + queryComponent(params: IQueryComponentOptions | string): Promise; /** * 获取所有已注册的组件类名,包含内置与自定义组件 * @returns 组件类名数组,如 ['cc.Label', 'cc.Sprite', 'MyCustomComponent'] */ - queryAll(): Promise; + queryAllComponent(): Promise; // ---- 编辑器相关接口 ---- /** - * 创建组件(编辑器使用),与 add 不同的是仅返回是否成功 + * 创建组件(编辑器使用),与 addComponent 不同的是仅返回是否成功 * @param params - 添加组件选项 - * @param params.nodePath - 目标节点路径 + * @param params.nodePathOrUuid - 目标节点路径或 UUID * @param params.component - 组件类名 * @returns 创建成功返回 true,失败返回 false */ - create(params: IAddComponentOptions): Promise; + createComponent(params: IAddComponentOptions): Promise; /** * 复位组件,将组件所有属性恢复为默认值 * @param params - 查询组件选项,用于定位要复位的组件 - * @param params.path - 组件路径 + * @param params.path - 组件路径,支持路径、UUID 或资源 URL * @returns 复位成功返回 true,失败返回 false */ - reset(params: IQueryComponentOptions): Promise; + resetComponent(params: IQueryComponentOptions): Promise; /** * 获取所有注册类名,支持按继承关系过滤 @@ -253,27 +243,27 @@ export interface IComponentService extends IServiceEvents { /** * 查询指定节点上所有组件暴露的可调用函数 - * @param path - 节点路径 + * @param uuid - 节点 UUID * @returns 节点上组件的函数信息,节点不存在时返回空对象 */ - queryFunctionOfNode(path: string): Promise; + queryComponentFunctionOfNode(uuid: string): Promise; /** * 执行组件上的指定方法 * @param options - 执行选项 - * @param options.path - 组件路径,如 'Canvas/cc.Label_1' + * @param options.uuid - 组件实例的 UUID * @param options.name - 要执行的方法名,如 'onLoad'、'start' * @param options.args - 方法参数列表 * @returns 执行成功返回 true,失败返回 false */ - executeMethod(options: IExecuteComponentMethodOptions): Promise; + executeComponentMethod(options: IExecuteComponentMethodOptions): Promise; /** * 查询指定名称的组件是否已注册(是否存在对应脚本) * @param name - 组件类名,如 'cc.Label' * @returns 存在返回 true,不存在返回 false */ - hasScript(name: string): Promise; + queryComponentHasScript(name: string): Promise; // ---- 内部接口,不对外暴露 ---- diff --git a/src/core/scene/common/editor/scene.ts b/src/core/scene/common/editor/scene.ts index c1598e27b..ac4333e75 100644 --- a/src/core/scene/common/editor/scene.ts +++ b/src/core/scene/common/editor/scene.ts @@ -1,8 +1,7 @@ import type { INode } from '../node'; import type { IComponentIdentifier } from '../component'; import type { IBaseIdentifier } from './base'; -import { IPrefabInfo, ITargetOverrideInfoForEditor } from '../prefab'; -import { IProperty } from '../../@types/public'; +import { IPrefabInfo } from '../prefab'; /** * 场景信息 @@ -13,18 +12,3 @@ export interface IScene extends IBaseIdentifier { children: INode[]; components: IComponentIdentifier[]; } - -export interface ISceneForEditor { - name: IProperty; - active: IProperty; - locked: IProperty; - _globals: Record; - isScene: boolean; - autoReleaseAssets: IProperty; - - uuid: IProperty; - children: IProperty[]; - parent: string; - __type__: string; - targetOverrides?: ITargetOverrideInfoForEditor[]; -} diff --git a/src/core/scene/common/node.ts b/src/core/scene/common/node.ts index a153b4a86..4171aeffd 100644 --- a/src/core/scene/common/node.ts +++ b/src/core/scene/common/node.ts @@ -1,10 +1,9 @@ import type { Node } from 'cc'; -import { IComponent, IComponentIdentifier, IRemovedComponentInfo, ISetPropertyOptionsForEditor } from './component'; +import { IComponent, IComponentIdentifier } from './component'; import { IVec3, IQuat } from './value-types'; import { IServiceEvents } from '../scene-process/service/core'; -import { IPrefabInfo, IPrefabStateInfo, ITargetOverrideInfoForEditor } from './prefab'; -import type { IProperty } from '../@types/public'; -import type { ISceneForEditor } from './editor/scene'; +import { IPrefabInfo, IPrefabStateInfo } from './prefab'; + // ====== Hierarchy tree types (for queryNodeTree) ====== export interface INodeTreeComponent { @@ -138,45 +137,6 @@ export interface INode extends INodeIdentifier { prefab: IPrefabInfo | null;// 是否是预制体 } -export interface IPrefabForEditor { - uuid: string; - fileId: string; - rootUuid: string; - sync: boolean; - prefabStateInfo: IPrefabStateInfo; - targetOverrides?: ITargetOverrideInfoForEditor[]; - instance?: IProperty; -} - -export interface INodeForEditor { - active: IProperty; - locked: IProperty; - name: IProperty; - position: IProperty; - - /** - * 此为 dump 数据,非 node.rotation - * 实际指向 node.eulerAngles - * rotation 为了给用户更友好的文案 - */ - rotation: IProperty; - mobility: IProperty; - - scale: IProperty; - layer: IProperty; - uuid: IProperty; - - children: IProperty[]; - parent: IProperty; - - __comps__: IProperty[]; - __type__: string; - __prefab__?: IPrefabForEditor; - _prefabInstance?: any; - removedComponents?: IRemovedComponentInfo[]; - mountedRoot?: string; -} - // 节点更新参数接口 export interface IUpdateNodeParams { path: string; @@ -256,15 +216,7 @@ export interface INodeEvents { 'node:removed': [Node, IChangeNodeOptions]; } -export interface IPublicNodeService extends Omit { } +export interface IPublicNodeService extends Omit {} /** * 节点的相关处理接口 @@ -274,155 +226,32 @@ export interface INodeService extends IServiceEvents { * 创建节点 * @param params */ - createByType(params: ICreateByNodeTypeParams): Promise; + createNodeByType(params: ICreateByNodeTypeParams): Promise; /** * 创建节点 * @param params */ - createByAsset(params: ICreateByAssetParams): Promise; + createNodeByAsset(params: ICreateByAssetParams): Promise; /** * 删除节点 - * @param params + * @param params */ - delete(params: IDeleteNodeParams): Promise; + deleteNode(params: IDeleteNodeParams): Promise; /** * 更新节点 * @param params */ - update(params: IUpdateNodeParams): Promise; + updateNode(params: IUpdateNodeParams): Promise; /** - * 查询节点信息 - * - 不传参数时,返回当前场景的 dump 数据(ISceneForEditor) - * - 传入 string 时,返回指定路径节点的 dump 数据(编辑器模式,返回 INodeForEditor 数据) - * - 传入 IQueryNodeParams 时,返回 INode(CLI 模式) - * - * @param params - 查询选项、节点路径字符串或不传 - * @returns 如果传入 IQueryNodeParams 返回 INode,如果传入 string 或不传返回 INodeForEditor 或 ISceneForEditor,未找到返回 null - */ - query(params?: IQueryNodeParams | string): Promise; + * 查询节点 + */ + queryNode(params: IQueryNodeParams): Promise; /** * 查询节点树(层级管理器格式) */ queryNodeTree(params: IQueryNodeTreeParams): Promise; - - // ---- 编辑器相关接口 ---- - - /** - * 预览设置节点属性,临时应用属性变更但不记录到 undo 栈 - * 用于编辑器中拖拽滑块等实时预览场景,首次调用时会缓存原始值, - * 可通过 cancelPreviewSetProperty 恢复 - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.path - 属性路径,如 'position'、'scale' - * @param options.dump - 属性的 dump 数据 - * @returns 设置成功返回 true,节点或属性路径无效返回 false - * - * @example - * ```ts - * // 预览修改节点位置 - * await previewSetProperty({ - * nodePath: 'Canvas/MyNode', - * path: 'position', - * dump: { value: { x: 100, y: 200, z: 0 }, type: 'cc.Vec3' }, - * }); - * ``` - */ - previewSetProperty(options: ISetPropertyOptionsForEditor): Promise; - - /** - * 取消预览设置,将节点属性恢复到 previewSetProperty 调用前的值 - * 仅使用 options.nodePath 和 options.path,options.dump 不会被使用 - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.path - 属性路径 - * @returns 恢复成功返回 true,无缓存的预览数据或节点无效返回 false - */ - cancelPreviewSetProperty(options: ISetPropertyOptionsForEditor): Promise; - - /** - * 设置节点属性,会记录到 undo 栈 - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.path - 属性路径,如 'position'、'rotation'、'layer' - * @param options.dump - 属性的 dump 数据 - * @returns 设置成功返回 true,节点不存在返回 false - * - * @example - * ```ts - * await setProperty({ - * nodePath: 'Canvas/MyNode', - * path: 'position', - * dump: { value: { x: 100, y: 200, z: 0 }, type: 'cc.Vec3' }, - * }); - * ``` - */ - setProperty(options: ISetPropertyOptionsForEditor): Promise; - - /** - * 重置节点的变换属性(position、rotation、scale、mobility)到默认值 - * - * @param path - 节点路径 - * @returns 重置成功返回 true,节点不存在返回 false - */ - reset(path: string): Promise; - - /** - * 重置节点的单个属性到 CCClass 定义的默认值 - * 仅使用 options.nodePath 和 options.path,options.dump 不会被使用 - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.path - 属性路径,如 'position'、'scale' - * @returns 重置成功返回 true,节点不存在返回 false - */ - resetProperty(options: ISetPropertyOptionsForEditor): Promise; - - /** - * 将节点上值为 null 的属性初始化为默认实例 - * 当属性为 null 且有定义构造函数类型时,会创建该类型的新实例 - * 仅使用 options.nodePath 和 options.path,options.dump 不会被使用 - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.path - 属性路径 - * @returns 初始化成功返回 true,节点不存在返回 false - * - * @example - * ```ts - * // 将节点上值为 null 的自定义属性初始化 - * await updatePropertyFromNull({ - * nodePath: 'Canvas/MyNode', - * path: 'customProperty', - * dump: {} as IProperty, - * }); - * ``` - */ - updatePropertyFromNull(options: ISetPropertyOptionsForEditor): Promise; - - /** - * 设置节点及其所有子节点的 layer 属性 - * 递归将相同的 layer 值应用到整个节点子树 - * 仅使用 options.nodePath 和 options.dump,options.path 不会被使用(内部固定为 'layer') - * - * @param options - 设置属性选项 - * @param options.nodePath - 节点路径 - * @param options.dump - layer 属性的 dump 数据 - * - * @example - * ```ts - * await setNodeAndChildrenLayer({ - * nodePath: 'Canvas/MyNode', - * path: 'layer', - * dump: { value: 1 << 25, type: 'Enum' }, - * }); - * ``` - */ - setNodeAndChildrenLayer(options: ISetPropertyOptionsForEditor): Promise; } /// diff --git a/src/core/scene/common/prefab/prefab-info.ts b/src/core/scene/common/prefab/prefab-info.ts index de52e86d8..ce23c3990 100644 --- a/src/core/scene/common/prefab/prefab-info.ts +++ b/src/core/scene/common/prefab/prefab-info.ts @@ -48,14 +48,6 @@ export interface ITargetOverrideInfo { targetInfo: ITargetInfo | null; } -export interface ITargetOverrideInfoForEditor { - source: string; - sourceInfo?: string[]; - propertyPath: string[]; - target: string; - targetInfo?: string[]; -} - export interface IPrefab { name: string; uuid: string; diff --git a/src/core/scene/main-process/index.ts b/src/core/scene/main-process/index.ts index c2192d788..2312ff23c 100644 --- a/src/core/scene/main-process/index.ts +++ b/src/core/scene/main-process/index.ts @@ -20,13 +20,12 @@ export interface IMainModule { export const Scene = { ...EditorProxy, ...ScriptProxy, + ...NodeProxy, + ...ComponentProxy, ...AssetProxy, ...EngineProxy, ...PrefabProxy, - // 节点相关的接口 - Node: NodeProxy, - // 组件相关的接口 - Component: ComponentProxy, + // 场景进程 worker: sceneWorker, }; diff --git a/src/core/scene/main-process/proxy/component-proxy.ts b/src/core/scene/main-process/proxy/component-proxy.ts index 629ede935..7eafbc498 100644 --- a/src/core/scene/main-process/proxy/component-proxy.ts +++ b/src/core/scene/main-process/proxy/component-proxy.ts @@ -6,28 +6,55 @@ import { IQueryComponentOptions, ISetPropertyOptions, IPublicComponentService, + IExecuteComponentMethodOptions, + IQueryClassesOptions, } from '../../common'; +import { IProperty } from '../../@types/public'; import { Rpc } from '../rpc'; export const ComponentProxy: IPublicComponentService = { - add(params: IAddComponentOptions): Promise { - return Rpc.getInstance().request('Component', 'add', [params]); + addComponent(params: IAddComponentOptions): Promise { + return Rpc.getInstance().request('Component', 'addComponent', [params]); }, - remove(params: IRemoveComponentOptions): Promise { - return Rpc.getInstance().request('Component', 'remove', [params]); + createComponent(params: IAddComponentOptions): Promise { + return Rpc.getInstance().request('Component', 'createComponent', [params]); }, - query(params: IQueryComponentOptions): Promise { - return Rpc.getInstance().request('Component', 'query', [params]); + removeComponent(params: IRemoveComponentOptions): Promise { + return Rpc.getInstance().request('Component', 'removeComponent', [params]); + }, + + queryComponent(params: IQueryComponentOptions): Promise { + return Rpc.getInstance().request('Component', 'queryComponent', [params]); }, setProperty(params: ISetPropertyOptions): Promise { return Rpc.getInstance().request('Component', 'setProperty', [params]); }, - queryAll(): Promise { - return Rpc.getInstance().request('Component', 'queryAll'); + queryAllComponent(): Promise { + return Rpc.getInstance().request('Component', 'queryAllComponent'); + }, + + queryClasses(options?: IQueryClassesOptions): Promise<{ name: string }[]> { + return Rpc.getInstance().request('Component', 'queryClasses', [options]); + }, + + queryComponentFunctionOfNode(uuid: string): Promise { + return Rpc.getInstance().request('Component', 'queryComponentFunctionOfNode', [uuid]); }, + + queryComponentHasScript(name: string): Promise { + return Rpc.getInstance().request('Component', 'queryComponentHasScript', [name]); + }, + + resetComponent(params: IQueryComponentOptions): Promise { + return Rpc.getInstance().request('Component', 'resetComponent', [params]); + }, + + executeComponentMethod(params: IExecuteComponentMethodOptions): Promise { + return Rpc.getInstance().request('Component', 'executeComponentMethod', [params]); + } }; diff --git a/src/core/scene/main-process/proxy/node-proxy.ts b/src/core/scene/main-process/proxy/node-proxy.ts index 051bb1188..0bc9779ec 100644 --- a/src/core/scene/main-process/proxy/node-proxy.ts +++ b/src/core/scene/main-process/proxy/node-proxy.ts @@ -1,6 +1,5 @@ import { INode, - INodeForEditor, INodeTreeItem, ICreateByNodeTypeParams, ICreateByAssetParams, @@ -12,26 +11,25 @@ import { IDeleteNodeResult, IPublicNodeService, } from '../../common'; -import { type ISceneForEditor } from '../../common/editor/scene'; import { Rpc } from '../rpc'; export const NodeProxy: IPublicNodeService = { - createByType(params: ICreateByNodeTypeParams): Promise { - return Rpc.getInstance().request('Node', 'createByType', [params]); + createNodeByType(params: ICreateByNodeTypeParams): Promise { + return Rpc.getInstance().request('Node', 'createNodeByType', [params]); }, - createByAsset(params: ICreateByAssetParams): Promise { - return Rpc.getInstance().request('Node', 'createByAsset', [params]); + createNodeByAsset(params: ICreateByAssetParams): Promise { + return Rpc.getInstance().request('Node', 'createNodeByAsset', [params]); }, - delete(params: IDeleteNodeParams): Promise { - return Rpc.getInstance().request('Node', 'delete', [params]); + deleteNode(params: IDeleteNodeParams): Promise { + return Rpc.getInstance().request('Node', 'deleteNode', [params]); }, - update(params: IUpdateNodeParams): Promise { - return Rpc.getInstance().request('Node', 'update', [params]); + updateNode(params: IUpdateNodeParams): Promise { + return Rpc.getInstance().request('Node', 'updateNode', [params]); }, - query(params?: IQueryNodeParams | string): Promise { - return Rpc.getInstance().request('Node', 'query', [params]); + queryNode(params: IQueryNodeParams): Promise { + return Rpc.getInstance().request('Node', 'queryNode', [params]); }, queryNodeTree(params: IQueryNodeTreeParams): Promise { return Rpc.getInstance().request('Node', 'queryNodeTree', [params]); - }, + } }; diff --git a/src/core/scene/scene-process/service/component.ts b/src/core/scene/scene-process/service/component.ts index 27c04301e..0a3d947c3 100644 --- a/src/core/scene/scene-process/service/component.ts +++ b/src/core/scene/scene-process/service/component.ts @@ -22,6 +22,7 @@ import { hasOneKindOfComponent } from './node/node-utils'; import { isEditorNode } from './node/node-utils'; import { createShouldHideInHierarchyCanvasNode } from './node/node-create'; import PrefabService from './prefab'; +import { IProperty } from '../../@types/public'; const NodeMgr = EditorExtends.Node; enum SceneModeType { @@ -56,7 +57,15 @@ export class ComponentService extends BaseService implements I public modeName: SceneModeType = SceneModeType.General; // private _stagingCameraInfo: any; protected _sceneEventListener: ISceneEvents[] = []; - + protected _recycleComponent: Record = {}; + + constructor() { + super(); + compMgr.on('add', this.onAddComponent.bind(this)); + compMgr.on('remove', this.onRemoveComponent.bind(this)); + compMgr.on('added', this.onComponentAdded.bind(this)); + compMgr.on('removed', this.onComponentRemoved.bind(this)); + } /** * 查询当前正在编辑的模式名字 @@ -84,7 +93,9 @@ export class ComponentService extends BaseService implements I opts.modeName = this.modeName; // TODO(qgh): 发送消息 //this.dispatchEvents('onComponentAdded', comp, opts); - compMgr.addRecycleComponent(comp.uuid); + if (this._recycleComponent[comp.uuid]) { + delete this._recycleComponent[comp.uuid]; + } } public onComponentRemoved(comp: Component, opts: IOptionBase = {}) { @@ -93,7 +104,7 @@ export class ComponentService extends BaseService implements I // this.dispatchEvents('onComponentRemoved', comp); // 编辑器中的this._sceneProxy.getRootNode()实现返回的是null PrefabService.onComponentRemovedInGeneralMode(comp, null); - compMgr.removeRecycleComponent(comp.uuid, comp); + this._recycleComponent[comp.uuid] = comp; } public dispatchEvents(eventName: keyof ISceneEvents, ...args: any[any]) { @@ -105,10 +116,10 @@ export class ComponentService extends BaseService implements I }); } - private async addImpl(nodePath: string, component: string): Promise { - const node = NodeMgr.getNodeByPath(nodePath); + private async addComponentImpl(nodePathOrUuid: string, component: string): Promise { + const node = NodeMgr.getNodeByPath(nodePathOrUuid) ?? NodeMgr.getNode(nodePathOrUuid); if (!node) { - throw new Error(`add component failed: ${nodePath} does not exist`); + throw new Error(`add component failed: ${nodePathOrUuid} does not exist`); } if (!component || component.length <= 0) { throw new Error(`add component failed: ${component} does not exist`); @@ -202,10 +213,10 @@ export class ComponentService extends BaseService implements I return dumpUtil.dumpComponent(comp as Component); } - async add(params: IAddComponentOptions): Promise { + async addComponent(params: IAddComponentOptions): Promise { try { await Service.Editor.lock(); - return await this.addImpl(params.nodePath, params.component); + return await this.addComponentImpl(params.nodePathOrUuid, params.component); } catch (error) { console.error(error); throw error; @@ -222,17 +233,17 @@ export class ComponentService extends BaseService implements I // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type private requireComponentList: Function[] = []; - async create(params: IAddComponentOptions): Promise { + async createComponent(params: IAddComponentOptions): Promise { if (Array.isArray(params.component)) { params.component.forEach((id) => { - this.create({ nodePath: params.nodePath, component: id }); + this.createComponent({ nodePathOrUuid: params.nodePathOrUuid, component: id }); }); console.warn('don\'t add component to more than one node at one time'); return false; } - const node = NodeMgr.getNodeByPath(params.nodePath); + const node = NodeMgr.getNodeByPath(params.nodePathOrUuid) ?? NodeMgr.getNode(params.nodePathOrUuid); if (!node) { - console.warn(`create component failed: ${params.nodePath} does not exist`); + console.warn(`create component failed: ${params.nodePathOrUuid} does not exist`); return false; } @@ -357,7 +368,7 @@ export class ComponentService extends BaseService implements I } } - async remove(params: IRemoveComponentOptions): Promise { + async removeComponent(params: IRemoveComponentOptions): Promise { try { await Service.Editor.lock(); @@ -366,7 +377,7 @@ export class ComponentService extends BaseService implements I throw new Error(`Remove component failed: ${params.path} does not exist`); } - this.emit('component:before-remove-component', comp); + this.emit('component:before-remove', comp); const result = compMgr.removeComponent(comp); // 需要立刻执行removeComponent操作,否则会延迟到下一帧 cc.Object._deferredDestroy(); @@ -381,7 +392,7 @@ export class ComponentService extends BaseService implements I } } - async queryImpl(params: IQueryComponentOptions, isEditor: boolean = false): Promise { + async queryComponentImpl(params: IQueryComponentOptions, isEditor: boolean = false): Promise { const comp = await this.findComponent(params.path); if (!comp) { console.warn(`Query component failed: ${params.path} does not exist`); @@ -394,11 +405,11 @@ export class ComponentService extends BaseService implements I } } - async query(params: IQueryComponentOptions | string): Promise { + async queryComponent(params: IQueryComponentOptions | string): Promise { if (typeof params === 'string') { - return this.queryImpl({ path: params }, true); + return this.queryComponentImpl({ path: params }, true); } else { - return this.queryImpl(params); + return this.queryComponentImpl(params); } } @@ -415,7 +426,7 @@ export class ComponentService extends BaseService implements I } async setProperty(options: ISetPropertyOptions | ISetPropertyOptionsForEditor): Promise { - if ('nodePath' in options) { + if ('uuid' in options) { return await this.setPropertyForEditor(options as ISetPropertyOptionsForEditor); } else { return await this.setPropertyForCli(options); @@ -427,7 +438,7 @@ export class ComponentService extends BaseService implements I * @param {*} uuid * @return {cc.Node} */ - queryNode(uuid: string | undefined): Node | null { + query(uuid: string | undefined): Node | null { if (typeof uuid === 'undefined') { return null; } @@ -438,10 +449,10 @@ export class ComponentService extends BaseService implements I async setPropertyForEditor(options: ISetPropertyOptionsForEditor): Promise { // 多个节点更新值 - if (Array.isArray(options.nodePath)) { + if (Array.isArray(options.uuid)) { try { - for (let i = 0; i < options.nodePath.length; i++) { - await this.setPropertyForEditor({ nodePath: options.nodePath[i], path: options.path, dump: options.dump, record: options?.record }); + for (let i = 0; i < options.uuid.length; i++) { + await this.setPropertyForEditor({ uuid: options.uuid[i], path: options.path, dump: options.dump, record: options?.record }); } return true; } catch (e) { @@ -449,9 +460,9 @@ export class ComponentService extends BaseService implements I return false; } } - const node = NodeMgr.getNodeByPath(options.nodePath); + const node = this.query(options.uuid); if (!node) { - console.warn(`Set property failed: ${options.nodePath} does not exist`); + console.warn(`Set property failed: ${options.uuid} does not exist`); return false; } @@ -513,7 +524,7 @@ export class ComponentService extends BaseService implements I return true; } - async queryAll(): Promise { + async queryAllComponent(): Promise { const keys = Object.keys(cc.js._registeredClassNames); const components: string[] = []; keys.forEach((key) => { @@ -527,7 +538,7 @@ export class ComponentService extends BaseService implements I return components; } - async hasScript(name: string): Promise { + async queryComponentHasScript(name: string): Promise { const classes = await this.queryClasses(); return classes.some((cls) => cls.name === name); } @@ -563,8 +574,8 @@ export class ComponentService extends BaseService implements I return classes; } - async queryFunctionOfNode(path: string): Promise { - const node = NodeMgr.getNodeByPath(path); + async queryComponentFunctionOfNode(uuid: string): Promise { + const node = NodeMgr.getNode(uuid); if (!node) { return {}; } @@ -576,8 +587,8 @@ export class ComponentService extends BaseService implements I } private readonly CompMgrEventHandlers = { - ['add']: 'onCompAdd', - ['remove']: 'onCompRemove', + ['add']: 'add', + ['remove']: 'remove', } as const; private compMgrEventHandlers = new Map void>(); /** @@ -607,7 +618,7 @@ export class ComponentService extends BaseService implements I * @param {String} uuid * @param {cc.Component} component */ - onCompAdd(uuid: string, component: Component) { + add(uuid: string, component: Component) { if (isEditorNode(component.node)) { return; } @@ -619,7 +630,7 @@ export class ComponentService extends BaseService implements I * @param {String} uuid * @param {cc.Component} component */ - onCompRemove(uuid: string, component: Component) { + remove(uuid: string, component: Component) { if (isEditorNode(component.node)) { return; } @@ -630,7 +641,7 @@ export class ComponentService extends BaseService implements I * 重置组件 * @param uuid component 的 uuid */ - public async reset(params: IQueryComponentOptions): Promise { + public async resetComponent(params: IQueryComponentOptions): Promise { try { const comp = await this.findComponent(params.path); if (!comp) { @@ -651,11 +662,7 @@ export class ComponentService extends BaseService implements I } } - public async executeMethod(options: IExecuteComponentMethodOptions): Promise { - const comp = compMgr.queryFromPath(options.path); - if (!comp) { - return null; - } - return await compMgr.executeComponentMethod(comp.uuid, options.name, options.args); + public async executeComponentMethod(options: IExecuteComponentMethodOptions): Promise { + return await compMgr.executeComponentMethod(options.uuid, options.name, options.args); } } diff --git a/src/core/scene/scene-process/service/component/index.ts b/src/core/scene/scene-process/service/component/index.ts index cf2674866..8a8df9d81 100644 --- a/src/core/scene/scene-process/service/component/index.ts +++ b/src/core/scene/scene-process/service/component/index.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'events'; import dumpUtil from '../dump'; const { get } = require('lodash'); @@ -5,18 +6,9 @@ const CompMgr = EditorExtends.Component; import utils from './utils'; import { Component, MissingScript } from 'cc'; import { IProperty } from '../../../@types/public'; -import { IComponentIdentifier, type IComponentEvents } from '../../../common'; -import { ServiceEvents } from '../core/global-events'; - -export class CompManager { - protected _recycleComponent: Record = {}; - - emit(event: K, ...args: IComponentEvents[K]): void; - emit(event: string, ...args: any[]): void; - emit(event: string, ...args: any[]) { - ServiceEvents.emit(event, ...args); - } +import { IComponentIdentifier } from '../../../common'; +export class CompManager extends EventEmitter { init() { this.registerCompMgrEvents(); } @@ -57,7 +49,7 @@ export class CompManager { * @param {cc.Component} component */ add(uuid: string, component: Component) { - this.emit('component:added', component); + this.emit('added', component); } /** @@ -66,7 +58,7 @@ export class CompManager { * @param {cc.Component} component */ remove(uuid: string, component: Component) { - this.emit('component:removed', component); + this.emit('removed', component); } /** @@ -100,25 +92,6 @@ export class CompManager { return CompMgr.getPathFromUuid(uuid); } - addRecycleComponent(uuid: string) { - if (this._recycleComponent[uuid]) { - delete this._recycleComponent[uuid]; - } - } - - removeRecycleComponent(uuid: string, comp: Component) { - this._recycleComponent[comp.uuid] = comp; - } - - /** - * 在回收站中查询一个组件的实例 - * @param {*} uuid - * @returns {cc.Component} - */ - queryRecycle(uuid: string): Component | null { - return this._recycleComponent[uuid] ?? null; - } - /** * 获取所有在用的组件 */ @@ -139,12 +112,12 @@ export class CompManager { return false; } - this.emit('component:before-remove-component', component); + this.emit('before-remove-component', component); component.node.removeComponent(component); // 需要立刻执行removeComponent操作,否则会延迟到下一帧 cc.Object._deferredDestroy(); - this.emit('component:remove', component); + this.emit('remove', component); return true; } @@ -264,7 +237,7 @@ export class CompManager { return; } - this.emit('component:add', component); + this.emit('add', component); // 一些组件在添加的时候,需要执行部分特殊的逻辑 if (component.constructor && (utils.addComponentMap as any)[component.constructor.name]) { diff --git a/src/core/scene/scene-process/service/core/base-service.ts b/src/core/scene/scene-process/service/core/base-service.ts index f0c4cf44d..4a7860be5 100644 --- a/src/core/scene/scene-process/service/core/base-service.ts +++ b/src/core/scene/scene-process/service/core/base-service.ts @@ -26,9 +26,10 @@ export interface IServiceEvents { onSetPropertyComponent?(comp: Component): void; onComponentAdded?(comp: Component): void; onComponentRemoved?(comp: Component): void; - onBeforeChangeComponent?(node: Node): void; - onBeforeAddComponent?(name:string, node: Node): void; onBeforeRemoveComponent?(comp: Component): void; + onComponentBeforeChanged?(node: Node): void; + onBeforeComponentAdded?(name:string, node: Node): void; + onComponentChanged?(name:string, opts: IChangeNodeOptions): void; // Asset events onAssetDeleted?(uuid: string): void; diff --git a/src/core/scene/scene-process/service/dump/decode.ts b/src/core/scene/scene-process/service/dump/decode.ts index 6917eeb95..aeb2d03ce 100644 --- a/src/core/scene/scene-process/service/dump/decode.ts +++ b/src/core/scene/scene-process/service/dump/decode.ts @@ -2,72 +2,14 @@ declare const cc: any; -import { ccClassAttrPropertyDefaultValue, getDefault, getTypeInheritanceChain, getTypeName, parsingPath } from './utils'; +import { ccClassAttrPropertyDefaultValue, getDefault, getTypeInheritanceChain, parsingPath } from './utils'; import lodash from 'lodash'; const { get, set } = lodash; import { DumpDefines } from './dump-defines'; -import { Component, editorExtrasTag, Node, Vec3, MobilityMode, Prefab, Quat, assetManager, Animation } from 'cc'; -import { promisify } from 'util'; -import { IComponentForEditor, INodeForEditor, ISceneForEditor, ITargetOverrideInfoForEditor } from '../../../common'; -import compMgr from './../component/index'; -import nodeMgr from './../node/index'; -import { IProperty } from '../../../@types/public'; - - - -type TargetOverrideInfo = Prefab._utils.TargetOverrideInfo; -const TargetOverrideInfo = Prefab._utils.TargetOverrideInfo; -type TargetInfo = Prefab._utils.TargetInfo; -const TargetInfo = Prefab._utils.TargetInfo; -type PrefabInfo = Prefab._utils.PrefabInfo; -const PrefabInfo = Prefab._utils.PrefabInfo; - -function decodeChildren(children: any[], node: any) { - const dumpChildrenUuids: string[] = children.map((child: any) => child.value.uuid); - const nodeChildrenUuids: string[] = node.children.map((child: INodeForEditor) => child.uuid); - - /** - * 出于性能考虑,不去移动两个数组共有的节点 - * 移除在 node 中且不在 dump 中的 uuid - * 添加在 dump 中且不在 node 中的 uuid - * 按照 dump 中的顺序重新排列 - */ - nodeChildrenUuids.forEach((uuid: string) => { - // 删除不存在的节点 - if (!dumpChildrenUuids.includes(uuid)) { - const child = nodeMgr.query(uuid); - // 重要:过滤隐藏节点 或 无效节点 - if (!child || child.objFlags & cc.Object.Flags.HideInHierarchy) { - return; - } - child.parent = null; - } - }); - - dumpChildrenUuids.forEach((uuid: string, i: number) => { - const child = nodeMgr.query(uuid); - // 重要:过滤无效节点 - if (!child) { - return; - } - - // 重置对象状态位,后续应该提供还原的方法 - child.walk((node: Node) => { - node._objFlags &= cc.Object.Flags.PersistentMask; - node._objFlags &= (~cc.Object.Flags.Destroyed); - }); - - // 节点挂靠父级 - if (!nodeChildrenUuids.includes(uuid)) { - child.parent = node; - } - - // 按新的顺序排列 - child.setSiblingIndex(i); - }); -} - +import { Component, editorExtrasTag, Node, Vec3, MobilityMode } from 'cc'; +import { openSync } from 'fs'; +const NodeMgr = EditorExtends.Node; // 还原mountedRoot export function decodeMountedRoot(compOrNode: Node | Component, mountedRoot?: string) { @@ -77,7 +19,7 @@ export function decodeMountedRoot(compOrNode: Node | Component, mountedRoot?: st if (typeof mountedRoot === 'undefined') { return null; } - const mountedRootNode = nodeMgr.query(mountedRoot); + const mountedRootNode = NodeMgr.getNode(mountedRoot); if (mountedRootNode) { if (!compOrNode[editorExtrasTag]) { compOrNode[editorExtrasTag] = {}; @@ -90,305 +32,6 @@ export function decodeMountedRoot(compOrNode: Node | Component, mountedRoot?: st } } -// 差异还原节点上的组件 -async function decodeComponents(dumpComps: any, node: Node, excludeComps?: any) { - if (!dumpComps) { - // 容错处理 - return; - } - - // 用于判断 prefabNode 下的 component 复用 - const prefabFileIdToDumpComp: { [key: string]: any } = {}; - const dumpCompsUuids = dumpComps - .map((comp: any) => { - if (comp.value.uuid) { - if (comp.value.__prefab && comp.value.__prefab.value && comp.value.__prefab.value.fileId.value) { - prefabFileIdToDumpComp[comp.value.__prefab.value.fileId.value] = comp; - } - - return comp.value.uuid.value; - } - return ''; - }) - .filter(Boolean); - - const componentsUuids = node.components - .map((component: any) => { - if (excludeComps) { - // 需要 exclude 的 component,假装不在 node 上 - const compType = getTypeName(component.constructor); - if (excludeComps.includes(compType)) { - return ''; - } - } - - // 将 dumpComp 转为现有相同 fileId component 的配置,后面执行值覆盖 - if (component.__prefab && component.__prefab.fileId) { - const dumpComp = prefabFileIdToDumpComp[component.__prefab.fileId]; - if (dumpComp) { - const existIndex = dumpCompsUuids.indexOf(dumpComp.value.uuid.value); - if (existIndex !== -1) { - dumpCompsUuids.splice(existIndex, 1, component.uuid); - dumpComp.value.uuid.value = component.uuid; - } - } - } - - return component.uuid; - }) - .filter(Boolean); - - /** - * 删除现有在 node._compoennts 中但不在 dumpComps 中的 component - * 2次方: 次数限制的作用: - * 既能再次删除被依赖而不能被先删除的组件, - * 又能避免死循环 - */ - let maxLoopTimes = componentsUuids.length ** 2; - let i = componentsUuids.length - 1; - - do { - const compUuid = componentsUuids[i]; - - if (compUuid && !dumpCompsUuids.includes(compUuid)) { - // 删除失败会返回 false, 可能是组件被依赖,会下次再删 - if (compMgr.removeComponent(compUuid)) { - componentsUuids.splice(i, 1); - } else { - i--; - } - } else { - i--; - } - - maxLoopTimes--; - } while (componentsUuids.length !== 0 && maxLoopTimes); - - // 重要:当前帧执行删除,保障下面的排序逻辑和上面的删除处于同一帧 - cc.Object._deferredDestroy(); - - // 挂载上新的组件及调整组件的位置 - const components = node.components.slice(); // 下一步会清空,先缓存一份,以用于比较 - node['_components'].length = 0; // 先清空节点上的组件 - - for (let i = 0; i < dumpComps.length; i++) { - const dumpComp: IComponentForEditor = dumpComps[i]; - - if (!dumpComp.value || !dumpComp.value.uuid) { - continue; - } - - let component = components[i]; - - const compUuid = (dumpComp.value.uuid as IProperty).value as string; - let cacheComp = compMgr.query(compUuid); - - // 在用,查询没有 - if (!cacheComp) { - // 从 回收站 再查出来 - cacheComp = compMgr.queryRecycle(compUuid); - } - - if (cacheComp) { - // 有缓存 - if (component !== cacheComp) { - /** - * 新增场景:组件是从别的节点移过来的, - * 例如 prefab 从资源还原时,会先实例化一个临时节点,里面的组件会被移植过来 - */ - if (cacheComp.node !== node) { - _removeDependComponent(cacheComp); - } - - // 组件已被删除 - if (cacheComp.objFlags & cc.Object.Flags.Destroying || cacheComp.objFlags & cc.Object.Flags.Destroyed) { - // 57349 , 5 不会等于 128 - // 重置 component.objFlags 的状态是为了重新走组件的生命周期 - cacheComp.objFlags &= cc.Object.Flags.PersistentMask; - cacheComp.objFlags &= ~cc.Object.Flags.Destroyed; - - // 回收站的缓存机制是编辑器的,这里需要将组件从回收站还原 - // cce.Component.recycle(compUuid); - } - component = cacheComp; - } - nodeMgr.addComponentAt(node, component, i); // 插入新位置 - } - - // 编辑器预览时,undo时会设置clips导致动画停止播放 #15236 - // 记录上次播放的动画(因为可能不是默认clip),还原后再播放 - const playAnim: string[] = []; - // TODO(qgh):判断是否为预览进程 - // if (isPreviewProcess && dumpComp.type === 'cc.Animation') { - // const anim = component as Animation; - // anim.clips.map((clip) => anim.getState(clip?.name ?? '')) - // .filter((state: AnimationState) => state?.isPlaying) - // .forEach((state: AnimationState) => { playAnim.push(state.name); }); - // } - // 对于原先还在的组件,还原内部的值 - for (const key in dumpComp.value) { - await decodePatch(key, dumpComp.value[key], component); - } - - if (playAnim.length > 0) { - const anim = component as Animation; - playAnim.forEach((name: string) => { - anim.play(name); - }); - } - - // 还原mountedRoot - decodeMountedRoot(component, dumpComp.mountedRoot); - - // TODO: 不知道为啥这个方法是个protected的,应该改成public的 - // @ts-ignore - if (component && component.onRestore) { - // @ts-ignore - component.onRestore(); - } - } - - // 按依赖关系的顺序删除组件 - function _removeDependComponent(component: any) { - // 组件已被删除 - if (component.objFlags & cc.Object.Flags.Destroying || component.objFlags & cc.Object.Flags.Destroyed) { - // 57349 , 5 不会等于 128 - return; - } - - // 关系是 dependComponent 依赖 component - const dependComponent = component.node._getDependComponent(component); - dependComponent.forEach((dep: any) => { - _removeDependComponent(dep); - }); - - /** - * 需要立即执行 cc.Object._deferredDestroy() 动作 - */ - compMgr.removeComponent(component.uuid); - cc.Object._deferredDestroy(); - } -} - - -async function decodePrefab(dumpPrefab: any, node: any) { - // 不需要处理 - if (!dumpPrefab && !node['_prefab']) { - return; - } - - // 删除 - if (!dumpPrefab && node['_prefab']) { - node['_prefab'] = null; - return; - } - - // 新增 - const info = new PrefabInfo(); - const root = nodeMgr.query(dumpPrefab.rootUuid); - info.root = root ? root : node; - if (dumpPrefab.uuid) { - try { - info.asset = await promisify(assetManager.loadAny)(dumpPrefab.uuid); - } catch (e) { - console.error(e); - info.asset = new Prefab(); - info.asset.initDefault(dumpPrefab.uuid); - } - } - info.fileId = dumpPrefab.fileId || node.uuid; - if (dumpPrefab.instance) { - await decodePatch('instance', dumpPrefab.instance, info); - - } else { - info.instance = undefined; - } - - if (dumpPrefab.targetOverrides) { - info.targetOverrides = decodeTargetOverrides(dumpPrefab.targetOverrides); - } else { - info.targetOverrides = undefined; - } - - node['_prefab'] = info; -} - -/** - * 解码一个场景 dump 数据 - * @param dump - * @param scene - */ -export async function decodeScene(dump: ISceneForEditor, scene?: any) { - if (!dump) { - return; - } - scene = scene || new cc.Scene(); - scene.name = dump.name.value; - scene.active = dump.active.value; - if (dump.children) { - decodeChildren(dump.children, scene); - } - - for (const key of Object.keys(dump._globals)) { - await decodePatch(`_globals.${key}`, dump._globals[key], scene); - } - - if (dump.targetOverrides) { - if (!scene['_prefab']) { - scene['_prefab'] = new cc._PrefabInfo(); - } - scene['_prefab'].targetOverrides = decodeTargetOverrides(dump.targetOverrides); - } else { - scene['_prefab'] = undefined; - } -} - -/** - * 解码一个 dump 数据 - * @param dump - * @param node - */ -export async function decodeNode(dump: INodeForEditor, node?: Node, excludeComps?: any) { - if (!dump) { - return null; - } - - node = node || new cc.Node(); - - if (!node) { - return null; - } - - // 先还原prefab的相关信息,因为下面的属性设置会触发prefab的override - await decodePrefab(dump.__prefab__, node); - - node.name = dump.name.value as string; - node.active = dump.active.value as boolean; - node.layer = dump.layer.value as number; - node.mobility = dump.mobility.value as number; - node.setPosition(dump.position.value as Vec3); - const quat = new Quat(); - const vec3 = dump.rotation.value as Vec3; - Quat.fromEuler(quat, vec3.x, vec3.y, vec3.z); - node.setRotation(quat); - node.setScale(dump.scale.value as Vec3); - - decodeMountedRoot(node, dump.mountedRoot); - - if (dump.parent && dump.parent.value && dump.parent.value.uuid) { - node.parent = nodeMgr.query(dump.parent.value.uuid); - } else { - node.parent = null; - } - if (dump.children) { - decodeChildren(dump.children, node); - } - - await decodeComponents(dump.__comps__, node, excludeComps); - - return node; -} - async function _decodeByType(type: string, node: any, info: any, dump: any, opts?: any) { const dumpType = DumpDefines[type]; @@ -679,38 +322,9 @@ export function updatePropertyFromNull(node: any, path: string) { } } -export function decodeTargetOverrides(dumpedTargetOverrides: ITargetOverrideInfoForEditor[]) { - const targetOverrides: TargetOverrideInfo[] = []; - dumpedTargetOverrides.forEach((itr: ITargetOverrideInfoForEditor) => { - const targetOverride = new TargetOverrideInfo(); - targetOverride.source = nodeMgr.query(itr.source); - if (itr.sourceInfo) { - const sourceInfo = new TargetInfo(); - sourceInfo.localID = itr.sourceInfo; - targetOverride.sourceInfo = sourceInfo; - } - - targetOverride.propertyPath = itr.propertyPath; - - targetOverride.target = nodeMgr.query(itr.target); - if (itr.targetInfo) { - const targetInfo = new TargetInfo(); - targetInfo.localID = itr.targetInfo; - targetOverride.targetInfo = targetInfo; - } - - targetOverrides.push(targetOverride); - }); - - return targetOverrides; -} - export default { - decodeScene, - decodeNode, decodePatch, resetProperty, updatePropertyFromNull, decodeMountedRoot, - decodeTargetOverrides, }; diff --git a/src/core/scene/scene-process/service/dump/encode.ts b/src/core/scene/scene-process/service/dump/encode.ts index b2fc72da0..5c922c53e 100644 --- a/src/core/scene/scene-process/service/dump/encode.ts +++ b/src/core/scene/scene-process/service/dump/encode.ts @@ -7,205 +7,10 @@ import dumpUtil from './utils'; import { DumpDefines } from './dump-defines'; import { IProperty } from '../../../@types/public'; -import { IComponent, IComponentForEditor, INodeForEditor, ISceneForEditor, ITargetOverrideInfoForEditor } from '../../../common'; +import { IComponent, IComponentForEditor } from '../../../common'; import compMgr from '../component/index'; import { prefabUtils } from './../prefab/utils'; import { Service } from './../core'; -import { MobilityMode, Node, Prefab, Component, js } from 'cc'; - -/** - * 编码一个 node 数据 - * @param node - */ -export function encodeNode(node: Node): INodeForEditor { - const ctor = node.constructor; - - const LayersEnumList = Object.keys(cc.Layers.Enum).map((key, index) => { - return { name: key, value: cc.Layers.Enum[key] }; - }); - LayersEnumList.sort((a, b) => { - return a.value - b.value; - }); - - const MobilityModeEnumList = Object.keys(MobilityMode).map((key, index) => { - return { name: key, value: MobilityMode[key as keyof typeof MobilityMode] }; - }); - - // FIXME: avoid using private field - // TODO:这里的需要知道当前场景是 2D 还是 3D - //const is2DProject = cce.SceneFacadeManager['_projectType'] === '2d'; - const is2DProject = true; - - const data: INodeForEditor = { - active: encodeObject(node.active, { displayName: 'Active', default: null }, node), - locked: encodeObject(Boolean(node.objFlags & cc.Object.Flags.LockedInEditor), { displayName: 'Locked', default: false, animatable: false }, node), - name: encodeObject(node.name, { displayName: 'Name', default: null, animatable: false }, node), - position: encodeObject( - node.position, - { - displayName: 'i18n:scene.cc.Node.properties.position.displayName', - default: new cc.math.Vec3(), - tooltip: 'i18n:scene.cc.Node.properties.position.tooltip', - }, - node, - 'position', - ), - rotation: encodeObject( - node.eulerAngles, - { - name: 'eulerAngles', - displayName: 'i18n:scene.cc.Node.properties.eulerAngles.displayName', - default: new cc.math.Vec3(), - tooltip: `i18n:scene.cc.Node.properties.eulerAngles.${is2DProject ? 'tooltip2D' : 'tooltip3D'}`, - }, - node, - is2DProject ? 'angle' : 'eulerAngles', - ), - scale: encodeObject( - node.scale, - { - displayName: 'i18n:scene.cc.Node.properties.scale.displayName', - default: new cc.math.Vec3(1, 1, 1), - tooltip: 'i18n:scene.cc.Node.properties.scale.tooltip', - }, - node, - 'scale', - ), - mobility: encodeObject( - node.mobility, - { - displayName: 'i18n:scene.cc.Node.properties.mobility.displayName', - tooltip: 'i18n:scene.cc.Node.properties.mobility.tooltip', - default: 0, - type: 'Enum', - enumList: MobilityModeEnumList, - }, - node, - 'mobility', - ), - layer: encodeObject( - node.layer, { - displayName: 'i18n:scene.cc.Node.properties.layer.displayName', - tooltip: 'i18n:scene.cc.Node.properties.layer.tooltip', - default: 1073741824, - type: 'Enum', - enumList: LayersEnumList, - readonly: false, - animatable: false, - }, - node, - 'layer', - ), - uuid: encodeObject(node.uuid, { displayName: 'UUID', default: null, animatable: false }, node), - - parent: encodeObject( - node.parent, - { - ctor: cc.Node, - }, - node, - ), - - children: node.children - .map((child: any) => { - if (!child || child.objFlags & cc.Object.Flags.HideInHierarchy) { - return; - } - - return encodeObject( - child, - { - ctor: cc.Node, - }, - node, - ); - }) - .filter((v): v is IProperty => !!v), - - __type__: dumpUtil.getTypeName(ctor), - __comps__: node['_components'].map((comp: any) => { - return encodeComponentForEditor(comp); - }), - - mountedRoot: prefabUtils.getMountedRoot(node)?.uuid, - }; - - if (node['_prefab']) { - const prefabStateInfo = prefabUtils.getPrefabStateInfo(node); - data.__prefab__ = { - uuid: (node['_prefab'].asset && node['_prefab'].asset._uuid) || '', - fileId: node['_prefab'].fileId, - rootUuid: (node['_prefab'].root && node['_prefab'].root.uuid) || '', - sync: true, - prefabStateInfo, - }; - - if (node['_prefab'].targetOverrides) { - data.__prefab__!.targetOverrides = encodeTargetOverrides(node['_prefab'].targetOverrides) ?? undefined; - } - - if (node['_prefab'].instance) { - data.__prefab__!.instance = encodeObject(node['_prefab'].instance, { default: null }, node); - } - - const removedComponents = prefabUtils.getRemovedComponents(node); - if (removedComponents.length > 0) { - data.removedComponents = removedComponents.map((comp: Component) => { - return { name: js.getClassName(comp), fileID: comp.__prefab!.fileId }; - }); - } - } - - // 根据 flag 调整 readyonly - _checkObjFlags(node, data); - - return data; -} - -/** - * 编码一个场景数据 - * @param scene - */ -export function encodeScene(scene: any): ISceneForEditor { - const ctor = scene.constructor; - - const data: ISceneForEditor = { - active: encodeObject(scene.active, { default: null }), - locked: encodeObject(false, { default: false }), - name: encodeObject(scene.name || ctor.name, { default: null }), - uuid: encodeObject(scene.uuid, { default: null }), - autoReleaseAssets: encodeObject(scene.autoReleaseAssets, { displayName: 'Auto Release Assets', default: false }), - children: scene.children - .map((child: any) => { - if (!child || child.objFlags & cc.Object.Flags.HideInHierarchy) { - return; - } - - return encodeObject(child, { - ctor: cc.Node, - }); - }) - .filter((v: any): v is IProperty => !!v), - parent: '', - __type__: dumpUtil.getTypeName(ctor), - _globals: {}, - isScene: true, - }; - - // 遍历 scene._globals 内所有属性 - if (scene._globals) { - scene._globals.constructor.__props__.map((key: string) => { - const attrs = cc.Class.attr(scene._globals.constructor, key); - data._globals[key] = encodeObject(scene._globals[key], attrs, scene._globals); - }); - } - - if (scene['_prefab']?.targetOverrides) { - data.targetOverrides = encodeTargetOverrides(scene['_prefab'].targetOverrides) ?? undefined; - } - - return data; -} /** * 编码一个 component @@ -401,71 +206,6 @@ function _encodeByType(type: string | undefined, object: any, data: IProperty, o return false; } -/** - * hack:处理 component 的 .objFlags 设置,需要传递给 node - * 比如 Canvas 的 IsPositionLocked 要传给 node,position.readonly = true - * 比如 Canvas 的 IsSizeLocked 要传给 UITransform, contentsize = true - * 暂时处理以下逻辑,后续可增删 - */ -function _checkObjFlags(node: any, data: INodeForEditor) { - let IsPositionLocked = false; - let IsSizeLocked = false; - let IsAnchorLocked = false; - let IsScaleLocked = false; - let IsRotationLocked = false; - node['_components'].forEach((component: any) => { - if (component.objFlags & cc.Object.Flags.IsPositionLocked) { - IsPositionLocked = true; - } - - if (component.objFlags & cc.Object.Flags.IsSizeLocked) { - IsSizeLocked = true; - } - - if (component.objFlags & cc.Object.Flags.IsAnchorLocked) { - IsAnchorLocked = true; - } - - if (component.objFlags & cc.Object.Flags.IsScaleLocked) { - IsScaleLocked = true; - } - - if (component.objFlags & cc.Object.Flags.IsRotationLocked) { - IsRotationLocked = true; - } - }); - - if (IsPositionLocked) { - data.position.readonly = true; - } - if (IsScaleLocked) { - data.scale.readonly = true; - } - - if (IsRotationLocked) { - data.rotation.readonly = true; - } - - const uiTransformComponents: any = []; - data.__comps__.forEach((comp: any) => { - if (comp.cid === 'cc.UITransform') { - uiTransformComponents.push(comp); - } - }); - - if (uiTransformComponents.length) { - if (IsSizeLocked) { - uiTransformComponents.forEach((comp: any) => { - comp.value.contentSize.readonly = true; - }); - } - if (IsAnchorLocked) { - uiTransformComponents.forEach((comp: any) => { - comp.value.anchorPoint.readonly = true; - }); - } - } -} /** * 编码一个对象 @@ -614,34 +354,7 @@ function getElementDefaultValueFromParentInitializer(parentInitializer: unknown) return null; } - -function encodeTargetOverrides(targetOverrides: any) { - if (!targetOverrides || targetOverrides.length <= 0) { - return null; - } - - const dumpedTargetOverrides: ITargetOverrideInfoForEditor[] = []; - targetOverrides.forEach((itr: Prefab._utils.TargetOverrideInfo) => { - if (!itr.source || !itr.target) { - return; - } - const dumpOverride = { - source: itr.source.uuid, - sourceInfo: itr.sourceInfo ? itr.sourceInfo.localID : undefined, - propertyPath: itr.propertyPath, - target: itr.target.uuid, - targetInfo: itr.targetInfo ? itr.targetInfo.localID : undefined, - }; - - dumpedTargetOverrides.push(dumpOverride); - }); - - return dumpedTargetOverrides; -} - export default { - encodeNode, - encodeScene, encodeComponent, encodeComponentForEditor, encodeObject, diff --git a/src/core/scene/scene-process/service/dump/index.ts b/src/core/scene/scene-process/service/dump/index.ts index 3f846ee60..c40b1845a 100644 --- a/src/core/scene/scene-process/service/dump/index.ts +++ b/src/core/scene/scene-process/service/dump/index.ts @@ -1,10 +1,10 @@ 'use strict'; -import { Node, Component, js, CCClass, Scene } from 'cc'; +import { Node, Component, js, CCClass } from 'cc'; import { parsingPath } from './utils'; import AssetUtil from './asset'; -import { decodePatch, decodeNode, decodeScene, resetProperty, updatePropertyFromNull } from './decode'; -import { encodeObject, encodeComponent, encodeComponentForEditor, encodeScene, encodeNode } from './encode'; -import { IComponent, IComponentForEditor, INodeForEditor, ISceneForEditor } from '../../../common'; +import { decodePatch, resetProperty, updatePropertyFromNull } from './decode'; +import { encodeObject, encodeComponent, encodeComponentForEditor } from './encode'; +import { IComponent, IComponentForEditor } from '../../../common'; // import * as dumpDecode from './decode'; const { get } = require('lodash'); @@ -25,21 +25,6 @@ class DumpUtil { return ret; } - /** - * 生成一个 node 的 dump 数据 - * @param {*} node - */ - dumpNode(node: Node): INodeForEditor | ISceneForEditor | null { - if (!node) { - return null; - } - if (node instanceof Scene) { - return encodeScene(node); - } - return encodeNode(node); - - } - // 生成一个component的dump数据 dumpComponent(comp: Component): IComponent; dumpComponent(comp: null | undefined): null; @@ -99,18 +84,6 @@ class DumpUtil { return updatePropertyFromNull(node, path); } - /** - * 还原一个节点的全部属性 - * @param {*} node - * @param {*} dump - */ - async restoreNode(node: Node, dump: any) { - if (dump && dump.isScene) { - return await decodeScene(dump, node); - } - return await decodeNode(dump, node); - } - /** * 解析节点的访问路径 * @param path diff --git a/src/core/scene/scene-process/service/node.ts b/src/core/scene/scene-process/service/node.ts index 86c5ee359..cbb2ba131 100644 --- a/src/core/scene/scene-process/service/node.ts +++ b/src/core/scene/scene-process/service/node.ts @@ -5,7 +5,6 @@ import { type IDeleteNodeParams, type IDeleteNodeResult, type INode, - type INodeForEditor, type INodeService, type IQueryNodeParams, type IQueryNodeTreeParams, @@ -16,20 +15,15 @@ import { NodeType, NodeEventType, EventSourceType, - IChangeNodeOptions, - ISetPropertyOptionsForEditor + IChangeNodeOptions } from '../../common'; -import { type ISceneForEditor } from '../../common/editor/scene'; import { Rpc } from '../rpc'; import { CCClass, CCObject, Node, Prefab, Quat, Vec3, TransformBit, UITransform, LODGroup } from 'cc'; import { createNodeByAsset, loadAny } from './node/node-create'; -import { getUICanvasNode, setLayer } from './node/node-utils'; +import { getUICanvasNode, isEditorNode, setLayer } from './node/node-utils'; import { sceneUtils } from './scene/utils'; import { prefabUtils } from './prefab/utils'; -import nodeMgr from './node/index'; import NodeConfig from './node/node-type-config'; -import { parseReadonlyDef } from 'zod-to-json-schema'; -import node from './node/index'; const NodeMgr = EditorExtends.Node; @@ -39,7 +33,7 @@ const NodeMgr = EditorExtends.Node; */ @register('Node') export class NodeService extends BaseService implements INodeService { - async createByType(params: ICreateByNodeTypeParams): Promise { + async createNodeByType(params: ICreateByNodeTypeParams): Promise { try { await Service.Editor.lock(); let canvasNeeded = params.canvasRequired || false; @@ -65,7 +59,7 @@ export class NodeService extends BaseService implements INodeServic } } - async createByAsset(params: ICreateByAssetParams): Promise { + async createNodeByAsset(params: ICreateByAssetParams): Promise { try { await Service.Editor.lock(); const assetUuid = await Rpc.getInstance().request('assetManager', 'queryUUID', [params.dbURL]); @@ -151,7 +145,7 @@ export class NodeService extends BaseService implements INodeServic resultNode.name = name; } if (checkUITransform) { - nodeMgr.ensureUITransformComponent(resultNode); + this.ensureUITransformComponent(resultNode); } // 发送添加节点事件,添加节点中的根节点 @@ -222,7 +216,7 @@ export class NodeService extends BaseService implements INodeServic // 设置父级 nextNode.setParent(currentParent); // 确保新创建的节点有必要的组件 - nodeMgr.ensureUITransformComponent(nextNode); + this.ensureUITransformComponent(nextNode); // 发送节点创建事件 this.emit('node:add', nextNode); @@ -237,7 +231,7 @@ export class NodeService extends BaseService implements INodeServic return currentParent; } - async delete(params: IDeleteNodeParams): Promise { + async deleteNode(params: IDeleteNodeParams): Promise { try { await Service.Editor.lock(); const root = Service.Editor.getRootNode(); @@ -251,7 +245,26 @@ export class NodeService extends BaseService implements INodeServic return null; } - nodeMgr.baseRemoveNode(node, params.keepWorldTransform); + // 发送节点修改消息 + const parent = node.parent; + this.emit('node:before-remove', node); + if (parent) { + this.emit('node:before-change', parent); + } + + node.setParent(null, params.keepWorldTransform); + node._objFlags |= CCObject.Flags.Destroyed; + // 3.6.1 特殊 hack,请在后续版本移除 + // 相关修复 pr: https://github.com/cocos/cocos-editor/pull/890 + try { + this._walkNode(node, (child: any) => { + child._objFlags |= CCObject.Flags.Destroyed; + }); + } catch (error) { + console.warn(error); + } + + this.emit('node:remove', node); return { path: path, @@ -264,7 +277,14 @@ export class NodeService extends BaseService implements INodeServic } } - async update(params: IUpdateNodeParams): Promise { + private _walkNode(node: Node, func: Function) { + node && node.children && node.children.forEach((child) => { + func(child); + this._walkNode(child, func); + }); + } + + async updateNode(params: IUpdateNodeParams): Promise { const updateOperate = () => { const node = NodeMgr.getNodeByPath(params.path); if (!node) { @@ -365,22 +385,7 @@ export class NodeService extends BaseService implements INodeServic } } - async query(params?: IQueryNodeParams | string): Promise { - if (params === undefined || params === null || typeof params === 'string') { - return this.queryForEditor(params); - } - return this.queryForCli(params); - } - - private async queryForEditor(path?: string): Promise { - const node = path ? NodeMgr.getNodeByPath(path) : Service.Editor.getRootNode(); - if (!node) { - return null; - } - return await nodeMgr.queryDump(node.uuid); - } - - private async queryForCli(params: IQueryNodeParams): Promise { + async queryNode(params: IQueryNodeParams): Promise { try { await Service.Editor.lock(); const root = Service.Editor.getRootNode(); @@ -462,6 +467,35 @@ export class NodeService extends BaseService implements INodeServic } } + /** + * 确保节点有 UITransform 组件 + * 目前只需保障在创建空节点的时候检查任意上级是否为 canvas + */ + ensureUITransformComponent(node: Node) { + if (node instanceof cc.Node && node.children.length === 0) { + // 空节点 + let inside = false; + let parent = node.parent; + + while (parent) { + const components = parent.components.map((comp) => cc.js.getClassName(comp.constructor)); + if (components.includes('cc.Canvas')) { + inside = true; + break; + } + parent = parent.parent; + } + + if (inside) { + try { + node.addComponent('cc.UITransform'); + } catch (error) { + console.error(error); + } + } + } + } + /** * 检查并根据需要创建 canvas节点或为父级添加UITransform组件,返回父级节点,如果需要canvas节点,则父级节点会是canvas节点 * @param workMode @@ -513,77 +547,200 @@ export class NodeService extends BaseService implements INodeServic const nodeMap = NodeMgr.getNodesInScene(); // 场景载入后要将现有节点监听所需事件 Object.keys(nodeMap).forEach((key) => { - nodeMgr.registerEventListeners(nodeMap[key]); + this.registerEventListeners(nodeMap[key]); }); - nodeMgr.registerNodeMgrEvents(); + this.registerNodeMgrEvents(); Service.Component.init(); } public onEditorClosed() { Service.Component.unregisterCompMgrEvents(); - nodeMgr.unregisterNodeMgrEvents(); + this.unregisterNodeMgrEvents(); const nodeMap = NodeMgr.getNodes(); Object.keys(nodeMap).forEach((key) => { - nodeMgr.unregisterEventListeners(nodeMap[key]); + this.unregisterEventListeners(nodeMap[key]); }); NodeMgr.clear(); EditorExtends.Component.clear(); } - public async previewSetProperty(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { - return false; + // ---------- + + private readonly NodeHandlers = { + [Node.EventType.TRANSFORM_CHANGED]: 'onNodeTransformChanged', + [Node.EventType.SIZE_CHANGED]: 'onNodeSizeChanged', + [Node.EventType.ANCHOR_CHANGED]: 'onNodeAnchorChanged', + [Node.EventType.CHILD_ADDED]: 'onNodeParentChanged', + [Node.EventType.CHILD_REMOVED]: 'onNodeParentChanged', + [Node.EventType.LIGHT_PROBE_CHANGED]: 'onLightProbeChanged', + } as const; + private nodeHandlers = new Map(); + + /** + * 监听引擎发出的 node 事件 + * @param {*} node + */ + registerEventListeners(node: Node) { + if (!node || !node.isValid || isEditorNode(node)) { + return; } - return await nodeMgr.previewSetNodeProperty(node.uuid, options.path, options.dump); + + // 遍历事件映射表,统一注册事件 + Object.entries(this.NodeHandlers).forEach(([eventType, handlerName]) => { + const boundHandler = (this as any)[handlerName].bind(this, node); + node.on(eventType, boundHandler, this); + this.nodeHandlers.set(`${eventType}_${node.uuid}`, boundHandler); + }); } - public async cancelPreviewSetProperty(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { - return false; + /** + * 取消监听引擎发出的node事件 + * @param {*} node + */ + unregisterEventListeners(node: Node) { + if (!node || !node.isValid || isEditorNode(node)) { + return; } - return await nodeMgr.cancelPreviewSetNodeProperty(node.uuid, options.path); + + // 遍历事件映射表,统一取消事件 + Object.keys(this.NodeHandlers).forEach(eventType => { + const key = `${eventType}_${node.uuid}`; + const handler = this.nodeHandlers.get(key); + if (handler) { + node.off(eventType, handler); + this.nodeHandlers.delete(key); + } + }); + } + + private readonly NodeMgrEventHandlers = { + ['add']: 'add', + ['change']: 'change', + ['remove']: 'remove', + } as const; + private nodeMgrEventHandlers = new Map void>(); + /** + * 注册引擎 Node 管理相关事件的监听 + */ + registerNodeMgrEvents() { + this.unregisterNodeMgrEvents(); + Object.entries(this.NodeMgrEventHandlers).forEach(([eventType, handlerName]) => { + const handler = (this as any)[handlerName].bind(this); + NodeMgr.on(eventType, handler); + this.nodeMgrEventHandlers.set(eventType, handler); + // console.log(`NodeMgr on ${eventType}`); + }); } - public async setProperty(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { - return false; + unregisterNodeMgrEvents() { + for (const eventType of this.nodeMgrEventHandlers.keys()) { + const handler = this.nodeMgrEventHandlers.get(eventType); + if (handler) { + NodeMgr.off(eventType, handler); + this.nodeMgrEventHandlers.delete(eventType); + // console.log(`NodeMgr off ${eventType}`); + } } - return await nodeMgr.setProperty(node.uuid, options.path, options.dump); } - public async reset(path: string): Promise { - const node = NodeMgr.getNodeByPath(path); - if (!node) { - return false; + onNodeTransformChanged(node: Node, transformBit: TransformBit) { + const changeOpts: IChangeNodeOptions = { type: NodeEventType.TRANSFORM_CHANGED, source: EventSourceType.ENGINE }; + + switch (transformBit) { + case Node.TransformBit.POSITION: + changeOpts.propPath = 'position'; + break; + case Node.TransformBit.ROTATION: + changeOpts.propPath = 'rotation'; + break; + case Node.TransformBit.SCALE: + changeOpts.propPath = 'scale'; + break; } - return await nodeMgr.resetNode(node.uuid); + + this.emit('node:change', node, changeOpts); } - public async resetProperty(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { - return false; + onNodeSizeChanged(node: Node) { + const changeOpts: IChangeNodeOptions = { type: NodeEventType.SIZE_CHANGED, source: EventSourceType.ENGINE }; + const uiTransform = node.getComponent(UITransform); + if (uiTransform) { + const index = node.components.indexOf(uiTransform); + changeOpts.propPath = `_components.${index}.contentSize`; } - return await nodeMgr.resetProperty(node.uuid, options.path); + this.emit('node:change', node, changeOpts); } - public async updatePropertyFromNull(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { - return false; + onNodeAnchorChanged(node: Node) { + const changeOpts: IChangeNodeOptions = { type: NodeEventType.ANCHOR_CHANGED, source: EventSourceType.ENGINE }; + const uiTransform = node.getComponent(UITransform); + if (uiTransform) { + const index = node.components.indexOf(uiTransform); + changeOpts.propPath = `_components.${index}.anchorPoint`; } - return await nodeMgr.updatePropertyFromNull(node.uuid, options.path); + this.emit('node:change', node, changeOpts); } - public async setNodeAndChildrenLayer(options: ISetPropertyOptionsForEditor): Promise { - const node = NodeMgr.getNodeByPath(options.nodePath); - if (!node) { + onNodeParentChanged(parent: Node, child: Node) { + if (isEditorNode(child)) { return; } - return await nodeMgr.setNodeAndChildrenLayer(node.uuid, options.dump); + + this.emit('node:change', parent, { type: NodeEventType.CHILD_CHANGED }); + + // 自身 parent = null 为删除,最后会有 deleted 消息,所以不需要再发 changed 消息 + if (child.parent) { + this.emit('node:change', child, { type: NodeEventType.PARENT_CHANGED }); + } + } + + onLightProbeChanged(node: Node) { + const changeOpts: IChangeNodeOptions = { type: NodeEventType.LIGHT_PROBE_CHANGED, source: EventSourceType.ENGINE }; + this.emit('node:change', node, changeOpts); + } + + /** + * 添加一个节点到管理器内 + * @param uuid + * @param {*} node + */ + add(uuid: string, node: Node) { + this.registerEventListeners(node); + + if (!isEditorNode(node)) { + this.emit('node:added', node); + } + } + + /** + * 一个节点被修改,由 EditorExtends.Node.emit('change') 触发 + * @param uuid + * @param node + */ + change(uuid: string, node: Node) { + if (!isEditorNode(node)) { + // 这里是因为 LOD 组件在挂到场景的时候,修改了自己的数据,但编辑器暂时无法知道修改了哪些数据 + // 所以针对 LOD 部分,增加了 propPath, prefab 才能正常修改 + let path = ''; + const lodGroup = node.getComponent(LODGroup); + if (lodGroup) { + const index = node.components.indexOf(lodGroup); + path = `__comps__.${index}`; + } + this.emit('node:change', node, { type: NodeEventType.SET_PROPERTY, propPath: path }); + } + } + + /** + * 从管理器内移除一个指定的节点 + * @param uuid + * @param {*} node + */ + remove(uuid: string, node: Node) { + this.unregisterEventListeners(node); + if (!isEditorNode(node)) { + this.emit('node:removed', node, { source: EventSourceType.ENGINE }); + } } } diff --git a/src/core/scene/scene-process/service/node/index.ts b/src/core/scene/scene-process/service/node/index.ts deleted file mode 100644 index 91bff39e1..000000000 --- a/src/core/scene/scene-process/service/node/index.ts +++ /dev/null @@ -1,1833 +0,0 @@ -'use strict'; - -import { IProperty } from '../../../@types/public'; - -/** - * 节点管理器 - * 负责管理当前打开场景的 uuid 与节点对应关系 - */ - -const NodeMgr = EditorExtends.Node; - -const { get, set } = require('lodash'); -import { isEditorNode, getNodeName } from './node-utils'; -import { ServiceEvents } from '../core/global-events'; - -// const { promisify } = require('util'); -// const { basename, extname } = require('path'); -// import nodeUtil from '../../../utils/node'; -import dumpUtil from '../dump'; - -// import getComponentFunctionOfNode from '../component/get-component-function-of-node'; -import { - Node, - director, - Component, - UITransform, - CCObject, - MissingScript, - LODGroup, - Prefab, -} from 'cc'; - -import { EventSourceType, NodeEventType, NodeOperationType } from '../public/event-enum'; -import { - type INodeEvents, - type INodeForEditor, - IChangeNodeOptions, -} from '../../../common'; -import { type ISceneForEditor } from '../../../common/editor/scene'; - - -import { loadAny } from './node-create'; -import compMgr from '../component/index'; - -const creatableAssetTypes = [ - 'cc.AnimationClip', - 'cc.AudioClip', - 'cc.BitmapFont', - 'cc.LabelAtlas', - 'cc.Mesh', - 'cc.ParticleAsset', - 'cc.Prefab', - 'cc.Script', - 'cc.SpriteFrame', - 'cc.TTFFont', - 'cc.TerrainAsset', - 'cc.TiledMapAsset', - 'cc.VideoClip', - 'dragonBones.DragonBonesAsset', - 'dragonBones.DragonBonesAtlasAsset', - 'sp.SkeletonData', -]; - -// 用于复制粘贴操作,暂存被复制节点的 clone 对象 -let stashInstants: any = null; - -/** - * 节点管理器 - * - * Events: - * node.on('before-change', (node) => {}); - * node.on('before-add', (node) => {}); - * node.on('before-remove', (node) => {}); - * node.on('change', (node) => {}); - * node.on('add', (node) => {}); - * node.on('remove', (node) => {}); - */ -export class NodeManager { - _onNodeAdded?: (...args: any[]) => void; - _onNodeChanged?: (...args: any[]) => void; - _onNodeRemoved?: (...args: any[]) => void; - _onTransformChanged?: (...args: any[]) => void; - _onSizeChanged?: (...args: any[]) => void; - _onAnchorChanged?: (...args: any[]) => void; - _onParentChanged?: (...args: any[]) => void; - _onLightProbeChanged?: (...args: any[]) => void; - - emit(event: K, ...args: INodeEvents[K]): void; - emit(event: string, ...args: any[]): void; - emit(event: string, ...args: any[]) { - ServiceEvents.emit(event, ...args); - } - - private _previewPropertysCache: Map> = new Map(); - get creatableAssetTypes() { - return creatableAssetTypes; - } - - init() { } - - /** - * 传入一个场景,将内部的节点全部缓存 - * @param {*} scene - */ - initWithScene(scene: any) { - if (!scene) { - return; - } - - const nodeMap = NodeMgr.getNodesInScene(); - - // 场景载入后要将现有节点监听所需事件 - Object.keys(nodeMap).forEach((key) => { - this.registerEventListeners(nodeMap[key]); - }); - - this.registerNodeMgrEvents(); - compMgr.init(); - - // 缓存预览设置的属性,用于还原预览前的设置 - this._previewPropertysCache = new Map(); - - this.emit('node:inited', this.queryUuids(), scene); - } - - public onSceneOpened(scene: any) { - this.initWithScene(scene); - } - - public onSceneClosed() { - compMgr.unregisterCompMgrEvents(); - this.unregisterNodeMgrEvents(); - this.clear(); - } - - - private readonly NodeMgrEventHandlers = { - ['add']: 'add', - ['change']: 'change', - ['remove']: 'remove', - } as const; - private nodeMgrEventHandlers = new Map void>(); - /** - * 注册引擎 Node 管理相关事件的监听 - */ - registerNodeMgrEvents() { - this.unregisterNodeMgrEvents(); - Object.entries(this.NodeMgrEventHandlers).forEach(([eventType, handlerName]) => { - const handler = (this as any)[handlerName].bind(this); - NodeMgr.on(eventType, handler); - this.nodeMgrEventHandlers.set(eventType, handler); - // console.log(`NodeMgr on ${eventType}`); - }); - } - - unregisterNodeMgrEvents() { - for (const eventType of this.nodeMgrEventHandlers.keys()) { - const handler = this.nodeMgrEventHandlers.get(eventType); - if (handler) { - NodeMgr.off(eventType, handler); - this.nodeMgrEventHandlers.delete(eventType); - // console.log(`NodeMgr off ${eventType}`); - } - } - } - - private readonly NodeHandlers = { - [Node.EventType.TRANSFORM_CHANGED]: 'onNodeTransformChanged', - [Node.EventType.SIZE_CHANGED]: 'onNodeSizeChanged', - [Node.EventType.ANCHOR_CHANGED]: 'onNodeAnchorChanged', - [Node.EventType.CHILD_ADDED]: 'onNodeParentChanged', - [Node.EventType.CHILD_REMOVED]: 'onNodeParentChanged', - [Node.EventType.LIGHT_PROBE_CHANGED]: 'onLightProbeChanged', - } as const; - private nodeHandlers = new Map(); - - /** - * 监听引擎发出的 node 事件 - * @param {*} node - */ - registerEventListeners(node: Node) { - if (!node || !node.isValid || isEditorNode(node)) { - return; - } - - // 遍历事件映射表,统一注册事件 - Object.entries(this.NodeHandlers).forEach(([eventType, handlerName]) => { - const boundHandler = (this as any)[handlerName].bind(this, node); - node.on(eventType, boundHandler, this); - this.nodeHandlers.set(`${eventType}_${node.uuid}`, boundHandler); - }); - } - - /** - * 取消监听引擎发出的node事件 - * @param {*} node - */ - unregisterEventListeners(node: Node) { - if (!node || !node.isValid || isEditorNode(node)) { - return; - } - - // 遍历事件映射表,统一取消事件 - Object.keys(this.NodeHandlers).forEach(eventType => { - const key = `${eventType}_${node.uuid}`; - const handler = this.nodeHandlers.get(key); - if (handler) { - node.off(eventType, handler); - this.nodeHandlers.delete(key); - } - }); - } - - onNodeTransformChanged(node: Node, transformBit: any) { - const changeOpts: IChangeNodeOptions = { type: NodeEventType.TRANSFORM_CHANGED, source: EventSourceType.ENGINE }; - - switch (transformBit) { - case Node.TransformBit.POSITION: - changeOpts.propPath = 'position'; - break; - case Node.TransformBit.ROTATION: - changeOpts.propPath = 'rotation'; - break; - case Node.TransformBit.SCALE: - changeOpts.propPath = 'scale'; - break; - } - - this.emit('node:change', node, changeOpts); - } - - onNodeSizeChanged(node: Node) { - const changeOpts: IChangeNodeOptions = { type: NodeEventType.SIZE_CHANGED, source: EventSourceType.ENGINE }; - const uiTransform = node.getComponent(UITransform); - if (uiTransform) { - const index = node.components.indexOf(uiTransform); - changeOpts.propPath = `_components.${index}.contentSize`; - } - this.emit('node:change', node, changeOpts); - } - - onNodeAnchorChanged(node: Node) { - const changeOpts: IChangeNodeOptions = { type: NodeEventType.ANCHOR_CHANGED, source: EventSourceType.ENGINE }; - const uiTransform = node.getComponent(UITransform); - if (uiTransform) { - const index = node.components.indexOf(uiTransform); - changeOpts.propPath = `_components.${index}.anchorPoint`; - } - this.emit('node:change', node, changeOpts); - } - - /** - * 监听引擎中节点 node.setParent(parent) 所发出来的事件 - * @param {*} parent - * @param {*} child - */ - onNodeParentChanged(parent: Node, child: Node) { - if (isEditorNode(child)) { - return; - } - - this.emit('node:change', parent, { type: NodeEventType.CHILD_CHANGED }); - - // 自身 parent = null 为删除,最后会有 deleted 消息,所以不需要再发 changed 消息 - if (child.parent) { - this.emit('node:change', child, { type: NodeEventType.PARENT_CHANGED }); - } - } - - /** - * 监听light-probe changed事件 - */ - onLightProbeChanged(node: Node) { - const changeOpts: IChangeNodeOptions = { type: NodeEventType.LIGHT_PROBE_CHANGED, source: EventSourceType.ENGINE }; - this.emit('node:change', node, changeOpts); - } - - /** - * 清空当前管理的节点 - */ - clear() { - const nodeMap = NodeMgr.getNodes(); - Object.keys(nodeMap).forEach((key) => { - this.unregisterEventListeners(nodeMap[key]); - }); - - NodeMgr.clear(); - compMgr.clear(); - } - - /** - * 添加一个节点到管理器内 - * @param {*} node - */ - add(uuid: string, node: Node) { - this.registerEventListeners(node); - - if (!isEditorNode(node)) { - this.emit('node:added', node); - } - } - - /** - * 一个节点被修改,由EditorExtends.Node.emit('change')触发 - * @param uuid - * @param node - */ - change(uuid: string, node: Node) { - if (!isEditorNode(node)) { - // 这里是因为 LOD 组件在挂到场景的时候,修改了自己的数据,但编辑器暂时无法知道修改了哪些数据 - // 所以针对 LOD 部分,增加了 propPath, prefab 才能正常修改 - let path = ''; - const lodGroup = node.getComponent(LODGroup); - if (lodGroup) { - const index = node.components.indexOf(lodGroup); - path = `__comps__.${index}`; - } - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: path }); - } - } - - /** - * 从管理器内移除一个指定的节点 - * @param {*} node - */ - remove(uuid: string, node: Node) { - this.unregisterEventListeners(node); - if (!isEditorNode(node)) { - this.emit('node:removed', node, { source: EventSourceType.ENGINE }); - } - } - - /** - * 查询一个节点的实例 - * @param {*} uuid - * @return {cc.Node} - */ - query(uuid: string | undefined): Node | null { - if (typeof uuid === 'undefined') { - return null; - } - return NodeMgr.getNode(uuid); - } - - /** - * 查询受管理的所有节点的 uuid 数组 - */ - queryUuids() { - const nodeMap = NodeMgr.getNodes(); - return Object.keys(nodeMap); - } - - /** - * 查询一个节点,并返回该节点的 dump 数据 - * 如果节点已被删除 parent = null,则返回 null - * @param {String} uuid - */ - queryDump(uuid: string): INodeForEditor | ISceneForEditor | null { - // 只查现有场景里的节点,不需要再查回收站里的节点 - const node = NodeMgr.getNodesInScene()[uuid]; - if (!node) { - return null; - } - return dumpUtil.dumpNode(node); - } - - /** - * 查询一个节点,并返回该节点的 dump 数据 - * 不论节点是否被删除 - * @param {String} uuid - */ - queryDumpAtAll(uuid: string): INodeForEditor | ISceneForEditor | null { - const node = this.query(uuid); - if (!node) { - return null; - } - return dumpUtil.dumpNode(node); - } - - // /** - // * 查询当前场景的节点树信息 - // * @param uuid asset uuid - // */ - // queryNodesByAssetUuid(uuid: string) { - // if (!uuid) { - // return []; - // } - - // return NodeMgr.getNodesByAsset(uuid); - // } - - // /** - // * 获取丢失资源的节点 - // * @returns uuids[] 节点数组 - // */ - // async queryNodesMissAsset() { - // const nodesUuid: string[] = []; - - // // 搜集 MissingScript - // const missScripts: { nodeUuid: string, scriptUuid: string }[] = []; - // EditorExtends.walkProperties( - // director.getScene()?.children, - // (obj: any, key: any, value: any, parsedObjects: any) => { - // // 搜集资源丢失的节点 - // if (value._uuid) { - // const isAssetMiss = !(cc.assetManager.assets.get(value._uuid) || cc.assetManager.assets.get(Editor.Utils.UUID.compressUUID(value._uuid, true))); - // if (isAssetMiss) { - // const node = findLast(parsedObjects, (item: any) => item instanceof cc.Node); - // if (node && !nodesUuid.includes(node.uuid)) { - // nodesUuid.push(node.uuid); - // } - // } - // } - // // 搜集 MissingScript(脚本丢失或者是编译不通过的脚本) - // if (value instanceof MissingScript) { - // // @ts-ignore __type__: 存储编译不通过或丢失的脚本 id - // const id = value._$erialized?.__type__; - // missScripts.push({ - // nodeUuid: value.node.uuid, - // scriptUuid: EditorExtends.UuidUtils.decompressUuid(id), - // }); - // } - // }, - // { - // dontSkipNull: false, - // ignoreSubPrefabHelper: true, - // }, - // ); - - // // 检测 MissingScript 的脚本是否真的丢失 - // const existingScriptResult = await Editor.Message.request('asset-db', 'batch-message-handler', missScripts.map((item: { nodeUuid: string, scriptUuid: string }) => { - // return { - // name: 'query-asset-info', - // args: [item.scriptUuid], - // }; - // })); - // const existingScriptUUIDs = existingScriptResult.map((info: IAssetInfo | null) => info && info.uuid); - // missScripts.forEach((item: { nodeUuid: string, scriptUuid: string }) => { - // if (!existingScriptUUIDs.includes(item.scriptUuid) && - // !nodesUuid.includes(item.nodeUuid)) { - // nodesUuid.push(item.nodeUuid); - // } - // }); - - // return nodesUuid; - // } - - /** - * 预览设置属性后的效果,不进入undo堆栈 - * @param uuid - * @param path - * @param dump - * @returns - */ - async previewSetNodeProperty(uuid: string, path: string, dump: IProperty): Promise { - const node = NodeMgr.getNode(uuid); - const info = dumpUtil.parsingPath(path, node); - if (!node) { - console.warn('previewSetNodeProperty failed:node not found', uuid); - return false; - } - if (!info.search) { - console.warn('previewSetNodeProperty failed:property path error', path); - return false; - } - // 需要自己记录设置前的属性,在取消时还原效果; - let target = get(node, info.search) ? get(node, info.search)[info.key] : undefined; - if (!target) { - // 属性为空时使用默认值 - target = dumpUtil.getDefaultValue(dump.type); - } - const data = dumpUtil.encodeObject(target, { - type: dump.type, - ctor: target.constructor, - }, target); - - // @ts-ignore - const cache: Map = this._previewPropertysCache.has(uuid) ? this._previewPropertysCache.get(uuid) : new Map(); - // 只有第一次预览时的数据,是节点原本的数据 - if (!cache?.has(path)) { - cache?.set(path, data); - } - this._previewPropertysCache.set(uuid, cache); - // 修改属性,false会避免记录undo操作; - return await this.setProperty(uuid, path, dump, false); - } - - async cancelPreviewSetNodeProperty(uuid: string, path: string): Promise { - // 拿到记录的数据,还原回数据 - const node = this.query(uuid); - const info = dumpUtil.parsingPath(path, node); - if (!node) { - console.warn('cancelPreviewSetNodeProperty failed:node not found', uuid); - return false; - } - if (!info.search) { - console.warn('cancelPreviewSetNodeProperty failed:property path error', path); - return false; - } - const cache = this._previewPropertysCache.get(uuid); - if (!cache) { - return false; - } - const dump = cache?.get(path); - if (!dump) { - return false; - } - // 清理掉原来的数据 - cache?.delete(path); - return await this.setProperty(uuid, path, dump, false); - } - - /** - * 设置一个节点的属性 - * @param {*} uuid - * @param {*} path - * @param {*} key - * @param {*} record 是否记录到undo堆栈上 - * @param {*} dump - */ - async setProperty(uuid: string, path: string, dump: IProperty, record = true): Promise { - // 多个节点更新值 - if (Array.isArray(uuid)) { - try { - for (let i = 0; i < uuid.length; i++) { - await this.setProperty(uuid[i], path, dump); - } - return true; - } catch (e) { - console.error(e); - return false; - } - } - const node = this.query(uuid); - if (!node) { - console.warn(`Set property failed: ${uuid} does not exist`); - return false; - } - - // 触发修改前的事件 - this.emit('node:before-change', node); - if (path === 'parent' && node.parent) { - // 发送节点修改消息 - this.emit('node:before-change', node.parent); - } - - // 恢复数据 - await dumpUtil.restoreProperty(node, path, dump); - - // 触发修改后的事件 - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: path, record: record }); - // 如果是数组的话,需要依次 emit change,路径定位到数组的下标位置 - if (dump.isArray && Array.isArray(dump.value)) { - dump.value.forEach((item, i) => { - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: `${path}.${i}`, record: record }); - }); - } - // 改变父子关系 - if (path === 'parent' && node.parent) { - // 发送节点修改消息 - this.emit('node:change', node.parent, { type: NodeOperationType.SET_PROPERTY, propPath: 'children', record: record }); - } - return true; - } - - /** - * 设置属性的默认值 - * @param {*} uuid - * @param {*} path - * @param {*} type - */ - async resetProperty(uuid: string, path: string): Promise { - // 多个节点更新值 - if (Array.isArray(uuid)) { - uuid.forEach((id) => { - this.resetProperty(id, path); - }); - return true; - } - const node = this.query(uuid); - if (!node) { - console.warn(`Set default value failed: ${uuid} does not exist`); - return false; - } - - // 触发修改前的事件 - this.emit('node:before-change', node); - - // 恢复数据 - await dumpUtil.resetProperty(node, path); - - // 触发修改后的事件 - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: path }); - return true; - } - - /** - * 将一个属性其现存值与定义类型值不匹配,或者为 null 默认值,改为一个可编辑的值 - * @param {*} uuid - * @param {*} path - */ - async updatePropertyFromNull(uuid: string, path: string): Promise { - // 多个节点更新值 - if (Array.isArray(uuid)) { - uuid.forEach((id) => { - this.updatePropertyFromNull(id, path); - }); - return true; - } - const node = this.query(uuid); - if (!node) { - console.warn(`Set default value failed: ${uuid} does not exist`); - return false; - } - - // 触发修改前的事件 - this.emit('node:before-change', node); - - // 恢复数据 - await dumpUtil.updatePropertyFromNull(node, path); - - // 触发修改后的事件 - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: path }); - return true; - } - - /** - * 重置节点属性 position rotation scale - * @param {*} uuid - */ - async resetNode(uuid: string): Promise { - // 多个节点更新值 - if (Array.isArray(uuid)) { - uuid.forEach((id) => { - this.resetNode(id); - }); - return true; - } - const node = this.query(uuid); - if (!node) { - console.warn(`Set default value failed: ${uuid} does not exist`); - return false; - } - - // 触发修改前的事件 - this.emit('node:before-change', node); - - // 恢复数据 - const properties = ['position', 'rotation', 'scale', 'mobility']; - for (const path of properties) { - await dumpUtil.resetProperty(node, path); - - // 触发修改后的事件 - this.emit('node:change', node, { type: NodeOperationType.SET_PROPERTY, propPath: path }); - } - return true; - } - - /** - * 设置某个节点连同它的子集的 layer 属性值 - * @param {*} uuid - * @param {*} dump - */ - async setNodeAndChildrenLayer(uuid: string, dump: any) { - await this.setProperty(uuid, 'layer', dump); - - const node = this.query(uuid); - - if (node && node.children && node.children.length > 0) { - node.children.forEach((child: any) => { - this.setNodeAndChildrenLayer(child.uuid, dump); - }); - } - } - - /** - * 调整一个数组类型的数据内某个 item 的位置 - * @param uuid 要被移动的节点或组件 - * @param path 数组的搜索路径 - * @param target 现在的索引位置 - * @param offset 偏移量 - */ - moveArrayElement(uuid: string, path: string, target: number, offset: number): boolean { - // TODO: deprecated 这一段 isArray 应该没有用到了,建议一段时间后可以删掉 - if (Array.isArray(uuid)) { - uuid.forEach((id) => { - this.moveArrayElement(id, path, target, offset); - }); - return false; - } - - const node = this.query(uuid); - if (!node) { - console.warn(`Move property failed: ${uuid} does not exist`); - return false; - } - - // 因为 path 内的 __comps__ 实际指向的是 _components - path = path.replace('__comps__', '_components'); - - // 找到指定的 data 数据 - const data = path ? get(node, path) : node; - if (!data) { - console.warn(`Move property failed: ${uuid} does not exist`); - return false; - } - - if (!Array.isArray(data)) { - console.warn(`Move property failed: ${uuid} - ${path} isn't an array`); - return false; - } - - // 发送节点修改消息 - this.emit('node:before-change', node); - - // 移动顺序 - if (path === 'children') { - // 过滤掉类似 Foreground Background 的节点 - const children = data.filter((child) => !(child.objFlags & cc.Object.Flags.HideInHierarchy)); - const child = children[target]; - - // 容错处理:新增的节点在引擎中还未创建,就指令其移动,setSiblingIndex 会报错 - if (!child) { - return false; - } - - // 找出要移动的节点在没有过滤掉隐藏节点的场景中的位置 - const index = data.indexOf(children[target + offset]); - - child.setSiblingIndex(index); - } else { - const temp = data.splice(target, 1); - data.splice(target + offset, 0, temp[0]); - - set(node, path, data); // 自身 = 自身(副本),为了兼顾材质需要整体赋值副本的情况 - } - - // 发送节点修改消息 - this.emit('node:change', node, { type: NodeOperationType.MOVE_ARRAY_ELEMENT, propPath: path }); - - return true; - } - - /** - * 删除一个数组元素 - * @param uuid 节点的 uuid - * @param path 元素所在数组的搜索路径 - * @param index 目标 item 原来的索引 - */ - removeArrayElement(uuid: string, path: string, index: number): boolean { - if (Array.isArray(uuid)) { - uuid.forEach((id) => { - this.removeArrayElement(id, path, index); - }); - return true; - } - const node = this.query(uuid); - const key = (path || '').split('.').pop(); - - if (key === 'children') { - console.warn('Unable to change `children` of the parent, Please change the `parent` of the child'); - return false; - } - - if (!node) { - console.warn(`Move property failed: ${uuid} does not exist`); - return false; - } - - // 因为 path 内的 __comps__ 实际指向的是 _components - path = path.replace('__comps__', '_components'); - - // 找到指定的 data 数据 - const data = path ? get(node, path) : node; - if (!data) { - console.warn(`Move property failed: ${uuid} does not exist`); - return false; - } - - if (!Array.isArray(data)) { - console.warn(`Move property failed: ${uuid} - ${path}.${key} isn't an array`); - return false; - } - - // 发送节点修改消息 - this.emit('node:before-change', node); - - // 删除components中的元素要通过调用removeComponent方法 - if (path === '_components') { - const comp = data[index]; - // https://github.com/cocos-creator/3d-tasks/issues/1116 - compMgr.removeComponent(comp.uuid); - } else { - // 删除某个 item - data.splice(index, 1); - - set(node, path, data); // 自身 = 自身(副本),为了兼顾材质需要整体赋值副本的情况 - } - - // 发送节点修改消息 - this.emit('node:change', node, { type: NodeOperationType.REMOVE_ARRAY_ELEMENT, propPath: path, index }); - - return true; - } - - /** - * 复制节点的动作,给下一步粘贴(创建)节点准备数据 - * @param {*} uuids 单个 string 或 array - */ - copyNode(uuids: string | string[]) { - if (!Array.isArray(uuids)) { - uuids = [uuids]; - } - - uuids = this.canRemoveOrCopy(uuids); - - stashInstants = {}; - - function changeFileId(node: Node) { - const prefabInfo = node['_prefab']; - - if (prefabInfo) { - if (prefabInfo.instance) { - return; - } else { - // 非prefabInstance节点,就变为普通节点来复制 - node['_prefab'] = null; - for (let i = 0; i < node.components.length; i++) { - const comp = node.components[i]; - comp.__prefab = null; - } - } - } - - if (node.children.length > 0) { - let index = node.children.length; - - // .children 是只读属性,需要用 splice - while (index--) { - const child = node.children[index]; - // 需要剔除不需要保存的私有节点 - const isPrivateNode = child.objFlags & cc.Object.Flags.HideInHierarchy; - const canDelete = child.objFlags & cc.Object.Flags.DontSave; - if (isPrivateNode && canDelete) { - node.removeChild(child); - // node.children.splice(index, 1); - } else { - changeFileId(child); - } - } - } - } - - for (const uuid of uuids) { - const node = this.query(uuid); - - if (!node) { - continue; - } - const instant = cc.instantiate(node); - - // Hack 目前 cc.instantiate 没有变动 fileId,这里变动一下,使它不重复 - changeFileId(instant); - - stashInstants[uuid] = { - instant, - }; - } - - return uuids; - } - - // /** - // * 复制节点自身 - // * 一般来自 ctrl + d 快捷键 - // * @param uuids - // */ - // duplicateNode(uuids: string | string[]) { - // if (!Array.isArray(uuids)) { - // uuids = [uuids]; - // } - - // const newUuids: string[] = []; - // const oldStashInstants = stashInstants; - // uuids = this.copyNode(uuids); - - // for (const uuid of uuids) { - // const node = this.query(uuid); - - // if (!node) { - // continue; - // } - - // const newUuid = this.createNode(node.parent?.uuid, null, uuid, false, true); - // if (newUuid) { - // newUuids.push(newUuid); - // } - // } - - // // 需要使用上次缓存的数据,否则粘贴会找不到上次拷贝的数据 #15805 - // stashInstants = oldStashInstants; - - // const results = newUuids.filter(Boolean); - - // // 选中新创建的节点 - // sceneSelection.clear(); - // results.forEach((uuid) => { - // sceneSelection.select(uuid); - // }); - - // return results; - // } - - // /** - // * 粘贴被复制的节点 - // * @param target - // * @param uuids - // * @param keepWorldTransform - // */ - // async pasteNode(target: string | null | undefined, uuids: string | string[], keepWorldTransform = false) { - // if (!Array.isArray(uuids)) { - // uuids = [uuids]; - // } - - // const newUuids: string[] = []; - - // for (const uuid of uuids) { - // const newUuid = this.createNode(target, null, uuid, keepWorldTransform, true); - // if (newUuid) { - // const node = this.query(newUuid); - // if (node) { - // // 预制体场景下,需要检查根节点有没有UI组件或canvas组件 - // const needCheckCanvasRequire = (cce.SceneFacadeManager.getCurrentFacade().modeName === SceneModeType.Prefab) && this.checkNodeUseCanvasRequired(node); - // const parent = await this.checkCanvasRequired(node, needCheckCanvasRequire, this.getNewNodeParent(target), undefined); - // parent !== node && node.setParent(parent, keepWorldTransform); - // } - // newUuids.push(newUuid); - // // 如果粘贴多个节点时,由于缺失了parent信息,会拿当前选中节点作为父节点 - // if (!target) { - // const node = this.query(newUuid); - // if (node) { - // target = node.parent?.uuid; - // } - // } - // } - // } - - // const results = newUuids.filter(Boolean); - - // // 选中新创建的节点 - // sceneSelection.clear(); - // results.forEach((uuid) => { - // sceneSelection.select(uuid); - // }); - - // return results; - // } - - /** - * 挂载节点,如拖入和剪切 - * @param parent - * @param uuids - * @param keepWorldTransform - */ - setParent(parent: string, uuids: string | string[], keepWorldTransform = false) { - if (!Array.isArray(uuids)) { - uuids = [uuids]; - } - - uuids = uuids.filter((uuid: string) => { - const node = this.query(uuid); - if (!node || !node.parent) { - return false; - } - return true; - }); - let parentNode: Node | null; - if (parent) { - parentNode = this.query(parent); - } - parentNode ||= director.getScene(); - - for (const uuid of uuids) { - const node = this.query(uuid); - node?.setParent(parentNode, keepWorldTransform); - } - - return uuids; - } - - /** - * 实时获取新节点在一个父节点下的有效名称 - * 规则是 Node 同名时为 Node-001 - * @param name 名称 - * @param parentUuid 父节点 uuid - */ - generateAvailableName(name: string, parentUuid?: string) { - if (!name) { - name = 'Node'; - } - - let parent = director.getScene() as Node; - - if (parentUuid) { - const node = this.query(parentUuid); - parent = node ? node : parent; - } - - return getNodeName(name, parent); - } - - // /** - // * 创建一个新节点 - // * @param uuid - // * @param name - // * @param stashUuid - // * @param keepWorldTransform - // * @param keepLayer - // */ - // createNode(uuid: string | null | undefined, name: any, stashUuid: string | null, keepWorldTransform = false, keepLayer = false): undefined | string { - // if (!cc.director.getScene()) { - // return; - // } - - // if (keepWorldTransform === null) { - // keepWorldTransform = true; - // } - - // /** - // * TODO: uuid 为 parentUuid - // * 理论上要在 prefab-scene-facade 的 createNode 中设置 - // * 注意 this.getNewNodeParent() 工具函数处理了无值情况下默认取第一个选中节点 - // * 如果提取到 prefab-scene-facade 设置,工具函数也要考虑 - // */ - // const parent = this.getNewNodeParent(uuid); - - // let node: Node | null = null; - - // if (stashUuid) { - // if (stashInstants[stashUuid]) { - // const { instant } = stashInstants[stashUuid]; - - // if (instant) { - // node = cc.instantiate(instant); - // if (node) { - // // 查找到第一层的PrefabInstance并设置新的FileId - // visitNode(node, (target) => { - // // @ts-ignore - // const prefabInfo = target['_prefab']; - // if (prefabInfo?.instance) { - // // 复制需要产生新的Prefab实例,因为需要不同的fileId - // prefabInfo.instance = prefabUtils.cloneInstanceWithNewFileId(prefabInfo.instance); - // return true; - // } - // }); - - // name = getNodeName(node.name, parent); - // } - // } - // } - // } - - // if (!node) { - // node = new cc.Node(); - // } - - // if (!node) { - // return; - // } - - // if (name) { - // node.name = name; - // } - - // /** - // * 新节点的 layer 跟随父级节点,但父级节点为场景根节点除外 - // * parent.layer 可能为 0 (界面下拉框为 None),此情况下新节点不跟随 - // */ - // if (parent.layer && parent !== director.getScene() && !keepLayer) { - // setLayer(node, parent.layer, true); - // } - - // this.emit('node:before-add', node); - // this.emit('node:before-change', parent); - - // node.setParent(parent, keepWorldTransform); - - // if (!stashUuid) { - // this.ensureUITransformComponent(node); - // } - - // // 发送添加节点事件,添加节点中的根节点 - // this.emit('node:add', node); - - // // 发送节点修改消息 - // if (parent) { - // this.emit('node:change', parent, { type: NodeEventType.CHILD_CHANGED }); - // } - - // // 选中新创建的节点 - // cce.Selection.clear(); - // cce.Selection.select(node.uuid); - - // return node.uuid; - // } - - /** - * 确保节点有 UITransform 组件 - * 目前只需保障在创建空节点的时候检查任意上级是否为 canvas - */ - ensureUITransformComponent(node: Node) { - if (node instanceof cc.Node && node.children.length === 0) { - // 空节点 - let inside = false; - let parent = node.parent; - - while (parent) { - const components = parent.components.map((comp) => cc.js.getClassName(comp.constructor)); - if (components.includes('cc.Canvas')) { - inside = true; - break; - } - parent = parent.parent; - } - - if (inside) { - try { - node.addComponent('cc.UITransform'); - } catch (error) { - console.error(error); - } - } - } - } - - async restorePrefab(uuid: string, assetUuid: string) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const that = this; - - if (!director.getScene()) { - return false; - } - - // 先取消选中,暂存选中的节点 uuid - // const selectedUuids = cce.Selection.query(); - // cce.Selection.clear(); - - const query = this.query; - const prefabRoot = query(uuid) as Node; - - // 根据 fileId 缓存旧节点,以用于一些引用节点的还原 - const oldNodes: Record = {}; - /** - * 缓存 fileId 对应的旧节点 - * @param node 节点 - */ - function collectOldNodes(node: Node) { - if (!node || !node['_prefab']) { - return; - } - - oldNodes[node['_prefab'].fileId] = node; - - if (Array.isArray(node.children)) { - node.children.forEach((child) => { - collectOldNodes(child); - }); - } - } - - // 根据 fileId 缓存子集列表 uuids - const childrenUuid: Record> = {}; - function collectChildrenUuid(node?: Node) { - const rt: Record = {}; - - if (node) { - if (childrenUuid[node.uuid]) { - return childrenUuid[node.uuid]; - } - - if (Array.isArray(node.children)) { - node.children.forEach((child: any) => { - // @ts-ignore - if (child && child['_prefab']) { - // @ts-ignore - rt[child['_prefab'].fileId] = child.uuid; - } - }); - childrenUuid[node.uuid] = rt; - } - } - return rt; - } - - // 根据 fileId 缓存子集列表的正确索引 - const childrenIndex: Record> = {}; - function collectChildrenIndex(node: Node | undefined) { - const rt: Record = {}; - - if (node) { - if (childrenIndex[node.uuid]) { - return childrenIndex[node.uuid]; - } - - if (Array.isArray(node.children)) { - node.children.forEach((child, i) => { - // @ts-ignore - if (child && child['_prefab']) { - // @ts-ignore - rt[child['_prefab'].fileId] = i; - } - }); - childrenIndex[node.uuid] = rt; - } - } - return rt; - } - - /** - * 检查 dump 中的引用节点是否可用 - * 旧节点还存在的时候,新数据是不可用的,新数据里需要替换为旧节点的 uuid - * 旧节点不存在的时候,新数据便是可用的,因为新节点一定会替换上去。 - * @param dumpComps 组件 - */ - function redirectSceneRefs(dumpComps: any) { - dumpComps.forEach((comps: any) => { - if (!comps.value || typeof comps.value !== 'object') { - return; - } - - const keys = Object.keys(comps.value); - for (const key of keys) { - if (['node'].includes(key)) { - continue; - } - - const comp = comps.value[key]; - - // 递归到里层 - if (comp.isArray && Array.isArray(comp.value)) { - redirectSceneRefs(comp.value); - continue; - } - - if (comp.type === 'cc.Node') { - const newNode = query(comp.value.uuid); - - // 数据错误 - if (!newNode || !newNode?.['_prefab']?.fileId) { - continue; - } - const oldNode = oldNodes[newNode['_prefab'].fileId]; - if (oldNode) { - comp.value.uuid = oldNode.uuid; // 换为旧节点的 uuid - } - } - } - }); - } - - /** - * 还原现有节点的 dump ,删除多余节点,添加新节点 - * @param newNode 新节点 - * @param parentNode 新节点的父节点 - * @param prefabParent 新节点通过 fileId 指向现有节点的父节点 - */ - async function restore(newNode: Node, parentNode?: Node, prefabParent?: Node) { - // 私有节点不还原 - if (newNode.objFlags & cc.Object.Flags.HideInHierarchy) { - return false; - } - - const fileId2Index = collectChildrenIndex(parentNode); // 对应新数据上的子集排列 - const fileId2Uuid = collectChildrenUuid(prefabParent); // 对应新数据上的 uuid - - const dump = dumpUtil.dumpNode(newNode) as INodeForEditor; - const fileId = dump.__prefab__!.fileId; - // 现有 prefab 节点 - const prefab = prefabParent ? query(fileId2Uuid[fileId]) : prefabRoot; - - if (prefab) { - // 如果现有的节点存在,只需还原 dump data - that.emit('node:before-change', prefab); - - // 删除掉不在新数据上的子节点 - if (Array.isArray(prefab.children)) { - const childrenFileId2Index = collectChildrenIndex(newNode); - - let index = 0; - let child = prefab.children[index]; - while (child && index < prefab.children.length) { - // @ts-ignore - if (child['_prefab'] && childrenFileId2Index[child['_prefab'].fileId] === undefined) { - that.removeNode(child.uuid); - } else { - index++; - } - child = prefab.children[index]; - } - } - - const prefabDump = dumpUtil.dumpNode(prefab) as INodeForEditor; - - // 删除不必要的字段 - // Prefab 里的 dump 为什么需要删除 uuid - // @ts-ignore - delete dump.uuid; - // @ts-ignore - delete dump.children; - - // 不是根节点 - if (prefabParent) { - dump.parent.value.uuid = prefabParent.uuid; - } else { - // 如果是 prefab 根节点,有些属性不能还原 - dump.active.value = prefabDump.active.value; - dump.name.value = prefabDump.name.value; - dump.position.value = prefabDump.position.value; - dump.rotation.value = prefabDump.rotation.value; - } - - // 使用原来的数据 - dump.__prefab__ = JSON.parse(JSON.stringify(prefabDump.__prefab__)); - - // 检查一些属性上的值,其节点引用是否正确 - if (Array.isArray(dump.__comps__)) { - redirectSceneRefs(dump.__comps__); - } - - // prefab 为现有的 prefab 节点,用新数据 dump 还原内部属性和组件的值 - await dumpUtil.restoreNode(prefab, dump); - - // 确保位置准确 - if (fileId2Index[fileId] !== undefined) { - prefab.setSiblingIndex(fileId2Index[fileId]); - } - - // 逐层移动到目标节点上 - let index = 0; - let childNode = newNode.children[index]; - while (childNode && index < newNode.children.length) { - const isMoved = await restore(childNode, newNode, prefab); - if (!isMoved) { - index++; - } - childNode = newNode.children[index]; - } - - that.emit('node:change', prefab); - - // 没有移动 - return false; - } - // 现有节点不存在,则将临时的 prefab 中 fileId 一致的节点移动过来替换 - const newPrefab = newNode['_prefab']; - if (newPrefab && prefabParent) { - that.emit('node:before-add', newNode); - that.emit('node:before-change', prefabParent); - const fileID = newPrefab.fileId; - const index = fileId2Index[fileID]; - prefabParent.insertChild(newNode, index); - newPrefab.root = prefabParent['_prefab']?.root; - that.emit('node:add', newNode); - that.emit('node:change', prefabParent); - } - - // 有移动 - return true; - } - - try { - collectOldNodes(prefabRoot); - const asset = await loadAny(assetUuid); - const newNode = cc.instantiate(asset); - prefabRoot.parent?.addChild(newNode); - await restore(newNode); // 逐层还原 prefab - newNode.parent = null; // 删除临时节点 - - this.emit('node:change', prefabRoot); - } catch (error) { - console.warn('The prefab asset no longer exist.'); - console.error(error); - return false; - } - - // 重新选中,恢复 gizmos 状态 - // setTimeout(() => { - // selectedUuids.forEach((selectedUuid: string) => { - // cce.Selection.select(selectedUuid); - // }); - // }); - - return true; - } - // /** - // * 节点内包含UITransform组件,则需要父级也包含该组件, - // * canvas本身不需要Canvas组件 - // * @param node - // * @returns - // */ - // checkNodeUseCanvasRequired(node: Node | undefined): boolean { - // if (!node) return false; - // let flag = false; - // if (node.getComponent('cc.Canvas')) return false; - // node.walk((child) => { - // if (flag) { - // return; - // } - // child.components.forEach((component) => { - // if (flag) { - // return; - // } - // flag = js.getClassName(component) === 'cc.UITransform'; - // }); - // }); - - // return flag; - // } - - // /** - // * 检查并根据需要创建 canvas节点或为父级添加UITransform组件,返回父级节点,如果需要canvas节点,则父级节点会是canvas节点 - // * @param component - // * @param canvasRequiredParam - // * @param parent - // * @param position - // * @returns - // */ - // async checkCanvasRequired(node: Node | undefined, canvasRequiredParam: boolean | undefined, parent: Node, position: Vec3 | undefined): Promise { - - // /** - // * TODO: mode 判断不应该在底层这里 - // * 从 assets 拖图片到场景,不会走 prefab-scene-facade 的 createNode - // * 直接从 onDrop 走到了这里,先保持这样,后续改 - // */ - // const mode = cce.SceneFacadeManager.queryMode(); - // let prefabProxy: PrefabSceneProxy | null = null; - // let prefabRoot = null; - - // if (mode === 'prefab') { - // prefabProxy = cce.SceneFacadeManager['_facadeFSM'].prefabSceneFacade['_sceneProxy'] as PrefabSceneProxy; - // prefabRoot = prefabProxy.getRootNode(); - - // // prefab 的场景节点是临时的根节点,需转为 prefab root node - // if (parent === director.getScene()) { - // parent = prefabRoot; - // } - // } - - // if (canvasRequiredParam) { - // let canvasNode: Node | null = null; - - // // 在 prefab 模式下,询问是否给根节点添加 UITransform 组件还是父级添加一个 Canvas 节点 - // if (mode === 'prefab') { - // canvasNode = getUICanvasNode(parent, false); - - // const UITransformParentNode = getUITransformParentNode(parent); - // if (!canvasNode) { - // if (!UITransformParentNode) { - // const result = await Editor.Dialog.warn(Editor.I18n.t('scene.contributions.messages.description.UITransform_lack'), { - // buttons: [ - // Editor.I18n.t('scene.contributions.messages.description.UITransform_add_to_root'), - // Editor.I18n.t('scene.contributions.messages.description.UITransform_within_canvas'), - // Editor.I18n.t('scene.contributions.messages.description.UITransform_cancel'), - // ], - // default: 0, - // cancel: 2, - // }); - - // if (result.response === 0) { - // if (!prefabRoot) { - // console.error('prefab Root is not exist'); - // return null; - // } - // prefabRoot.addComponent('cc.UITransform'); - - // if (prefabRoot.parent && !hasOneKindOfComponent(prefabRoot.parent, Canvas)) { - // // 为了显示,节点结构为:scene node > canvas node > prefab root node - // canvasNode = await createShouldHideInHierarchyCanvasNode(director.getScene()!); - // prefabRoot.parent = canvasNode; - // } - // } - // if (result.response === 2) { - // canvasNode = new Node(); - // } - // } else { - // canvasNode = UITransformParentNode; - // } - // } else { - // if (canvasNode.parent !== director.getScene()) { - // parent = canvasNode; - // } - // } - // } else { - // canvasNode = getUICanvasNode(parent); - // if (canvasNode) { - // parent = canvasNode; - // } - // } - - // // 自动创建一个 canvas 节点 - // if (!canvasNode) { - // let canvasAssetUuid = 'f773db21-62b8-4540-956a-29bacf5ddbf5'; - - // // 2d 项目创建的 ui 节点,canvas 下的 camera 的 visibility 默认勾上 default - // if (cce.SceneFacadeManager['_projectType'] === '2d') { - // canvasAssetUuid = '4c33600e-9ca9-483b-b734-946008261697'; - // } - - // assetManager.assets.remove(canvasAssetUuid); - // const canvasAsset = await loadAny(canvasAssetUuid); - // canvasNode = cc.instantiate(canvasAsset) as Node; - // cce.Prefab.removePrefabInfoFromNode(canvasNode); - - // parent.addChild(canvasNode); - // parent = canvasNode; - // } - - // // 目前 canvas 默认 z 为 1,而拖放到 Canvas 的控件因为检测的是 z 为 0 的平面,所以这边先强制把 z 设置为和 canvas 的一样 - // if (position) { - // position.z = canvasNode.position.z; - // } - // } - // return parent; - // } - - // /** - // * 从一个资源创建对应的节点 - // * @param {*} parentUuid - // * @param {*} assetUuid - // * @param {*} options { type: 资源类型, position: 位置坐标(vector3), name: 新的名字, canvasRequired?: true 是否需要有 cc.Canvas 父级 } - // */ - // async createNodeFromAsset(parentUuid: string | undefined | null, assetUuid: string, options: CreateNodeOptions): Promise { - // try { - // if (!cc.director.getScene()) return; - - // let parent: Node | null = this.getNewNodeParent(parentUuid); - // const { name, type, position, unlinkPrefab } = options; - - // // 有 assetUuid 时 type 默认可以为空 - // if (type && !creatableAssetTypes.includes(type)) return; - - // if (DEBUG_TIME_COST) { - // console.time('createNodeFromAsset'); - // } - - // if (options.autoAdaptToCreate === undefined) { - // options.autoAdaptToCreate = true; - // } - - // const { node, canvasRequired } = await createNodeByAsset({ - // uuid: assetUuid, - // type: type, - // canvasRequired: options.canvasRequired, - // autoAdaptToCreate: options.autoAdaptToCreate, - // }); - - // parent = await this.checkCanvasRequired(node, Boolean(canvasRequired), parent, position as Vec3) as Node; - - // if (name) { - // // 使用创建时指定的名称 - // node.name = basename(name, extname(name)); - // } - - // if (DEBUG_TIME_COST) { - // console.time('addNodeEvent'); - // } - - // this.emit('node:before-add', node); - // this.emit('node:before-change', parent); - - // /** - // * 默认创建节点是从 prefab 模板,所以初始是 prefab 节点 - // * 是否要 unlink 为普通节点 - // */ - // if (type !== 'cc.Prefab' || unlinkPrefab) { - // cce.Prefab.removePrefabInfoFromNode(node); - - // /** - // * 新节点的 layer 跟随父级节点,但父级节点为场景根节点除外 - // * parent.layer 可能为 0 (界面下拉框为 None),此情况下新节点不跟随 - // */ - // if (parent.layer && parent !== director.getScene()) { - // setLayer(node, parent.layer, true); - // } - // } - - // parent.addChild(node); - - // if (position) { - // node.setWorldPosition(position); - // } - - // // 发送添加节点事件,添加节点中的根节点. 'added'事件会在添加的所有节点(包括子节点)上发送 - // this.emit('node:add', node); - // // 发送节点修改消息 - // this.emit('node:change', parent, { type: NodeEventType.CHILD_CHANGED }); - - // if (DEBUG_TIME_COST) { - // console.timeEnd('addNodeEvent'); - // } - // // 选中新创建的节点 - // cce.Selection.clear(); - // cce.Selection.select(node.uuid); - - // if (DEBUG_TIME_COST) { - // console.timeEnd('createNodeFromAsset'); - // } - // return node.uuid; - // } catch (error) { - // console.error(error); - // return; - // } - // } - - private _walkNode(node: Node, func: Function) { - node && node.children && node.children.forEach((child) => { - func(child); - this._walkNode(child, func); - }); - } - - public baseRemoveNode(node: Node, keepWorldTransform?: boolean) { - // 增加容错 - if (!node) { - return; - } - - const parent = node.parent; - - // 发送节点修改消息 - this.emit('node:before-remove', node); - if (parent) { - this.emit('node:before-change', parent); - } - - //console.time('NodeMgr::removeNode'); - node.setParent(null, keepWorldTransform); - node._objFlags |= CCObject.Flags.Destroyed; - // 3.6.1 特殊 hack,请在后续版本移除 - // 相关修复 pr: https://github.com/cocos/cocos-editor/pull/890 - try { - this._walkNode(node, (child: any) => { - child._objFlags |= CCObject.Flags.Destroyed; - }); - } catch (error) { - console.warn(error); - } - - //console.timeEnd('NodeMgr::removeNode'); - - // 被删除节点里的根节点 - this.emit('node:remove', node, { source: EventSourceType.EDITOR }); - } - - /** - * 删除节点 - * @param {*} uuids - * @param {*} keepWorldTransform - */ - removeNode(uuids: string | string[], keepWorldTransform?: boolean) { - if (!Array.isArray(uuids)) { - uuids = [uuids]; - } - - uuids = this.canRemoveOrCopy(uuids); - - for (const uuid of uuids) { - const node: Node | null = this.query(uuid); - if (!node) { - continue; - } - this.baseRemoveNode(node, keepWorldTransform); - } - } - - /** - * 锁定一个节点不让其在场景中被选中 - * @param uuids 节点uuid - * @param locked true | false - * @param loop true | false 是否循环子孙级节点设置 - */ - changeNodeLock(uuids: string | string[], locked: boolean, loop: boolean) { - if (!Array.isArray(uuids)) { - uuids = [uuids]; - } - - for (const uuid of uuids) { - const node = this.query(uuid); - - // 增加容错 - if (!node) { - continue; - } - - this.emit('node:before-change', node); - - try { - if (locked) { - node.objFlags |= cc.Object.Flags.LockedInEditor; - } else { - node.objFlags &= ~cc.Object.Flags.LockedInEditor; - } - } catch (error) { - console.error(error); - } - - this.emit('node:change', node); - - // 处理内循环的情况 - if (loop === true && node.children && node.children.length > 0) { - node.children.forEach((child: any) => { - this.changeNodeLock(child.uuid, locked, loop); - }); - } - } - } - - /** - * 过滤根节点 - * 过滤子父包含的关系,只留下彼此独立的父节点 uuid - * @param uuids - */ - canRemoveOrCopy(uuids: string[]) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const t: any = this; - - const rt: string[] = []; - - // 剔除根节点或其他不可删除的节点 - const nodeUuids: string[] = []; - for (const uuid of uuids) { - const node = t.query(uuid); - - if (!node || !node.parent || node.objFlags & cc.Object.Flags.DontDestroy) { - continue; - } - - nodeUuids.push(uuid); - } - - // 剔除已在列表中其他节点的子节点 - for (const uuid of nodeUuids) { - const node = t.query(uuid); - if (!isChild(node)) { - rt.push(uuid); - } - } - - /** - * 是否是已在的列表中其他节点的子节点 - * @param node - */ - function isChild(node: any): boolean { - if (!node.parent) { - return false; - } - - if (nodeUuids.includes(node.parent.uuid)) { - return true; - } else { - return isChild(node.parent); - } - } - - return rt; - } - - // /** - // * 获取创建节点时所在的父节点 - // * @param uuid 父节点 - // */ - // getNewNodeParent(uuid: string | null | undefined): Node { - // let parent; - - // if (uuid) { - // parent = this.query(uuid); - // } else { - // /** - // * 如果有选中的节点,默认挂在第一个选中节点里 - // * 如果没有,挂在场景根节点里 - // */ - // const selects = cce.Selection.query(); - // if (Array.isArray(selects) && selects[0]) { - // parent = this.query(selects[0]); - // } else { - // parent = director.getScene(); - // } - // } - - // if (!parent) { - // parent = director.getScene(); - // } - - // // 不应该是Node里的逻辑 - // const mode = cce.SceneFacadeManager.queryMode(); - // if (mode === 'prefab') { - // const prefabProxy = cce.SceneFacadeManager['_facadeFSM'].prefabSceneFacade['_sceneProxy']; - // const prefabRoot = prefabProxy.getRootNode(); - - // // prefab 的场景节点是临时的根节点,需转为 prefab root node - // if (parent === cc.director.getScene()) { - // parent = prefabRoot; - // } - // } - // return parent as Node; - // } - - // changeNodeUUID(oldUUID: string | undefined, newUUID: string | undefined) { - // if (!oldUUID || !newUUID) { - // return; - // } - - // NodeMgr.changeNodeUUID(oldUUID, newUUID); - // } - - addComponentAt(node: Node, comp: Component, index: number): boolean { - if (!node || !comp || index < 0) { - return false; - } - - if (comp instanceof MissingScript && !comp._$erialized) { - return false; - } - - // @ts-ignore - node._addComponentAt(comp, index); - compMgr.emit('component:add', comp); - - return true; - } -} - -export default new NodeManager(); diff --git a/src/core/scene/scene-process/service/node/node-utils.ts b/src/core/scene/scene-process/service/node/node-utils.ts index 9a740f6ff..1d152476d 100644 --- a/src/core/scene/scene-process/service/node/node-utils.ts +++ b/src/core/scene/scene-process/service/node/node-utils.ts @@ -94,33 +94,6 @@ export function hasOneKindOfComponent(node: Node | Scene, kind: any) { return false; } -/** - * 生成一个 node-001 格式的可用节点名称 - * @param name 被检查的名称 - * @param parent 父级节点 - * @returns {string} path 可用名称的文件路径 - */ -export function getNodeName(name: string, parent: Node) { - if (!parent || !Array.isArray(parent.children)) { - return name; - } - - const names = parent.children.map((child: Node) => (child && child.name) || ''); - - while (names.includes(name)) { - if (/(\d+)$/.test(name)) { - name = name.replace(/(\d+)$/, (strA: string, strB: string) => { - let num = parseInt(strB, 10); - num += 1; - return num.toString().padStart(strB.length, '0'); - }); - } else { - name += '-001'; - } - } - - return name; -} /** * 设置节点层级 diff --git a/src/core/scene/scene-process/service/service-manager.ts b/src/core/scene/scene-process/service/service-manager.ts index c870d6a6f..34e64544e 100644 --- a/src/core/scene/scene-process/service/service-manager.ts +++ b/src/core/scene/scene-process/service/service-manager.ts @@ -37,9 +37,9 @@ const SERVICE_EVENTS_MAP: EventMap = { 'component:remove': 'onRemoveComponent', 'component:added': 'onComponentAdded', 'component:removed': 'onComponentRemoved', + 'component:before-remove': 'onBeforeRemoveComponent', 'component:set-property': 'onSetPropertyComponent', - 'component:before-add-component': 'onBeforeAddComponent', - 'component:before-remove-component': 'onBeforeRemoveComponent', + 'component:before-add-component': 'onBeforeComponentAdded', // Script 事件 'script:execution-finished': 'onScriptExecutionFinished', diff --git a/src/core/scene/test/component-proxy.testcase.ts b/src/core/scene/test/component-proxy.testcase.ts index bbe4ebfe8..7cc971b82 100644 --- a/src/core/scene/test/component-proxy.testcase.ts +++ b/src/core/scene/test/component-proxy.testcase.ts @@ -10,44 +10,14 @@ import { IComponent, IComponentForEditor, IQueryClassesOptions, - IExecuteComponentMethodOptions, NodeType, INode } from '../common'; import { ComponentProxy } from '../main-process/proxy/component-proxy'; import { NodeProxy } from '../main-process/proxy/node-proxy'; import { EditorProxy } from '../main-process/proxy/editor-proxy'; -import { Rpc } from '../main-process/rpc'; import { SceneTestEnv } from './scene-test-env'; -// 这些接口未在 IPublicComponentService 中暴露,测试中直接通过 RPC 调用 -const rpcRequest = (method: string, args?: any[]) => - (Rpc.getInstance() as any).request('Component', method, args); - -function createComponent(params: IAddComponentOptions): Promise { - return rpcRequest('create', [params]); -} - -function resetComponent(params: IQueryComponentOptions): Promise { - return rpcRequest('reset', [params]); -} - -function queryClasses(options?: IQueryClassesOptions): Promise<{ name: string }[]> { - return rpcRequest('queryClasses', [options]); -} - -function queryComponentFunctionOfNode(path: string): Promise { - return rpcRequest('queryFunctionOfNode', [path]); -} - -function executeComponentMethod(options: IExecuteComponentMethodOptions): Promise { - return rpcRequest('executeMethod', [options]); -} - -function queryComponentHasScript(name: string): Promise { - return rpcRequest('hasScript', [name]); -} - describe('Component Proxy 测试', () => { let nodePath = ''; let nodeId = ''; @@ -67,7 +37,7 @@ describe('Component Proxy 测试', () => { nodeType: NodeType.EMPTY, position: { x: 1, y: 2, z: 0 }, }; - const testNode = await NodeProxy.createByType(params); + const testNode = await NodeProxy.createNodeByType(params); expect(testNode).toBeDefined(); expect(testNode?.name).toBe('New Node'); if (!testNode) { @@ -84,7 +54,7 @@ describe('Component Proxy 测试', () => { path: nodePath, keepWorldTransform: false }; - await NodeProxy.delete(params); + await NodeProxy.deleteNode(params); expect(params).toBeDefined(); expect(params?.path).toBe(nodePath); } catch (e) { @@ -97,56 +67,56 @@ describe('Component Proxy 测试', () => { describe('1. 基础组件操作- 添加,查询,设置属性,移除', () => { let componentPath = ''; let componentInfo: IComponent | null; - it('add - 添加节点 - 完整节点名称:cc.Label', async () => { + it('addComponent - 添加节点 - 完整节点名称:cc.Label', async () => { //console.log("Created prefab node path=", prefabNode?.path); const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label' }; try { - componentInfo = await ComponentProxy.add(addComponentInfo); + componentInfo = await ComponentProxy.addComponent(addComponentInfo); componentPath = componentInfo.path; expect(componentInfo.path).toBe(`${nodePath}/cc.Label`); // 删除当前添加的节点,方便后续测试 const removeComponentInfo: IRemoveComponentOptions = { path: componentPath }; - const result = await ComponentProxy.remove(removeComponentInfo); + const result = await ComponentProxy.removeComponent(removeComponentInfo); expect(result).toBe(true); } catch (e) { console.log(`addComponent test error: ${e}`); throw e; } }); - it('add -添加节点 - 模糊节点名称:cc.label', async () => { + it('addComponent - 添加节点 - 模糊节点名称:cc.label', async () => { //console.log("Created prefab node path=", prefabNode?.path); const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.label' }; try { - componentInfo = await ComponentProxy.add(addComponentInfo); + componentInfo = await ComponentProxy.addComponent(addComponentInfo); componentPath = componentInfo.path; expect(componentInfo.path).toBe(`${nodePath}/cc.Label`); // 删除当前添加的节点,方便后续测试 const removeComponentInfo: IRemoveComponentOptions = { path: componentPath }; - const result = await ComponentProxy.remove(removeComponentInfo); + const result = await ComponentProxy.removeComponent(removeComponentInfo); expect(result).toBe(true); } catch (e) { console.log(`addComponent test error: ${e}`); throw e; } }); - it('add -添加节点 - 模糊节点名称:Label', async () => { + it('addComponent - 添加节点 - 模糊节点名称:Label', async () => { //console.log("Created prefab node path=", prefabNode?.path); const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'Label' }; try { - componentInfo = await ComponentProxy.add(addComponentInfo); + componentInfo = await ComponentProxy.addComponent(addComponentInfo); componentPath = componentInfo.path; expect(componentInfo.path).toBe(`${nodePath}/cc.Label`); @@ -154,21 +124,21 @@ describe('Component Proxy 测试', () => { const removeComponentInfo: IRemoveComponentOptions = { path: componentPath }; - const result = await ComponentProxy.remove(removeComponentInfo); + const result = await ComponentProxy.removeComponent(removeComponentInfo); expect(result).toBe(true); } catch (e) { console.log(`addComponent test error: ${e}`); throw e; } }); - it('add -添加节点 - 模糊节点名称:label', async () => { + it('addComponent - 添加节点 - 模糊节点名称:label', async () => { //console.log("Created prefab node path=", prefabNode?.path); const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'label' }; try { - componentInfo = await ComponentProxy.add(addComponentInfo); + componentInfo = await ComponentProxy.addComponent(addComponentInfo); componentPath = componentInfo.path; expect(componentInfo.path).toBe(`${nodePath}/cc.Label`); @@ -179,12 +149,12 @@ describe('Component Proxy 测试', () => { } }); - it('query - 查询组件- 根据 uuid 查询', async () => { + it('queryComponent - 查询组件- 根据 uuid 查询', async () => { const queryComponent: IQueryComponentOptions = { path: componentInfo!.uuid }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -200,12 +170,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据完整组件名查询', async () => { + it('queryComponent - 查询组件-根据完整组件名查询', async () => { const queryComponent: IQueryComponentOptions = { path: componentPath }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -221,12 +191,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据模糊的匹配-Label', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-Label', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/Label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -242,12 +212,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据模糊的匹配-cc.label', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-cc.label', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/cc.label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -263,12 +233,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据模糊的匹配-Label', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-Label', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/Label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -284,12 +254,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据模糊的匹配-label', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-label', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -306,12 +276,12 @@ describe('Component Proxy 测试', () => { } }); - it('query - 查询组件-根据模糊的匹配-label不带下标', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-label不带下标', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -327,12 +297,12 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询组件-根据模糊的匹配-cc.label不带下标', async () => { + it('queryComponent - 查询组件-根据模糊的匹配-cc.label不带下标', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/cc.label' }; try { - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo).toBeDefined(); if (componentInfo!.cid) { expect(componentInfo!.cid).toBe('cc.Label'); @@ -348,23 +318,23 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('query - 查询不存在组件', async () => { + it('queryComponent - 查询不存在组件', async () => { const queryComponent: IQueryComponentOptions = { path: nodePath + '/cc.Button' }; try { - await ComponentProxy.query(queryComponent) as IComponent; + await ComponentProxy.queryComponent(queryComponent) as IComponent; } catch (e) { expect(e instanceof Error ? e.message : String(e)).toBe(`No component found for this path(${queryComponent.path}).`); } }); - it('query - 根据不存在的 URL 查询组件', async () => { + it('queryComponent - 根据不存在的 URL 查询组件', async () => { const queryComponent: IQueryComponentOptions = { path: 'db://assets/non-existent-script.ts' }; try { - const result = await ComponentProxy.query(queryComponent) as IComponent; + const result = await ComponentProxy.queryComponent(queryComponent) as IComponent; // 如果没有抛出异常,则结果应该为 null expect(result).toBeNull(); } catch (e) { @@ -373,10 +343,10 @@ describe('Component Proxy 测试', () => { } }); - it('query - 查询存在相同组件', async () => { + it('queryComponent - 查询存在相同组件', async () => { const newNodePath = 'TestNode/new node'; const addComponentInfo: IAddComponentOptions = { - nodePath: newNodePath, + nodePathOrUuid: newNodePath, component: 'label' }; try { @@ -386,21 +356,21 @@ describe('Component Proxy 测试', () => { nodeType: NodeType.EMPTY, position: { x: 1, y: 2, z: 0 }, }; - const testNode = await NodeProxy.createByType(params); + const testNode = await NodeProxy.createNodeByType(params); expect(testNode).toBeDefined(); expect(testNode?.name).toBe('new node'); if (!testNode) { return; } - const cameraComponentInfo = await ComponentProxy.add(addComponentInfo); + const cameraComponentInfo = await ComponentProxy.addComponent(addComponentInfo); componentPath = cameraComponentInfo.path; - expect(cameraComponentInfo.path).toBe(`${addComponentInfo.nodePath}/cc.Label`); + expect(cameraComponentInfo.path).toBe(`${addComponentInfo.nodePathOrUuid}/cc.Label`); const queryComponent: IQueryComponentOptions = { path: nodePath + '/cc.label' }; - await ComponentProxy.query(queryComponent) as IComponent; + await ComponentProxy.queryComponent(queryComponent) as IComponent; } catch (e) { expect(e instanceof Error ? e.message : String(e)).toBe(`This path contains multiple component paths(TestNode/New Node/cc.Label,TestNode/new node/cc.Label). Please specify which one to use.`); @@ -409,12 +379,12 @@ describe('Component Proxy 测试', () => { const removeComponentInfo: IRemoveComponentOptions = { path: `${newNodePath}/cc.Label` }; - const result = await ComponentProxy.remove(removeComponentInfo); + const result = await ComponentProxy.removeComponent(removeComponentInfo); expect(result).toBe(true); } }); - it('setProperty - 设置组件属性 - string类型', async () => { + it('setComponentProperty - 设置组件属性 - string类型', async () => { const queryComponent: IQueryComponentOptions = { path: componentPath }; @@ -428,7 +398,7 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['string'].value).toBe('label'); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['string'].value).toBe('abc'); } catch (e) { console.log(`setComponentProperty test error: ${e}`); @@ -436,12 +406,12 @@ describe('Component Proxy 测试', () => { } }); - it('remove - 删除组件', async () => { + it('removeComponent - 删除组件', async () => { const removeComponentInfo: IRemoveComponentOptions = { path: componentPath }; try { - const result = await ComponentProxy.remove(removeComponentInfo); + const result = await ComponentProxy.removeComponent(removeComponentInfo); expect(result).toBe(true); } catch (e) { console.log(`removeComponent test error: ${e}`); @@ -452,7 +422,7 @@ describe('Component Proxy 测试', () => { path: componentPath }; try { - await ComponentProxy.query(queryComponent) as IComponent; + await ComponentProxy.queryComponent(queryComponent) as IComponent; } catch (e) { expect(e instanceof Error ? e.message : String(e)).toBe(`No component found for this path(${queryComponent.path}).`); } @@ -466,7 +436,7 @@ describe('Component Proxy 测试', () => { afterAll(async () => { try { for (const component of components) { - const result = await ComponentProxy.remove({ path: component.path }); + const result = await ComponentProxy.removeComponent({ path: component.path }); expect(result).toBe(true); }; } catch (e) { @@ -475,18 +445,18 @@ describe('Component Proxy 测试', () => { } console.log('组合测试 - 添加多个不同节点 - 结束'); }); - it('add -添加多个不同节点', async () => { + it('addComponent - 添加多个不同节点', async () => { try { for (const componentName of testComponents) { const componentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: componentName }; - const component = await ComponentProxy.add(componentInfo); + const component = await ComponentProxy.addComponent(componentInfo); expect(component.path).toBe(`${nodePath}/${componentName}`); components.push(component); - const queryComponentInfo = await ComponentProxy.query({ path: component.path }) as IComponent; + const queryComponentInfo = await ComponentProxy.queryComponent({ path: component.path }) as IComponent; if (queryComponentInfo!.cid) { expect(queryComponentInfo!.cid).toBe(componentName); } @@ -509,7 +479,7 @@ describe('Component Proxy 测试', () => { afterAll(async () => { try { for (const component of components) { - const result = await ComponentProxy.remove({ path: component.path }); + const result = await ComponentProxy.removeComponent({ path: component.path }); expect(result).toBe(true); }; } catch (e) { @@ -518,17 +488,17 @@ describe('Component Proxy 测试', () => { } console.log('组合测试 - 添加多个相同节点 - 结束'); }); - it('add -添加多个相同节点', async () => { + it('addComponent - 添加多个相同节点', async () => { try { for (let i = 0; i < testCount; i++) { const componentInfo1: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testComponent }; - const component = await ComponentProxy.add(componentInfo1); + const component = await ComponentProxy.addComponent(componentInfo1); expect(component.path).toBe(`${nodePath}/${testComponent}${i === 0 ? '' : '_' + String(i).padStart(3, '0')}`); components.push(component); - const queryComponentInfo = await ComponentProxy.query({ path: component.path }) as IComponent; + const queryComponentInfo = await ComponentProxy.queryComponent({ path: component.path }) as IComponent; if (queryComponentInfo!.cid) { expect(queryComponentInfo!.cid).toBe(testComponent); } @@ -551,14 +521,14 @@ describe('Component Proxy 测试', () => { // 确保测试了中,没有其他的组件 beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testComponent }; try { - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; expect(component.path).toBe(`${nodePath}/cc.Label`); - componentInfo = await ComponentProxy.query({ path: componentPath }) as IComponent; + componentInfo = await ComponentProxy.queryComponent({ path: componentPath }) as IComponent; expect(componentInfo).toBeDefined(); queryComponent.path = componentPath; } catch (e) { @@ -567,7 +537,7 @@ describe('Component Proxy 测试', () => { }); afterAll(async () => { try { - const result = await ComponentProxy.remove({ path: componentPath }); + const result = await ComponentProxy.removeComponent({ path: componentPath }); expect(result).toBe(true); } catch (e) { console.log(`组合测试 - 添加多个相同节点 - 错误 ${e}`); @@ -575,7 +545,7 @@ describe('Component Proxy 测试', () => { } console.log('组合测试 - 添加多个相同节点 - 结束'); }); - it('setProperty - 设置组件属性 - number类型', async () => { + it('setComponentProperty - 设置组件属性 - number类型', async () => { try { expect(componentInfo?.properties['fontSize'].value).toBe(40); @@ -587,14 +557,14 @@ describe('Component Proxy 测试', () => { }; const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['fontSize'].value).toBe(80); } catch (e) { console.log(`setComponentProperty test error: ${e}`); throw e; } }); - it('setProperty - 设置组件属性 - enum类型', async () => { + it('setComponentProperty - 设置组件属性 - enum类型', async () => { try { const setComponentProperty: ISetPropertyOptions = { componentPath: componentPath, @@ -603,14 +573,14 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['overflow'].value).toBe(0); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['overflow'].value).toBe(1); } catch (e) { console.log(`setComponentProperty test error: ${e}`); throw e; } }); - it('setProperty - 设置组件属性 - boolean类型', async () => { + it('setComponentProperty - 设置组件属性 - boolean类型', async () => { try { const setComponentProperty: ISetPropertyOptions = { componentPath: componentPath, @@ -619,14 +589,14 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['enableOutline'].value).toBe(false); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['enableOutline'].value).toBe(true); } catch (e) { console.log(`setComponentProperty test error: ${e}`); throw e; } }); - it('setProperty - 设置组件属性 - color类型', async () => { + it('setComponentProperty - 设置组件属性 - color类型', async () => { try { const setComponentProperty: ISetPropertyOptions = { componentPath: componentPath, @@ -645,7 +615,7 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['outlineColor'].value.a).toBe(255); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['outlineColor'].value.r).toBe(50); expect(componentInfo?.properties['outlineColor'].value.g).toBe(100); expect(componentInfo?.properties['outlineColor'].value.b).toBe(150); @@ -655,7 +625,7 @@ describe('Component Proxy 测试', () => { throw e; } }); - it('setProperty - 设置组件属性 - 设置枚举类型之外的值', async () => { + it('setComponentProperty - 设置组件属性 - 设置枚举类型之外的值', async () => { try { const setComponentProperty: ISetPropertyOptions = { componentPath: componentPath, @@ -666,7 +636,7 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['overflow'].value).toBe(1); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['overflow'].value).toBe(100000); } catch (e) { console.log(`setComponentProperty test error: ${e}`); @@ -682,14 +652,14 @@ describe('Component Proxy 测试', () => { // 确保测试了中,没有其他的组件 beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testComponent }; try { - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; expect(component.path).toBe(`${nodePath}/cc.Sprite`); - componentInfo = await ComponentProxy.query({ path: componentPath }) as IComponent; + componentInfo = await ComponentProxy.queryComponent({ path: componentPath }) as IComponent; expect(componentInfo).toBeDefined(); queryComponent.path = componentPath; } catch (e) { @@ -699,14 +669,14 @@ describe('Component Proxy 测试', () => { }); afterAll(async () => { try { - const result = await ComponentProxy.remove({ path: componentPath }); + const result = await ComponentProxy.removeComponent({ path: componentPath }); expect(result).toBe(true); } catch (e) { console.log(`组合测试 - 添加多个相同节点 - 错误 ${e}`); throw e; } }); - it('setProperty - 设置组件属性 - 设置SpriteFrame', async () => { + it('setComponentProperty - 设置组件属性 - 设置SpriteFrame', async () => { try { // 对错误的值 类型 会修改失败,但是返回还是true const setComponentProperty: ISetPropertyOptions = { @@ -720,7 +690,7 @@ describe('Component Proxy 测试', () => { expect(componentInfo?.properties['spriteFrame'].value.uuid).toBe(''); const result = await ComponentProxy.setProperty(setComponentProperty); expect(result).toBe(true); - componentInfo = await ComponentProxy.query(queryComponent) as IComponent; + componentInfo = await ComponentProxy.queryComponent(queryComponent) as IComponent; expect(componentInfo?.properties['spriteFrame'].value.uuid).toBe('20835ba4-6145-4fbc-a58a-051ce700aa3e@f9941'); } catch (e) { console.log(`setComponentProperty test error: ${e}`); @@ -741,13 +711,13 @@ describe('Component Proxy 测试', () => { queryChildren: false, queryComponent: true }; - buildinComponentTypes = await ComponentProxy.queryAll(); - const result = await NodeProxy.query(params) as INode | null; + buildinComponentTypes = await ComponentProxy.queryAllComponent(); + const result = await NodeProxy.queryNode(params); expect(result).toBeDefined(); expect(result?.components?.length == 0); }); - it('add -添加内置组件测试 - 这个测试例设计有问题,可以忽略。', async () => { + it('addComponent - 添加内置组件测试 - 这个测试例设计有问题,可以忽略。', async () => { /** * 这个测试例设计有问题,因为内置组件太多,有冲突,有重复(依赖创建组件 会有重复),有无法删除组件(UITransform) * 这样导致很难排除哪些有依赖,哪些有冲突等,因此,只能通过日志的方式输出,查看哪些组件是冲突的。 @@ -771,11 +741,11 @@ describe('Component Proxy 测试', () => { } const componentInfo1: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: componentType }; try { - const component = await ComponentProxy.add(componentInfo1); + const component = await ComponentProxy.addComponent(componentInfo1); createdComponents.push(component); } catch (e) { // 这里会产生冲突、重复组件(因为依赖会创建一些重复组件,导致测试会异常), 这是正常的异常 @@ -789,9 +759,9 @@ describe('Component Proxy 测试', () => { queryChildren: false, queryComponent: true }; - const node = await NodeProxy.query(params) as INode | null; + const node = await NodeProxy.queryNode(params); for (let i = 0; i < node!.components!.length; ++i) { - await ComponentProxy.remove({ path: node!.components!.at(i)!.path }); + await ComponentProxy.removeComponent({ path: node!.components!.at(i)!.path }); } } catch (e) { // 有些移除会失败,因为有依赖,例如 UITransform 、 Label组件,也属于正常的异常,这也属于正常的异常 @@ -816,7 +786,7 @@ describe('Component Proxy 测试', () => { nodeType: NodeType.EMPTY, position: { x: 1, y: 2, z: 0 }, }; - const testNode = await NodeProxy.createByType(params); + const testNode = await NodeProxy.createNodeByType(params); expect(testNode).toBeDefined(); if (!testNode) { return; @@ -830,29 +800,29 @@ describe('Component Proxy 测试', () => { path: nodes[i].path, keepWorldTransform: false }; - await NodeProxy.delete(params); + await NodeProxy.deleteNode(params); expect(params).toBeDefined(); } }); - it('add -每个组件添加同一个组件,但是最后的组件名是一样的,只是节点名称不一样', async () => { + it('addComponent - 每个组件添加同一个组件,但是最后的组件名是一样的,只是节点名称不一样', async () => { try { const testComponent = 'cc.Layout'; for (let i = 0; i < nodes.length; ++i) { const componentInfo1: IAddComponentOptions = { - nodePath: nodes[i].path, + nodePathOrUuid: nodes[i].path, component: testComponent, }; - const component = await ComponentProxy.add(componentInfo1); + const component = await ComponentProxy.addComponent(componentInfo1); expect(component).toBeDefined(); expect(component.path).toBe(`${nodes[i].path}/cc.Layout`); } for (let i = 0; i < nodes.length; ++i) { const componentInfo1: IAddComponentOptions = { - nodePath: nodes[i].path, + nodePathOrUuid: nodes[i].path, component: testComponent, }; - const component = await ComponentProxy.add(componentInfo1); + const component = await ComponentProxy.addComponent(componentInfo1); expect(component).toBeDefined(); expect(component.path).toBe(`${nodes[i].path}/cc.Layout_001`); } @@ -872,7 +842,7 @@ describe('Component Proxy 测试', () => { nodeType: NodeType.EMPTY, position: { x: 1, y: 2, z: 0 }, }; - const testNode = await NodeProxy.createByType(params); + const testNode = await NodeProxy.createNodeByType(params); expect(testNode).toBeDefined(); if (!testNode) { return; @@ -885,45 +855,45 @@ describe('Component Proxy 测试', () => { path: nodePath, keepWorldTransform: false }; - await NodeProxy.delete(params); + await NodeProxy.deleteNode(params); expect(params).toBeDefined(); }); - it('add -添加多个不允许并存的组件', async () => { + it('addComponent - 添加多个不允许并存的组件', async () => { const testComponent = 'cc.Label'; const componentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testComponent, }; - let component = await ComponentProxy.add(componentInfo); + let component = await ComponentProxy.addComponent(componentInfo); expect(component).toBeDefined(); expect(component.path).toBe(`${nodePath}/${testComponent}`); try { - component = await ComponentProxy.add(componentInfo); + component = await ComponentProxy.addComponent(componentInfo); } catch (e) { // 添加接受相同组件添加的错误 expect(e instanceof Error ? e.message : String(e)).toBe(`Can't add component '${testComponent}' because ${nodeName} already contains the same component.`); expect(component.path).toBe(`${nodePath}/${testComponent}`); } - const result = await ComponentProxy.remove({ path: component.path }); + const result = await ComponentProxy.removeComponent({ path: component.path }); expect(result).toBe(true); }); - it('add -添加多个冲突的组件', async () => { + it('addComponent - 添加多个冲突的组件', async () => { const testComponent = 'cc.Sprite'; const testConfictsComponent = 'cc.Line'; const componentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testComponent, }; - let component = await ComponentProxy.add(componentInfo); + let component = await ComponentProxy.addComponent(componentInfo); expect(component).toBeDefined(); expect(component.path).toBe(`${nodePath}/${testComponent}`); try { const componentConficts: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: testConfictsComponent, }; - component = await ComponentProxy.add(componentConficts); + component = await ComponentProxy.addComponent(componentConficts); } catch (e) { // 添加异常冲突 expect(e instanceof Error ? e.message : String(e)).toBe(`Can't add component '${testConfictsComponent}' to ${nodeName} because it conflicts with the existing '${testComponent}' derived component.`); @@ -933,16 +903,16 @@ describe('Component Proxy 测试', () => { }); describe('8. createComponent - 创建组件测试', () => { - it('create - 创建已知组件应返回 true', async () => { + it('createComponent - 创建已知组件应返回 true', async () => { const options: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; try { - const result = await createComponent(options); + const result = await ComponentProxy.createComponent(options); expect(result).toBe(true); // 删除组件 - const removeResult = await ComponentProxy.remove({ path: `${nodePath}/cc.Label` }); + const removeResult = await ComponentProxy.removeComponent({ path: `${nodePath}/cc.Label` }); expect(removeResult).toBe(true); } catch (e) { console.log(`createComponent test error: ${e}`); @@ -950,13 +920,13 @@ describe('Component Proxy 测试', () => { } }); - it('create - 创建不存在组件应抛出异常', async () => { + it('createComponent - 创建不存在组件应抛出异常', async () => { const options: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.NonExistentComponent', }; try { - await createComponent(options); + await ComponentProxy.createComponent(options); } catch (e) { expect(e).toBeDefined(); } @@ -968,22 +938,22 @@ describe('Component Proxy 测试', () => { let componentUuid = ''; beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; componentUuid = component.uuid; }); afterAll(async () => { - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); }); - it('query - cli 返回 IComponent 结构', async () => { + it('queryComponent - cli 返回 IComponent 结构', async () => { const params: IQueryComponentOptions = { path: componentPath, }; - const result = await ComponentProxy.query(params) as IComponent; + const result = await ComponentProxy.queryComponent(params) as IComponent; expect(result).toBeDefined(); // IComponent 有 properties、path、uuid、name、enabled 等直接值字段 expect(result.properties).toBeDefined(); @@ -994,8 +964,8 @@ describe('Component Proxy 测试', () => { expect(typeof result.enabled).toBe('boolean'); expect(result.cid).toBe('cc.Label'); }); - it('query - 返回 IComponentForEditor 结构', async () => { - const result = await ComponentProxy.query(componentPath) as IComponentForEditor; + it('queryComponent - 返回 IComponentForEditor 结构', async () => { + const result = await ComponentProxy.queryComponent(componentPath) as IComponentForEditor; expect(result).toBeDefined(); // IComponentForEditor 有 value(对象,包含编码后的属性)、type、cid、mountedRoot 等字段 expect(result.value).toBeDefined(); @@ -1010,31 +980,23 @@ describe('Component Proxy 测试', () => { expect(value['enabled']).toBeDefined(); } }); - it('query - 传入 uuid 返回 IComponentForEditor 结构', async () => { - const result = await ComponentProxy.query(componentUuid) as IComponentForEditor; - expect(result).toBeDefined(); - expect(result.value).toBeDefined(); - expect(typeof result.value).toBe('object'); - expect(result.type).toBe('cc.Label'); - expect(result.cid).toBe('cc.Label'); - }); }); - describe('10. reset - 重置组件测试', () => { + describe('10. resetComponent - 重置组件测试', () => { let componentPath = ''; beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; }); afterAll(async () => { - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); }); - it('reset - 修改属性后重置应恢复默认值', async () => { + it('resetComponent - 修改属性后重置应恢复默认值', async () => { // 先修改属性 const setComponentProperty: ISetPropertyOptions = { componentPath: componentPath, @@ -1044,69 +1006,60 @@ describe('Component Proxy 测试', () => { expect(setResult).toBe(true); // 确认属性已修改 - let componentInfo = await ComponentProxy.query({ path: componentPath }) as IComponent; + let componentInfo = await ComponentProxy.queryComponent({ path: componentPath }) as IComponent; expect(componentInfo?.properties['string'].value).toBe('modified'); // 重置组件 - const resetResult = await resetComponent({ path: componentPath }); + const resetResult = await ComponentProxy.resetComponent({ path: componentPath }); expect(resetResult).toBe(true); // 验证属性已恢复默认值 - componentInfo = await ComponentProxy.query({ path: componentPath }) as IComponent; + componentInfo = await ComponentProxy.queryComponent({ path: componentPath }) as IComponent; expect(componentInfo?.properties['string'].value).toBe('label'); }); - it('reset - 重置不存在的组件应返回 false', async () => { - const result = await resetComponent({ + it('resetComponent - 重置不存在的组件应返回 false', async () => { + const result = await ComponentProxy.resetComponent({ path: 'non-existent-path/cc.Label_001', }); expect(result).toBe(false); }); }); - describe('11. executeMethod - 执行组件方法测试', () => { + describe('11. executeComponentMethod - 执行组件方法测试', () => { let componentUuid = ''; let componentPath = ''; beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentUuid = component.uuid; componentPath = component.path; }); afterAll(async () => { - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); }); - it('executeMethod - 执行组件上存在的方法', async () => { + it('executeComponentMethod - 执行组件上存在的方法', async () => { try { - await executeComponentMethod({ - path: componentPath, + const result = await ComponentProxy.executeComponentMethod({ + uuid: componentUuid, name: 'onLoad', args: [], }); + expect(typeof result).toBe('boolean'); } catch (e) { // 某些方法可能在编辑器环境中无法执行,记录但不影响测试 console.log(`executeComponentMethod test: ${e}`); } }); - - it('executeMethod - 执行返回非 undefined 值的方法', async () => { - const result = await executeComponentMethod({ - path: componentPath, - name: 'node.getSiblingIndex', - args: [], - }); - expect(result).toBeDefined(); - expect(typeof result).toBe('number'); - }); }); describe('12. queryClasses - 查询注册类名测试', () => { it('queryClasses - 无参数查询所有注册类', async () => { - const result = await queryClasses(); + const result = await ComponentProxy.queryClasses(); expect(result).toBeDefined(); expect(Array.isArray(result)).toBe(true); expect(result.length).toBeGreaterThan(0); @@ -1121,7 +1074,7 @@ describe('Component Proxy 测试', () => { const options: IQueryClassesOptions = { extends: 'cc.Component', }; - const result = await queryClasses(options); + const result = await ComponentProxy.queryClasses(options); expect(result).toBeDefined(); expect(Array.isArray(result)).toBe(true); expect(result.length).toBeGreaterThan(0); @@ -1134,15 +1087,15 @@ describe('Component Proxy 测试', () => { const options: IQueryClassesOptions = { extends: ['cc.Component'], }; - const result = await queryClasses(options); + const result = await ComponentProxy.queryClasses(options); expect(result).toBeDefined(); expect(Array.isArray(result)).toBe(true); expect(result.length).toBeGreaterThan(0); }); it('queryClasses - excludeSelf 排除自身', async () => { - const withSelf = await queryClasses({ extends: 'cc.Component' }); - const withoutSelf = await queryClasses({ extends: 'cc.Component', excludeSelf: true }); + const withSelf = await ComponentProxy.queryClasses({ extends: 'cc.Component' }); + const withoutSelf = await ComponentProxy.queryClasses({ extends: 'cc.Component', excludeSelf: true }); expect(withSelf).toBeDefined(); expect(withoutSelf).toBeDefined(); @@ -1158,89 +1111,91 @@ describe('Component Proxy 测试', () => { const options: IQueryClassesOptions = { extends: 'cc.NonExistentClass', }; - const result = await queryClasses(options); + const result = await ComponentProxy.queryClasses(options); expect(result).toBeDefined(); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(0); }); }); - describe('13. queryFunctionOfNode - 查询节点组件函数测试', () => { + describe('13. queryComponentFunctionOfNode - 查询节点组件函数测试', () => { let componentPath = ''; beforeAll(async () => { const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; }); afterAll(async () => { - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); }); - it('queryFunctionOfNode - 查询有效节点的组件函数', async () => { - const result = await queryComponentFunctionOfNode(nodePath); + it('queryComponentFunctionOfNode - 查询有效节点的组件函数', async () => { + const result = await ComponentProxy.queryComponentFunctionOfNode(nodeId); expect(result).toBeDefined(); expect(typeof result).toBe('object'); }); - it('queryFunctionOfNode - 查询不存在节点返回空对象', async () => { - const result = await queryComponentFunctionOfNode('non-existent-path'); + it('queryComponentFunctionOfNode - 查询不存在节点返回空对象', async () => { + const result = await ComponentProxy.queryComponentFunctionOfNode('non-existent-uuid'); expect(result).toBeDefined(); expect(typeof result).toBe('object'); expect(Object.keys(result).length).toBe(0); }); }); - describe('14. hasScript - 查询组件是否存在脚本测试', () => { - it('hasScript - 内置组件应返回 true', async () => { - const result = await queryComponentHasScript('cc.Label'); + describe('14. queryComponentHasScript - 查询组件是否存在脚本测试', () => { + it('queryComponentHasScript - 内置组件应返回 true', async () => { + const result = await ComponentProxy.queryComponentHasScript('cc.Label'); expect(result).toBe(true); }); - it('hasScript - 另一个内置组件应返回 true', async () => { - const result = await queryComponentHasScript('cc.Sprite'); + it('queryComponentHasScript - 另一个内置组件应返回 true', async () => { + const result = await ComponentProxy.queryComponentHasScript('cc.Sprite'); expect(result).toBe(true); }); - it('hasScript - 不存在的组件应返回 false', async () => { - const result = await queryComponentHasScript('cc.NonExistentComponent'); + it('queryComponentHasScript - 不存在的组件应返回 false', async () => { + const result = await ComponentProxy.queryComponentHasScript('cc.NonExistentComponent'); expect(result).toBe(false); }); - it('hasScript - 空字符串应返回 false', async () => { - const result = await queryComponentHasScript(''); + it('queryComponentHasScript - 空字符串应返回 false', async () => { + const result = await ComponentProxy.queryComponentHasScript(''); expect(result).toBe(false); }); }); describe('15. setPropertyForEditor - Editor 专属设置属性测试', () => { let componentPath = ''; + let nodeUUid = ''; beforeAll(async () => { const queryNodeParam: IQueryNodeParams = { path: nodePath, queryChildren: false, queryComponent: false, }; - const nodeInfo = await NodeProxy.query(queryNodeParam) as INode | null; + const nodeInfo = await NodeProxy.queryNode(queryNodeParam); const addComponentInfo: IAddComponentOptions = { - nodePath: nodePath, + nodePathOrUuid: nodePath, component: 'cc.Label', }; - const component = await ComponentProxy.add(addComponentInfo); + const component = await ComponentProxy.addComponent(addComponentInfo); componentPath = component.path; + nodeUUid = nodeInfo!.nodeId; }); afterAll(async () => { - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); }); it('setPropertyForEditor - 设置 string 属性', async () => { // 先查询获取当前 dump 结构 - const fullComponent = await ComponentProxy.query(componentPath) as IComponentForEditor; + const fullComponent = await ComponentProxy.queryComponent(componentPath) as IComponentForEditor; expect(fullComponent).toBeDefined(); if (fullComponent.value && typeof fullComponent.value === 'object' && !Array.isArray(fullComponent.value)) { @@ -1248,7 +1203,7 @@ describe('Component Proxy 测试', () => { const stringDump = { ...value['string'], value: 'pink-test' }; const result = await ComponentProxy.setProperty({ - nodePath: nodePath, + uuid: nodeUUid, path: '__comps__.2.string', dump: stringDump, record: false @@ -1256,7 +1211,7 @@ describe('Component Proxy 测试', () => { expect(result).toBe(true); // 验证修改生效 - const updated = await ComponentProxy.query({ + const updated = await ComponentProxy.queryComponent({ path: componentPath, }) as IComponent; expect(updated?.properties['string'].value).toBe('pink-test'); @@ -1273,66 +1228,66 @@ describe('Component Proxy 测试', () => { nodeType: NodeType.EMPTY, position: { x: 0, y: 0, z: 0 }, }; - const testNode = await NodeProxy.createByType(params); + const testNode = await NodeProxy.createNodeByType(params); expect(testNode).toBeDefined(); testNodePath = testNode!.path; }); afterAll(async () => { - await NodeProxy.delete({ path: testNodePath, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: testNodePath, keepWorldTransform: false }); }); - it('add - 唯一组件不添加后缀', async () => { - const component = await ComponentProxy.add({ - nodePath: testNodePath, + it('addComponent - 唯一组件不添加后缀', async () => { + const component = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: 'cc.Label', }); expect(component).toBeDefined(); expect(component.path).toBe(`${testNodePath}/cc.Label`); - await ComponentProxy.remove({ path: component.path }); + await ComponentProxy.removeComponent({ path: component.path }); }); - it('add - 两个不同类型组件各自不添加后缀', async () => { - const comp1 = await ComponentProxy.add({ - nodePath: testNodePath, + it('addComponent - 两个不同类型组件各自不添加后缀', async () => { + const comp1 = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: 'cc.Label', }); - const comp2 = await ComponentProxy.add({ - nodePath: testNodePath, + const comp2 = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: 'cc.Layout', }); expect(comp1.path).toBe(`${testNodePath}/cc.Label`); expect(comp2.path).toBe(`${testNodePath}/cc.Layout`); - await ComponentProxy.remove({ path: comp2.path }); - await ComponentProxy.remove({ path: comp1.path }); + await ComponentProxy.removeComponent({ path: comp2.path }); + await ComponentProxy.removeComponent({ path: comp1.path }); }); - it('add - 第二个同类型组件添加_001后缀', async () => { - const comp1 = await ComponentProxy.add({ - nodePath: testNodePath, + it('addComponent - 第二个同类型组件添加_001后缀', async () => { + const comp1 = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: 'cc.Layout', }); expect(comp1.path).toBe(`${testNodePath}/cc.Layout`); - const comp2 = await ComponentProxy.add({ - nodePath: testNodePath, + const comp2 = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: 'cc.Layout', }); expect(comp2.path).toBe(`${testNodePath}/cc.Layout_001`); - await ComponentProxy.remove({ path: comp2.path }); - await ComponentProxy.remove({ path: comp1.path }); + await ComponentProxy.removeComponent({ path: comp2.path }); + await ComponentProxy.removeComponent({ path: comp1.path }); }); - it('add - 多个同类型组件依次添加_001,_002,...后缀', async () => { + it('addComponent - 多个同类型组件依次添加_001,_002,...后缀', async () => { const totalCount = 5; const testComponent = 'cc.Layout'; const components: IComponentIdentifier[] = []; for (let i = 0; i < totalCount; i++) { - const comp = await ComponentProxy.add({ - nodePath: testNodePath, + const comp = await ComponentProxy.addComponent({ + nodePathOrUuid: testNodePath, component: testComponent, }); expect(comp).toBeDefined(); @@ -1345,36 +1300,36 @@ describe('Component Proxy 测试', () => { } for (const comp of components.reverse()) { - await ComponentProxy.remove({ path: comp.path }); + await ComponentProxy.removeComponent({ path: comp.path }); } }); - it('add - 删除中间组件后新增应复用已删除的名称', async () => { + it('addComponent - 删除中间组件后新增应复用已删除的名称', async () => { const testComponent = 'cc.Layout'; // 添加3个同类型组件: cc.Layout, cc.Layout_001, cc.Layout_002 - const comp0 = await ComponentProxy.add({ nodePath: testNodePath, component: testComponent }); - const comp1 = await ComponentProxy.add({ nodePath: testNodePath, component: testComponent }); - const comp2 = await ComponentProxy.add({ nodePath: testNodePath, component: testComponent }); + const comp0 = await ComponentProxy.addComponent({ nodePathOrUuid: testNodePath, component: testComponent }); + const comp1 = await ComponentProxy.addComponent({ nodePathOrUuid: testNodePath, component: testComponent }); + const comp2 = await ComponentProxy.addComponent({ nodePathOrUuid: testNodePath, component: testComponent }); expect(comp0.path).toBe(`${testNodePath}/${testComponent}`); expect(comp1.path).toBe(`${testNodePath}/${testComponent}_001`); expect(comp2.path).toBe(`${testNodePath}/${testComponent}_002`); // 删除 _001 - const removeResult = await ComponentProxy.remove({ path: comp1.path }); + const removeResult = await ComponentProxy.removeComponent({ path: comp1.path }); expect(removeResult).toBe(true); // 再添加2个,第一个应复用 _001,第二个为 _003 - const comp3 = await ComponentProxy.add({ nodePath: testNodePath, component: testComponent }); - const comp4 = await ComponentProxy.add({ nodePath: testNodePath, component: testComponent }); + const comp3 = await ComponentProxy.addComponent({ nodePathOrUuid: testNodePath, component: testComponent }); + const comp4 = await ComponentProxy.addComponent({ nodePathOrUuid: testNodePath, component: testComponent }); expect(comp3.path).toBe(`${testNodePath}/${testComponent}_001`); expect(comp4.path).toBe(`${testNodePath}/${testComponent}_003`); // 清理 - await ComponentProxy.remove({ path: comp4.path }); - await ComponentProxy.remove({ path: comp3.path }); - await ComponentProxy.remove({ path: comp2.path }); - await ComponentProxy.remove({ path: comp0.path }); + await ComponentProxy.removeComponent({ path: comp4.path }); + await ComponentProxy.removeComponent({ path: comp3.path }); + await ComponentProxy.removeComponent({ path: comp2.path }); + await ComponentProxy.removeComponent({ path: comp0.path }); }); }); }); \ No newline at end of file diff --git a/src/core/scene/test/editor-proxy-prefab.testcase.ts b/src/core/scene/test/editor-proxy-prefab.testcase.ts index 03b2a6eb4..609dc9234 100644 --- a/src/core/scene/test/editor-proxy-prefab.testcase.ts +++ b/src/core/scene/test/editor-proxy-prefab.testcase.ts @@ -41,7 +41,7 @@ describe('EditorProxy Prefab 测试', () => { it('save - 通过 UUID 保存预制体', async () => { expect(identifier).toBeTruthy(); - await NodeProxy.createByType({ + await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'prefab-test-node-uuid', @@ -109,7 +109,7 @@ describe('EditorProxy Prefab 测试', () => { it('save - 通过 URL 保存预制体', async () => { expect(instanceAssetURL).toBeTruthy(); - await NodeProxy.createByType({ + await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'prefab-test-node-url', @@ -164,7 +164,7 @@ describe('EditorProxy Prefab 测试', () => { urlOrUUID: SceneTestEnv.prefabURL, }); - const node = await NodeProxy.createByType({ + const node = await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'current-prefab-test-node', @@ -172,8 +172,8 @@ describe('EditorProxy Prefab 测试', () => { expect(node).not.toBeNull(); - const label = await ComponentProxy.add({ - nodePath: node?.path as string, + const label = await ComponentProxy.addComponent({ + nodePathOrUuid: node?.path as string, component: 'cc.Label' }); await ComponentProxy.setProperty({ diff --git a/src/core/scene/test/editor-proxy-scene.testcase.ts b/src/core/scene/test/editor-proxy-scene.testcase.ts index b4b97233d..8723696b5 100644 --- a/src/core/scene/test/editor-proxy-scene.testcase.ts +++ b/src/core/scene/test/editor-proxy-scene.testcase.ts @@ -35,7 +35,7 @@ describe('EditorProxy Scene 测试', () => { expect(identifier).toBeTruthy(); if (!identifier) return; - await NodeProxy.createByType({ + await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'scene-test-node-uuid', @@ -94,7 +94,7 @@ describe('EditorProxy Scene 测试', () => { await EditorProxy.open({ urlOrUUID: SceneTestEnv.sceneURL, }); - await NodeProxy.createByType({ + await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'scene-test-node-url', @@ -139,7 +139,7 @@ describe('EditorProxy Scene 测试', () => { await EditorProxy.open({ urlOrUUID: SceneTestEnv.sceneURL, }); - await NodeProxy.createByType({ + await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'current-scene-test-node', diff --git a/src/core/scene/test/engine-proxy.testcase.ts b/src/core/scene/test/engine-proxy.testcase.ts index 740db76b9..be4523dd9 100644 --- a/src/core/scene/test/engine-proxy.testcase.ts +++ b/src/core/scene/test/engine-proxy.testcase.ts @@ -30,7 +30,7 @@ describe('Engine Proxy 测试', () => { const eventSceneUpdatePromise = utils.once(sceneWorker, 'engine:update'); const eventSceneTickedPromise = utils.once(sceneWorker, 'engine:ticked'); - const createdNode = await NodeProxy.createByType({ + const createdNode = await NodeProxy.createNodeByType({ path: '', name: 'TestNode', nodeType: NodeType.EMPTY, @@ -46,7 +46,7 @@ describe('Engine Proxy 测试', () => { const eventSceneUpdatePromise = utils.once(sceneWorker, 'engine:update'); const eventSceneTickedPromise = utils.once(sceneWorker, 'engine:ticked'); - await NodeProxy.update({ + await NodeProxy.updateNode({ path: nodePath, properties: { position: { x: 5, y: 5, z: 5 } @@ -62,8 +62,8 @@ describe('Engine Proxy 测试', () => { const eventSceneUpdatePromise = utils.once(sceneWorker, 'engine:update'); const eventSceneTickedPromise = utils.once(sceneWorker, 'engine:ticked'); - const component = await ComponentProxy.add({ - nodePath: nodePath, + const component = await ComponentProxy.addComponent({ + nodePathOrUuid: nodePath, component: 'cc.Label' }); componentPath = component.path; @@ -93,7 +93,7 @@ describe('Engine Proxy 测试', () => { const eventSceneUpdatePromise = utils.once(sceneWorker, 'engine:update'); const eventSceneTickedPromise = utils.once(sceneWorker, 'engine:ticked'); - await ComponentProxy.remove({ path: componentPath }); + await ComponentProxy.removeComponent({ path: componentPath }); await eventSceneUpdatePromise; await eventSceneTickedPromise; @@ -104,7 +104,7 @@ describe('Engine Proxy 测试', () => { const eventSceneUpdatePromise = utils.once(sceneWorker, 'engine:update'); const eventSceneTickedPromise = utils.once(sceneWorker, 'engine:ticked'); - await NodeProxy.delete({ + await NodeProxy.deleteNode({ path: nodePath, keepWorldTransform: false }); diff --git a/src/core/scene/test/node-dump-proxy.testcase.ts b/src/core/scene/test/node-dump-proxy.testcase.ts deleted file mode 100644 index 814aa87d9..000000000 --- a/src/core/scene/test/node-dump-proxy.testcase.ts +++ /dev/null @@ -1,491 +0,0 @@ -import { - type ICreateByNodeTypeParams, - type IDeleteNodeParams, - type IQueryNodeParams, - type INode, - type INodeForEditor, - type ISetPropertyOptionsForEditor, - NodeType, -} from '../common'; -import { type ISceneForEditor } from '../common/editor/scene'; -import { NodeProxy } from '../main-process/proxy/node-proxy'; -import { EditorProxy } from '../main-process/proxy/editor-proxy'; -import { Rpc } from '../main-process/rpc'; -import { SceneTestEnv } from './scene-test-env'; - -// 这些接口未在 IPublicNodeService 中暴露,测试中直接通过 RPC 调用 -const rpcRequest = (method: string, args?: any[]) => - (Rpc.getInstance() as any).request('Node', method, args); - -function queryNodeDump(path: string): Promise { - return rpcRequest('query', [path]); -} - -function setNodeProperty(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('setProperty', [options]); -} - -function previewSetNodeProperty(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('previewSetProperty', [options]); -} - -function cancelPreviewSetNodeProperty(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('cancelPreviewSetProperty', [options]); -} - -function resetNode(path: string): Promise { - return rpcRequest('reset', [path]); -} - -function resetNodeProperty(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('resetProperty', [options]); -} - -function updateNodePropertyFromNull(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('updatePropertyFromNull', [options]); -} - -function setNodeAndChildrenLayer(options: ISetPropertyOptionsForEditor): Promise { - return rpcRequest('setNodeAndChildrenLayer', [options]); -} - -describe('Node Dump Proxy 测试', () => { - let testNode: INode | null = null; - let testNodeUuid = ''; - const testNodeName = 'DumpTestNode'; - - beforeAll(async () => { - await EditorProxy.open({ - urlOrUUID: SceneTestEnv.sceneURL, - }); - const params: ICreateByNodeTypeParams = { - path: '/', - name: testNodeName, - nodeType: NodeType.EMPTY, - }; - testNode = await NodeProxy.createByType(params); - expect(testNode).toBeDefined(); - - // 通过 queryNode 获取节点 UUID - const queryParams: IQueryNodeParams = { - path: testNode!.path, - queryChildren: false, - queryComponent: false, - }; - const nodeInfo = await NodeProxy.query(queryParams) as INode | null; - expect(nodeInfo).not.toBeNull(); - testNodeUuid = nodeInfo!.nodeId; - }); - - afterAll(async () => { - if (testNode) { - await NodeProxy.delete({ path: testNode.path, keepWorldTransform: false }); - } - await EditorProxy.close({ - urlOrUUID: SceneTestEnv.sceneURL, - }); - }); - - describe('8. query - 查询节点 dump 数据', () => { - it('query - 查询有效节点返回 dump 数据', async () => { - const dump = await queryNodeDump(testNode!.path); - expect(dump).not.toBeNull(); - expect(dump).toBeDefined(); - }); - - it('query - dump 包含必要字段', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(dump).not.toBeNull(); - - // 基本属性字段 - expect(dump.name).toBeDefined(); - expect(dump.name.value).toBe(testNodeName); - expect(dump.active).toBeDefined(); - expect(dump.active.value).toBe(true); - expect(dump.position).toBeDefined(); - expect(dump.rotation).toBeDefined(); - expect(dump.scale).toBeDefined(); - expect(dump.layer).toBeDefined(); - expect(dump.uuid).toBeDefined(); - - // 结构字段 - expect(dump.__comps__).toBeDefined(); - expect(Array.isArray(dump.__comps__)).toBe(true); - expect(dump.__type__).toBeDefined(); - }); - - it('query - 查询不存在的节点返回 null', async () => { - const dump = await queryNodeDump('non-existent-path'); - expect(dump).toBeNull(); - }); - - it('query - 不传参数返回根节点 dump 数据', async () => { - const dump = await rpcRequest('query', []); - expect(dump).not.toBeNull(); - const sceneResult = dump as ISceneForEditor; - expect(sceneResult.__type__).toBeDefined(); - expect(sceneResult.uuid).toBeDefined(); - expect(Array.isArray(sceneResult.children)).toBe(true); - }); - }); - - describe('9. setProperty - 设置节点属性', () => { - it('setProperty - 修改节点位置', async () => { - // 先获取当前 dump 作为模板 - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const positionDump = { ...dump.position, value: { x: 100, y: 200, z: 0 } }; - - const options: ISetPropertyOptionsForEditor = { - nodePath: testNode!.path, - path: 'position', - dump: positionDump, - }; - const result = await setNodeProperty(options); - expect(result).toBe(true); - - // 验证修改生效 - const updatedDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(updatedDump.position.value).toEqual({ x: 100, y: 200, z: 0 }); - }); - - it('setProperty - 修改节点名称', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const nameDump = { ...dump.name, value: 'RenamedNode' }; - - const result = await setNodeProperty({ - nodePath: testNode!.path, - path: 'name', - dump: nameDump, - }); - expect(result).toBe(true); - - const updatedDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(updatedDump.name.value).toBe('RenamedNode'); - - // 还原名称 - const restoreDump = { ...updatedDump.name, value: testNodeName }; - await setNodeProperty({ - nodePath: testNode!.path, - path: 'name', - dump: restoreDump, - }); - }); - - it('setProperty - 修改节点 active 状态', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const activeDump = { ...dump.active, value: false }; - - const result = await setNodeProperty({ - nodePath: testNode!.path, - path: 'active', - dump: activeDump, - }); - expect(result).toBe(true); - - const updatedDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(updatedDump.active.value).toBe(false); - - // 还原 - await setNodeProperty({ - nodePath: testNode!.path, - path: 'active', - dump: { ...updatedDump.active, value: true }, - }); - }); - - it('setProperty - 修改节点缩放', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const scaleDump = { ...dump.scale, value: { x: 2, y: 2, z: 2 } }; - - const result = await setNodeProperty({ - nodePath: testNode!.path, - path: 'scale', - dump: scaleDump, - }); - expect(result).toBe(true); - - const updatedDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(updatedDump.scale.value).toEqual({ x: 2, y: 2, z: 2 }); - - // 还原 - await setNodeProperty({ - nodePath: testNode!.path, - path: 'scale', - dump: { ...updatedDump.scale, value: { x: 1, y: 1, z: 1 } }, - }); - }); - - it('setProperty - 不存在的节点返回 false', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const result = await setNodeProperty({ - nodePath: 'non-existent-path', - path: 'position', - dump: dump.position, - }); - expect(result).toBe(false); - }); - }); - - describe('10. previewSetProperty / cancelPreviewSetProperty - 预览与取消', () => { - let labelNodeUuid = ''; - let labelNode: INode | null = null; - - beforeAll(async () => { - // 创建 Label 节点,自带组件,适合测试多层路径的预览 - labelNode = await NodeProxy.createByType({ - path: '/', - name: 'PreviewTestLabel', - nodeType: NodeType.LABEL, - }); - expect(labelNode).toBeDefined(); - - const nodeInfo = await NodeProxy.query({ - path: labelNode!.path, - queryChildren: false, - queryComponent: false, - }) as INode | null; - labelNodeUuid = nodeInfo!.nodeId; - }); - - afterAll(async () => { - if (labelNode) { - await NodeProxy.delete({ path: labelNode.path, keepWorldTransform: false }); - } - }); - - it('预览修改组件属性后取消,值应恢复', async () => { - // 获取原始 dump,找到 Label 组件的 string 属性 - const originalDump = await queryNodeDump(labelNode!.path) as INodeForEditor; - expect(originalDump.__comps__.length).toBeGreaterThan(0); - - // 找到 cc.Label 组件的索引(通常在 UITransform 之后) - let labelCompIndex = -1; - for (let i = 0; i < originalDump.__comps__.length; i++) { - const comp = originalDump.__comps__[i]; - if (comp.type === 'cc.Label') { - labelCompIndex = i; - break; - } - } - expect(labelCompIndex).toBeGreaterThanOrEqual(0); - - const labelComp = originalDump.__comps__[labelCompIndex]; - const compValue = labelComp.value as Record; - const originalString = compValue['string'].value; - const stringDump = { ...compValue['string'], value: 'preview-test-value' }; - const previewPath = `__comps__.${labelCompIndex}.string`; - - // 预览修改 - const previewResult = await previewSetNodeProperty({ - nodePath: labelNode!.path, - path: previewPath, - dump: stringDump, - }); - expect(previewResult).toBe(true); - - // 验证预览已生效 - const previewedDump = await queryNodeDump(labelNode!.path) as INodeForEditor; - const previewedComp = previewedDump.__comps__[labelCompIndex].value as Record; - expect(previewedComp['string'].value).toBe('preview-test-value'); - - // 取消预览 - const cancelResult = await cancelPreviewSetNodeProperty({ - nodePath: labelNode!.path, - path: previewPath, - dump: stringDump, - }); - expect(cancelResult).toBe(true); - - // 验证已恢复原值 - const restoredDump = await queryNodeDump(labelNode!.path) as INodeForEditor; - const restoredComp = restoredDump.__comps__[labelCompIndex].value as Record; - expect(restoredComp['string'].value).toBe(originalString); - }); - - it('预览修改后正式提交,值应保留', async () => { - const originalDump = await queryNodeDump(labelNode!.path) as INodeForEditor; - - let labelCompIndex = -1; - for (let i = 0; i < originalDump.__comps__.length; i++) { - if (originalDump.__comps__[i].type === 'cc.Label') { - labelCompIndex = i; - break; - } - } - expect(labelCompIndex).toBeGreaterThanOrEqual(0); - - const compValue = originalDump.__comps__[labelCompIndex].value as Record; - const stringDump = { ...compValue['string'], value: 'committed-value' }; - const previewPath = `__comps__.${labelCompIndex}.string`; - - // 预览修改 - await previewSetNodeProperty({ - nodePath: labelNode!.path, - path: previewPath, - dump: stringDump, - }); - - // 正式提交相同的值 - const commitResult = await setNodeProperty({ - nodePath: labelNode!.path, - path: previewPath, - dump: stringDump, - }); - expect(commitResult).toBe(true); - - // 验证值已保留 - const committedDump = await queryNodeDump(labelNode!.path) as INodeForEditor; - const committedComp = committedDump.__comps__[labelCompIndex].value as Record; - expect(committedComp['string'].value).toBe('committed-value'); - }); - }); - - describe('11. reset - 重置节点变换', () => { - it('reset - 修改后重置,变换属性恢复默认', async () => { - // 先修改位置和缩放 - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - await setNodeProperty({ - nodePath: testNode!.path, - path: 'position', - dump: { ...dump.position, value: { x: 100, y: 200, z: 300 } }, - }); - await setNodeProperty({ - nodePath: testNode!.path, - path: 'scale', - dump: { ...dump.scale, value: { x: 5, y: 5, z: 5 } }, - }); - - // 重置节点 - const result = await resetNode(testNode!.path); - expect(result).toBe(true); - - // 验证变换属性恢复默认 - const resetDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(resetDump.position.value).toEqual({ x: 0, y: 0, z: 0 }); - expect(resetDump.scale.value).toEqual({ x: 1, y: 1, z: 1 }); - }); - }); - - describe('12. resetProperty - 重置单个属性', () => { - it('resetProperty - 重置位置属性', async () => { - // 先修改位置 - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - await setNodeProperty({ - nodePath: testNode!.path, - path: 'position', - dump: { ...dump.position, value: { x: 42, y: 42, z: 42 } }, - }); - - // 重置 position - const result = await resetNodeProperty({ - nodePath: testNode!.path, - path: 'position', - dump: dump.position, - }); - expect(result).toBe(true); - - const resetDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(resetDump.position.value).toEqual({ x: 0, y: 0, z: 0 }); - }); - - it('resetProperty - 重置缩放属性', async () => { - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - await setNodeProperty({ - nodePath: testNode!.path, - path: 'scale', - dump: { ...dump.scale, value: { x: 3, y: 3, z: 3 } }, - }); - - const result = await resetNodeProperty({ - nodePath: testNode!.path, - path: 'scale', - dump: dump.scale, - }); - expect(result).toBe(true); - - const resetDump = await queryNodeDump(testNode!.path) as INodeForEditor; - expect(resetDump.scale.value).toEqual({ x: 1, y: 1, z: 1 }); - }); - }); - - describe('13. setNodeAndChildrenLayer - 递归设置 layer', () => { - let parentNode: INode | null = null; - let childNode: INode | null = null; - let parentUuid = ''; - let childUuid = ''; - - beforeAll(async () => { - // 创建父节点 - parentNode = await NodeProxy.createByType({ - path: '/', - name: 'LayerParent', - nodeType: NodeType.EMPTY, - }); - expect(parentNode).toBeDefined(); - - // 创建子节点 - childNode = await NodeProxy.createByType({ - path: parentNode!.path, - name: 'LayerChild', - nodeType: NodeType.EMPTY, - }); - expect(childNode).toBeDefined(); - - // 获取 UUID - const parentInfo = await NodeProxy.query({ - path: parentNode!.path, - queryChildren: false, - queryComponent: false, - }) as INode | null; - parentUuid = parentInfo!.nodeId; - - const childInfo = await NodeProxy.query({ - path: childNode!.path, - queryChildren: false, - queryComponent: false, - }) as INode | null; - childUuid = childInfo!.nodeId; - }); - - afterAll(async () => { - if (parentNode) { - await NodeProxy.delete({ path: parentNode.path, keepWorldTransform: false }); - } - }); - - it('setNodeAndChildrenLayer - 父子节点 layer 统一设置', async () => { - const dump = await queryNodeDump(parentNode!.path) as INodeForEditor; - const targetLayer = 1 << 25; // UI_2D layer - const layerDump = { ...dump.layer, value: targetLayer }; - - await setNodeAndChildrenLayer({ - nodePath: parentNode!.path, - path: 'layer', - dump: layerDump, - }); - - // 验证父节点 - const parentDump = await queryNodeDump(parentNode!.path) as INodeForEditor; - expect(parentDump.layer.value).toBe(targetLayer); - - // 验证子节点 - const childDump = await queryNodeDump(childNode!.path) as INodeForEditor; - expect(childDump.layer.value).toBe(targetLayer); - }); - }); - - describe('14. updatePropertyFromNull - 初始化 null 属性', () => { - it('updatePropertyFromNull - 调用不报错', async () => { - // 该接口用于将 null 类型属性初始化为可编辑值 - // 对于 Empty 节点的基本属性(position 等),不存在 null 情况 - // 这里验证接口调用不抛异常即可 - const dump = await queryNodeDump(testNode!.path) as INodeForEditor; - const result = await updateNodePropertyFromNull({ - nodePath: testNode!.path, - path: 'position', - dump: dump.position, - }); - expect(typeof result).toBe('boolean'); - }); - }); -}); diff --git a/src/core/scene/test/node-proxy.testcase.ts b/src/core/scene/test/node-proxy.testcase.ts index 8e691b91a..adc2081de 100644 --- a/src/core/scene/test/node-proxy.testcase.ts +++ b/src/core/scene/test/node-proxy.testcase.ts @@ -6,10 +6,8 @@ import { type IQueryNodeTreeParams, type IUpdateNodeParams, type INode, - type INodeForEditor, NodeType, } from '../common'; -import { type ISceneForEditor } from '../common/editor/scene'; import { IVec3 } from '../common/value-types'; import { NodeProxy } from '../main-process/proxy/node-proxy'; import { SceneTestEnv } from './scene-test-env'; @@ -33,7 +31,7 @@ describe('Node Proxy 测试', () => { }); describe('1. 基础节点操作', () => { - it('createByType - 创建多级父节点的节点', async () => { + it('createNode - 创建多级父节点的节点', async () => { const multiParentPath = 'Canvas/TestNode/TestNode2/TestNode3'; const params: ICreateByNodeTypeParams = { path: multiParentPath, @@ -42,14 +40,14 @@ describe('Node Proxy 测试', () => { position: testPosition }; - createdNode = await NodeProxy.createByType(params); + createdNode = await NodeProxy.createNodeByType(params); expect(createdNode).toBeDefined(); expect(createdNode?.name).toBe('TestNode'); expect(createdNode?.path).toBe(multiParentPath + '/TestNode'); }); - it('createByAsset - 创建带预制体的节点', async () => { + it('createNode - 创建带预制体的节点', async () => { const params: ICreateByAssetParams = { dbURL: 'db://internal/default_prefab/ui/Label.prefab', @@ -57,13 +55,13 @@ describe('Node Proxy 测试', () => { name: 'PrefabNode', }; - const prefabNode = await NodeProxy.createByAsset(params); + const prefabNode = await NodeProxy.createNodeByAsset(params); expect(prefabNode).toBeDefined(); expect(prefabNode?.name).toBe('PrefabNode'); console.log('Created prefab node path=', prefabNode?.path); }); - it('createByType - 创建新节点', async () => { + it('createNode - 创建新节点', async () => { const params: ICreateByNodeTypeParams = { path: testNodePath, name: 'TestNode', @@ -71,7 +69,7 @@ describe('Node Proxy 测试', () => { position: testPosition }; - createdNode = await NodeProxy.createByType(params); + createdNode = await NodeProxy.createNodeByType(params); expect(createdNode).toBeDefined(); expect(createdNode?.name).toBe('TestNode'); // 会在根节点下先创建 TestNode 再创建 Canvas/TestNode (SPRITE 节点会在 Canvas 下创建, 节点重名为 ‘TestNode’) @@ -82,7 +80,7 @@ describe('Node Proxy 测试', () => { }); describe('2. 节点查询操作(依赖创建的节点)', () => { - it('query - 查询节点基本信息', async () => { + it('queryNode - 查询节点基本信息', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const params: IQueryNodeParams = { @@ -91,14 +89,14 @@ describe('Node Proxy 测试', () => { queryComponent: true }; - const result = await NodeProxy.query(params) as INode | null; + const result = await NodeProxy.queryNode(params); expect(result).toBeDefined(); expect(result?.path).toBe('TestNode/Canvas/TestNode'); expect(result?.name).toBe('TestNode'); } }); - it('query - 查询节点及子节点信息', async () => { + it('queryNode - 查询节点及子节点信息', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const params: IQueryNodeParams = { @@ -107,50 +105,14 @@ describe('Node Proxy 测试', () => { queryComponent: false }; - const result = await NodeProxy.query(params) as INode | null; + const result = await NodeProxy.queryNode(params); expect(result).toBeDefined(); } }); }); - describe('2.1 query - 编辑器模式与空参数查询', () => { - it('query - 传入 string 返回 INodeForEditor', async () => { - expect(createdNode).not.toBeNull(); - if (createdNode) { - const result = await NodeProxy.query(createdNode.path) as INodeForEditor | null; - expect(result).not.toBeNull(); - expect(result!.name).toBeDefined(); - expect(result!.name.value).toBe('TestNode'); - expect(result!.active).toBeDefined(); - expect(result!.position).toBeDefined(); - expect(result!.rotation).toBeDefined(); - expect(result!.scale).toBeDefined(); - expect(result!.layer).toBeDefined(); - expect(result!.uuid).toBeDefined(); - expect(result!.__comps__).toBeDefined(); - expect(result!.__type__).toBeDefined(); - } - }); - - it('query - 不传参数返回根节点 dump 数据', async () => { - const result = await NodeProxy.query(); - expect(result).not.toBeNull(); - // 根节点是场景,应包含 isScene 字段 - const sceneResult = result as ISceneForEditor; - expect(sceneResult.isScene).toBeTruthy(); - expect(sceneResult.__type__).toBeDefined(); - expect(sceneResult.uuid).toBeDefined(); - expect(Array.isArray(sceneResult.children)).toBe(true); - }); - - it('query - 传入不存在的路径返回 null', async () => { - const result = await NodeProxy.query('non-existent-path'); - expect(result).toBeNull(); - }); - }); - describe('3. 节点更新操作(依赖创建的节点)', () => { - it('update - 更新节点位置', async () => { + it('updateNode - 更新节点位置', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const newPosition: IVec3 = { x: 5, y: 5, z: 5 }; @@ -162,7 +124,7 @@ describe('Node Proxy 测试', () => { } }; - const result = await NodeProxy.update(params); + const result = await NodeProxy.updateNode(params); expect(result).toBeDefined(); expect(result?.path).toBe(createdNode.path); @@ -172,12 +134,12 @@ describe('Node Proxy 测试', () => { queryChildren: false, queryComponent: true }; - const updatedNode = await NodeProxy.query(queryParams) as INode | null; + const updatedNode = await NodeProxy.queryNode(queryParams); expect(updatedNode?.properties.position).toEqual(newPosition); } }); - it('update - 更新节点激活状态', async () => { + it('updateNode - 更新节点激活状态', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const params: IUpdateNodeParams = { @@ -188,7 +150,7 @@ describe('Node Proxy 测试', () => { } }; - const result = await NodeProxy.update(params); + const result = await NodeProxy.updateNode(params); expect(result).toBeDefined(); // 验证更新是否生效 @@ -197,12 +159,12 @@ describe('Node Proxy 测试', () => { queryChildren: false, queryComponent: true }; - const updatedNode = await NodeProxy.query(queryParams) as INode | null; + const updatedNode = await NodeProxy.queryNode(queryParams); expect(updatedNode?.properties.active).toBe(false); } }); - it('update - 更新节点旋转和缩放', async () => { + it('updateNode - 更新节点旋转和缩放', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const newScale: IVec3 = { x: 2, y: 2, z: 2 }; @@ -215,7 +177,7 @@ describe('Node Proxy 测试', () => { } }; - const result = await NodeProxy.update(params); + const result = await NodeProxy.updateNode(params); expect(result).toBeDefined(); // 验证更新是否生效 @@ -224,14 +186,14 @@ describe('Node Proxy 测试', () => { queryChildren: false, queryComponent: true }; - const updatedNode = await NodeProxy.query(queryParams) as INode | null; + const updatedNode = await NodeProxy.queryNode(queryParams); expect(updatedNode?.properties.scale).toEqual(newScale); } }); }); describe('4. 节点删除操作(依赖创建的节点)', () => { - it('delete - 删除节点(不保持世界变换)', async () => { + it('deleteNode - 删除节点(不保持世界变换)', async () => { expect(createdNode).not.toBeNull(); if (createdNode) { const params: IDeleteNodeParams = { @@ -239,7 +201,7 @@ describe('Node Proxy 测试', () => { keepWorldTransform: false }; - const result = await NodeProxy.delete(params); + const result = await NodeProxy.deleteNode(params); expect(result).toBeDefined(); expect(result?.path).toBe(createdNode.path); @@ -249,14 +211,14 @@ describe('Node Proxy 测试', () => { queryChildren: false, queryComponent: true }; - const deletedNode = await NodeProxy.query(queryParams) as INode | null; + const deletedNode = await NodeProxy.queryNode(queryParams); expect(deletedNode).toBeNull(); createdNode = null; } }); - it('delete - 删除节点(保持世界变换)', async () => { + it('deleteNode - 删除节点(保持世界变换)', async () => { // 先创建一个新节点用于删除测试 const createParams: ICreateByNodeTypeParams = { path: 'NodeToDelete', @@ -265,7 +227,7 @@ describe('Node Proxy 测试', () => { workMode: '3d' }; - const tempNode = await NodeProxy.createByType(createParams); + const tempNode = await NodeProxy.createNodeByType(createParams); expect(tempNode).toBeDefined(); // 删除该节点 @@ -274,25 +236,25 @@ describe('Node Proxy 测试', () => { keepWorldTransform: true }; - const result = await NodeProxy.delete(deleteParams); + const result = await NodeProxy.deleteNode(deleteParams); expect(result).toBeDefined(); expect(result?.path).toBe('NodeToDelete/NodeToDelete'); }); }); describe('5. 边界情况测试', () => { - it('query - 查询不存在的节点应返回null', async () => { + it('queryNode - 查询不存在的节点应返回null', async () => { const params: IQueryNodeParams = { path: '/NonExistentNode', queryChildren: false, queryComponent: false }; - const result = await NodeProxy.query(params) as INode | null; + const result = await NodeProxy.queryNode(params); expect(result).toBeNull(); }); - it('update - 更新不存在的节点应抛异常', async () => { + it('updateNode - 更新不存在的节点应抛异常', async () => { const params: IUpdateNodeParams = { path: '/NonExistentNode', name: 'NonExistentNode', @@ -301,16 +263,16 @@ describe('Node Proxy 测试', () => { } }; - await expect(NodeProxy.update(params)).rejects.toThrow(); + await expect(NodeProxy.updateNode(params)).rejects.toThrow(); }); - it('delete - 删除不存在的节点应返回null', async () => { + it('deleteNode - 删除不存在的节点应返回null', async () => { const params: IDeleteNodeParams = { path: '/NonExistentNode', keepWorldTransform: false }; - const result = await NodeProxy.delete(params); + const result = await NodeProxy.deleteNode(params); expect(result).toBeNull(); }); }); @@ -326,7 +288,7 @@ describe('Node Proxy 测试', () => { keepWorldTransform: true }; - const result = await NodeProxy.delete(deleteParams); + const result = await NodeProxy.deleteNode(deleteParams); expect(result).toBeDefined(); expect(result?.path).toBe(node!.path); }; @@ -335,7 +297,7 @@ describe('Node Proxy 测试', () => { throw e; } }); - it('createByType - 创建所有内置节点', async () => { + it('createNode - 创建所有内置节点', async () => { const addCanvas: NodeType[] = [ NodeType.SPRITE, @@ -369,7 +331,7 @@ describe('Node Proxy 测试', () => { continue; } try { - createdNode = await NodeProxy.createByType(params); + createdNode = await NodeProxy.createNodeByType(params); expect(createdNode).toBeDefined(); allNodes.push(createdNode!); @@ -470,7 +432,7 @@ describe('Node Proxy 测试', () => { name: 'TreeTestNode', nodeType: NodeType.EMPTY, }; - const created = await NodeProxy.createByType(createParams); + const created = await NodeProxy.createNodeByType(createParams); expect(created).toBeDefined(); const params: IQueryNodeTreeParams = { path: created!.path }; @@ -480,7 +442,7 @@ describe('Node Proxy 测试', () => { expect(subtree!.isScene).toBe(false); // 清理 - await NodeProxy.delete({ path: created!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: created!.path, keepWorldTransform: false }); }); it('queryNodeTree - 查询不存在的路径应返回 null', async () => { @@ -496,7 +458,7 @@ describe('Node Proxy 测试', () => { name: 'CompTreeTestNode', nodeType: NodeType.SPRITE, }; - const created = await NodeProxy.createByType(createParams); + const created = await NodeProxy.createNodeByType(createParams); expect(created).toBeDefined(); const tree = await NodeProxy.queryNodeTree({ path: created!.path }); @@ -511,7 +473,7 @@ describe('Node Proxy 测试', () => { } // 清理 - await NodeProxy.delete({ path: created!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: created!.path, keepWorldTransform: false }); }); }); @@ -522,46 +484,46 @@ describe('Node Proxy 测试', () => { afterAll(async () => { for (const node of createdNodes.reverse()) { try { - await NodeProxy.delete({ path: node.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: node.path, keepWorldTransform: false }); } catch (e) { console.log(`删除节点失败: ${node.path}, ${e}`); } } }); - it('createByType - 唯一名称不添加后缀', async () => { + it('createNode - 唯一名称不添加后缀', async () => { const params: ICreateByNodeTypeParams = { path: parentPath, name: 'UniqueNode', nodeType: NodeType.EMPTY, }; - const node = await NodeProxy.createByType(params); + const node = await NodeProxy.createNodeByType(params); expect(node).toBeDefined(); expect(node!.name).toBe('UniqueNode'); expect(node!.path).toBe('UniqueNode'); createdNodes.push(node!); }); - it('createByType - 第二个同名节点添加_001后缀', async () => { + it('createNode - 第二个同名节点添加_001后缀', async () => { const params: ICreateByNodeTypeParams = { path: parentPath, name: 'DupNode', nodeType: NodeType.EMPTY, }; - const node1 = await NodeProxy.createByType(params); + const node1 = await NodeProxy.createNodeByType(params); expect(node1).toBeDefined(); expect(node1!.name).toBe('DupNode'); expect(node1!.path).toBe('DupNode'); createdNodes.push(node1!); - const node2 = await NodeProxy.createByType(params); + const node2 = await NodeProxy.createNodeByType(params); expect(node2).toBeDefined(); expect(node2!.name).toBe('DupNode_001'); expect(node2!.path).toBe('DupNode_001'); createdNodes.push(node2!); }); - it('createByType - 多个同名节点依次添加_001,_002,...后缀', async () => { + it('createNode - 多个同名节点依次添加_001,_002,...后缀', async () => { const totalCount = 5; const baseName = 'MultiDupNode'; for (let i = 0; i < totalCount; i++) { @@ -570,7 +532,7 @@ describe('Node Proxy 测试', () => { name: baseName, nodeType: NodeType.EMPTY, }; - const node = await NodeProxy.createByType(params); + const node = await NodeProxy.createNodeByType(params); expect(node).toBeDefined(); const expectedName = i === 0 ? baseName : `${baseName}_${String(i).padStart(3, '0')}`; expect(node!.name).toBe(expectedName); @@ -579,32 +541,32 @@ describe('Node Proxy 测试', () => { } }); - it('createByType - 删除中间节点后新增应复用已删除的名称', async () => { + it('createNode - 删除中间节点后新增应复用已删除的名称', async () => { const baseName = 'GapNode'; // 添加3个同名节点: GapNode, GapNode_001, GapNode_002 - const node0 = await NodeProxy.createByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); - const node1 = await NodeProxy.createByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); - const node2 = await NodeProxy.createByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); + const node0 = await NodeProxy.createNodeByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); + const node1 = await NodeProxy.createNodeByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); + const node2 = await NodeProxy.createNodeByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); expect(node0!.path).toBe(baseName); expect(node1!.path).toBe(`${baseName}_001`); expect(node2!.path).toBe(`${baseName}_002`); // 删除 _001 - const deleteResult = await NodeProxy.delete({ path: node1!.path, keepWorldTransform: false }); + const deleteResult = await NodeProxy.deleteNode({ path: node1!.path, keepWorldTransform: false }); expect(deleteResult).toBeDefined(); // 再添加2个,第一个应复用 _001,第二个为 _003 - const node3 = await NodeProxy.createByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); - const node4 = await NodeProxy.createByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); + const node3 = await NodeProxy.createNodeByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); + const node4 = await NodeProxy.createNodeByType({ path: parentPath, name: baseName, nodeType: NodeType.EMPTY }); expect(node3!.path).toBe(`${baseName}_001`); expect(node4!.path).toBe(`${baseName}_003`); // 清理 - await NodeProxy.delete({ path: node4!.path, keepWorldTransform: false }); - await NodeProxy.delete({ path: node3!.path, keepWorldTransform: false }); - await NodeProxy.delete({ path: node2!.path, keepWorldTransform: false }); - await NodeProxy.delete({ path: node0!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: node4!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: node3!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: node2!.path, keepWorldTransform: false }); + await NodeProxy.deleteNode({ path: node0!.path, keepWorldTransform: false }); }); }); }); \ No newline at end of file diff --git a/src/core/scene/test/prefab-proxy.testcase.ts b/src/core/scene/test/prefab-proxy.testcase.ts index 195c077b4..0b94a0dae 100644 --- a/src/core/scene/test/prefab-proxy.testcase.ts +++ b/src/core/scene/test/prefab-proxy.testcase.ts @@ -85,7 +85,7 @@ describe('Prefab Proxy In Scene 测试', () => { position: { x: 10, y: 20, z: 0 } }; - const testNode = await NodeProxy.createByType(createParams); + const testNode = await NodeProxy.createNodeByType(createParams); expect(testNode).toBeDefined(); expect(testNode?.name).toBe('TestPrefabNode'); if (testNode) { @@ -144,7 +144,7 @@ describe('Prefab Proxy In Scene 测试', () => { name: 'PrefabInstanceNode-CreatePrefabFromNode' }; - const prefabInstanceNode = await NodeProxy.createByAsset(createParams); + const prefabInstanceNode = await NodeProxy.createNodeByAsset(createParams); expect(prefabInstanceNode).toBeDefined(); expect(prefabInstanceNode?.prefab).toBeDefined(); expect(prefabInstanceNode?.prefab?.asset).toBeDefined(); @@ -170,7 +170,7 @@ describe('Prefab Proxy In Scene 测试', () => { position: { x: 10, y: 20, z: 0 } }; - const normalNode = await NodeProxy.createByType(createParams); + const normalNode = await NodeProxy.createNodeByType(createParams); expect(normalNode).toBeTruthy(); const params: IIsPrefabInstanceParams = { @@ -212,7 +212,7 @@ describe('Prefab Proxy In Scene 测试', () => { position: { x: 10, y: 20, z: 0 } }; - const normalNode = await NodeProxy.createByType(createParams); + const normalNode = await NodeProxy.createNodeByType(createParams); expect(normalNode).toBeTruthy(); const params: IGetPrefabInfoParams = { @@ -237,7 +237,7 @@ describe('Prefab Proxy In Scene 测试', () => { it('修改预制体实例与身上组件的属性', async () => { expect(testNodePrefabNode).toBeTruthy(); if (testNodePrefabNode) { - const uNode = await NodeProxy.update({ + const uNode = await NodeProxy.updateNode({ path: testNodePrefabNode.path, properties: { position: position @@ -245,7 +245,7 @@ describe('Prefab Proxy In Scene 测试', () => { }); expect(uNode).toBeTruthy(); - const node = await NodeProxy.query({ path: uNode?.path as string, queryChildren: false, queryComponent: false }) as INode | null; + const node = await NodeProxy.queryNode({ path: uNode?.path as string, queryChildren: false, queryComponent: false }); expect(node).toBeTruthy(); expect(node?.components?.length).toBeGreaterThan(0); @@ -279,7 +279,7 @@ describe('Prefab Proxy In Scene 测试', () => { position: { x: 10, y: 20, z: 0 } }; - const normalNode = await NodeProxy.createByType(createParams); + const normalNode = await NodeProxy.createNodeByType(createParams); expect(normalNode).toBeTruthy(); if (normalNode) { const params: IApplyPrefabChangesParams = { @@ -306,7 +306,7 @@ describe('Prefab Proxy In Scene 测试', () => { name: 'PrefabInstanceNode-applyPrefabChanges' }; - const prefabInstanceNode = await NodeProxy.createByAsset(createParams); + const prefabInstanceNode = await NodeProxy.createNodeByAsset(createParams); expect(prefabInstanceNode).toBeTruthy(); expect(prefabInstanceNode?.properties.position).toEqual(position); expect(prefabInstanceNode?.components?.length).toBeGreaterThan(0); @@ -314,7 +314,7 @@ describe('Prefab Proxy In Scene 测试', () => { const path = prefabInstanceNode && prefabInstanceNode.components && prefabInstanceNode.components[0].path || ''; expect(path).toBeTruthy(); - const component = await ComponentProxy.query({ + const component = await ComponentProxy.queryComponent({ path: path, }) as IComponent; @@ -338,7 +338,7 @@ describe('Prefab Proxy In Scene 测试', () => { position: { x: 10, y: 20, z: 0 } }; - const normalNode = await NodeProxy.createByType(createParams); + const normalNode = await NodeProxy.createNodeByType(createParams); expect(normalNode).toBeTruthy(); if (normalNode) { const params: IRevertToPrefabParams = { @@ -354,11 +354,11 @@ describe('Prefab Proxy In Scene 测试', () => { expect(testNodePrefabNode).toBeTruthy(); if (testNodePrefabNode) { - const node = await NodeProxy.query({ path: testNodePrefabNode.path, queryChildren: false, queryComponent: false }) as INode | null; + const node = await NodeProxy.queryNode({ path: testNodePrefabNode.path, queryChildren: false, queryComponent: false }); expect(node).toBeTruthy(); if (!node) return; - const uNode = await NodeProxy.update({ + const uNode = await NodeProxy.updateNode({ path: testNodePrefabNode.path, properties: { position: position @@ -376,7 +376,7 @@ describe('Prefab Proxy In Scene 测试', () => { const result = await PrefabProxy.revertToPrefab(params); expect(result).toBe(true); - const node2 = await NodeProxy.query({ path: path, queryChildren: false, queryComponent: false }) as INode | null; + const node2 = await NodeProxy.queryNode({ path: path, queryChildren: false, queryComponent: false }); expect(node.properties.position).toEqual(node2?.properties.position); } }); @@ -390,7 +390,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const testNode = await NodeProxy.createByType(createParams); + const testNode = await NodeProxy.createNodeByType(createParams); expect(testNode).toBeTruthy(); if (!testNode) return; @@ -408,7 +408,7 @@ describe('Prefab Proxy In Scene 测试', () => { const prefabNodePath = prefabNode.path; // 获取初始属性 - const initialQuery = await NodeProxy.query({ path: prefabNodePath, queryChildren: false, queryComponent: false }) as INode | null; + const initialQuery = await NodeProxy.queryNode({ path: prefabNodePath, queryChildren: false, queryComponent: false }); expect(initialQuery).toBeTruthy(); if (!initialQuery) return; @@ -437,7 +437,7 @@ describe('Prefab Proxy In Scene 测试', () => { }; const appliedName = `${originalName}-Renamed`; - const firstUpdateResult = await NodeProxy.update({ + const firstUpdateResult = await NodeProxy.updateNode({ path: prefabNodePath, name: appliedName, properties: { @@ -479,7 +479,7 @@ describe('Prefab Proxy In Scene 测试', () => { w: 1.2, }; - const secondUpdateResult = await NodeProxy.update({ + const secondUpdateResult = await NodeProxy.updateNode({ path: updatedPrefabNodePath, properties: { scale: overriddenScale, @@ -495,14 +495,14 @@ describe('Prefab Proxy In Scene 测试', () => { nodePath: updatedPrefabNodePath, }; - const queryNode = await NodeProxy.query({ path: updatedPrefabNodePath, queryChildren: false, queryComponent: false }) as INode | null; + const queryNode = await NodeProxy.queryNode({ path: updatedPrefabNodePath, queryChildren: false, queryComponent: false }); queryNode && console.log(queryNode.properties); const revertResult = await PrefabProxy.revertToPrefab(revertParams); expect(revertResult).toBe(true); // 验证还原后的属性 - const revertedQuery = await NodeProxy.query({ path: updatedPrefabNodePath, queryChildren: false, queryComponent: false }) as INode | null; + const revertedQuery = await NodeProxy.queryNode({ path: updatedPrefabNodePath, queryChildren: false, queryComponent: false }); expect(revertedQuery).toBeTruthy(); if (!revertedQuery) return; @@ -529,7 +529,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const parentNode = await NodeProxy.createByType(createParentParams); + const parentNode = await NodeProxy.createNodeByType(createParentParams); expect(parentNode).toBeTruthy(); if (!parentNode) return; @@ -541,7 +541,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const childNode = await NodeProxy.createByType(createChildParams); + const childNode = await NodeProxy.createNodeByType(createChildParams); expect(childNode).toBeTruthy(); if (!childNode) return; @@ -559,11 +559,11 @@ describe('Prefab Proxy In Scene 测试', () => { const prefabNodePath = prefabNode.path; // 查询节点及其子节点,确认子节点存在(创建预制体后,父节点名称已改变,子节点 path 也会改变) - const beforeRevertQuery = await NodeProxy.query({ + const beforeRevertQuery = await NodeProxy.queryNode({ path: prefabNodePath, queryChildren: true, queryComponent: false - }) as INode | null; + }); expect(beforeRevertQuery).toBeTruthy(); if (!beforeRevertQuery) return; @@ -583,7 +583,7 @@ describe('Prefab Proxy In Scene 测试', () => { expect(originalChildPath).not.toBe(''); // 修改父节点属性 - const updateResult = await NodeProxy.update({ + const updateResult = await NodeProxy.updateNode({ path: prefabNodePath, properties: { position: { x: 100, y: 100, z: 100 }, @@ -600,11 +600,11 @@ describe('Prefab Proxy In Scene 测试', () => { expect(revertResult).toBe(true); // 查询节点及其子节点,验证子节点的 path 保持不变 - const afterRevertQuery = await NodeProxy.query({ + const afterRevertQuery = await NodeProxy.queryNode({ path: prefabNodePath, queryChildren: true, queryComponent: false - }) as INode | null; + }); expect(afterRevertQuery).toBeTruthy(); if (!afterRevertQuery) return; @@ -663,7 +663,7 @@ describe('Prefab Proxy In Scene 测试', () => { name: 'PrefabInstanceNode2' }; - const prefabInstance2 = await NodeProxy.createByAsset(createParams); + const prefabInstance2 = await NodeProxy.createNodeByAsset(createParams); expect(prefabInstance2).toBeDefined(); if (prefabInstance2) { @@ -687,7 +687,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const normalNode = await NodeProxy.createByType(createParams); + const normalNode = await NodeProxy.createNodeByType(createParams); expect(normalNode).toBeTruthy(); if (!normalNode) return; @@ -711,7 +711,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const testNode = await NodeProxy.createByType(createNodeParams); + const testNode = await NodeProxy.createNodeByType(createNodeParams); expect(testNode).toBeTruthy(); if (!testNode) return; @@ -758,7 +758,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const anotherNode = await NodeProxy.createByType(anotherNodeParams); + const anotherNode = await NodeProxy.createNodeByType(anotherNodeParams); expect(anotherNode).toBeTruthy(); if (!anotherNode) return; @@ -794,7 +794,7 @@ describe('Prefab Proxy In Scene 测试', () => { }; const renamedNode = `${nodeName}-Renamed`; - const initialUpdateResult = await NodeProxy.update({ + const initialUpdateResult = await NodeProxy.updateNode({ path: nodePath, name: renamedNode, properties: { @@ -830,7 +830,7 @@ describe('Prefab Proxy In Scene 测试', () => { w: 0.7071068, }; - const secondUpdateResult = await NodeProxy.update({ + const secondUpdateResult = await NodeProxy.updateNode({ path: nodePath, properties: { position: changedPosition, @@ -852,7 +852,7 @@ describe('Prefab Proxy In Scene 测试', () => { expect(revertResult).toBe(true); // 验证还原后的属性 - const queryNodeResult = await NodeProxy.query({ path: nodePath, queryChildren: false, queryComponent: false }) as INode | null; + const queryNodeResult = await NodeProxy.queryNode({ path: nodePath, queryChildren: false, queryComponent: false }); expect(queryNodeResult).not.toBeNull(); if (queryNodeResult) { const props = queryNodeResult.properties; @@ -895,7 +895,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const parentNode = await NodeProxy.createByType(parentNodeParams); + const parentNode = await NodeProxy.createNodeByType(parentNodeParams); expect(parentNode).toBeTruthy(); if (!parentNode) return; @@ -907,7 +907,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const childNode = await NodeProxy.createByType(childNodeParams); + const childNode = await NodeProxy.createNodeByType(childNodeParams); expect(childNode).toBeTruthy(); // 从父节点创建预制体(包含子节点) @@ -971,7 +971,7 @@ describe('Prefab Proxy In Scene 测试', () => { }); it('测试重复创建预制体(覆盖测试)', async () => { - const node = await NodeProxy.createByType({ + const node = await NodeProxy.createNodeByType({ path: '', nodeType: NodeType.EMPTY, name: 'Duplicate-Node' @@ -1015,7 +1015,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const testNode = await NodeProxy.createByType(createNodeParams); + const testNode = await NodeProxy.createNodeByType(createNodeParams); expect(testNode).toBeTruthy(); if (!testNode) return; @@ -1058,7 +1058,7 @@ describe('Prefab Proxy In Scene 测试', () => { nodeType: NodeType.EMPTY, }; - const testNode = await NodeProxy.createByType(createNodeParams); + const testNode = await NodeProxy.createNodeByType(createNodeParams); expect(testNode).toBeTruthy(); if (!testNode) return; @@ -1079,7 +1079,7 @@ describe('Prefab Proxy In Scene 测试', () => { // 多次修改和应用 for (let i = 0; i < 3; i++) { - const updateResult = await NodeProxy.update({ + const updateResult = await NodeProxy.updateNode({ path: prefabNodePath, properties: { position: { @@ -1100,7 +1100,7 @@ describe('Prefab Proxy In Scene 测试', () => { } // 修改后还原 - const finalUpdateResult = await NodeProxy.update({ + const finalUpdateResult = await NodeProxy.updateNode({ path: prefabNodePath, properties: { position: { x: 999, y: 999, z: basePos.z ?? 0 }, diff --git a/src/core/scene/test/scene.test.ts b/src/core/scene/test/scene.test.ts index 8f91212b6..425c3ff50 100644 --- a/src/core/scene/test/scene.test.ts +++ b/src/core/scene/test/scene.test.ts @@ -24,7 +24,6 @@ afterAll(async () => { import './editor-proxy-scene.testcase'; import './editor-proxy-prefab.testcase'; import './node-proxy.testcase'; -import './node-dump-proxy.testcase'; import './component-proxy.testcase'; import './prefab-proxy.testcase'; import './script-proxy.testcase';