feat: 实现可扩展的字段编辑器系统与专业资产选择器 (#227)
This commit is contained in:
@@ -7,7 +7,8 @@ import { BehaviorTreeEditor } from '../BehaviorTreeEditor';
|
||||
import { BehaviorTreeService } from '../../services/BehaviorTreeService';
|
||||
import { showToast } from '../../services/NotificationService';
|
||||
import { FolderOpen } from 'lucide-react';
|
||||
import type { Node as BehaviorTreeNode } from '../../domain/models/Node';
|
||||
import { Node as BehaviorTreeNode } from '../../domain/models/Node';
|
||||
import { BehaviorTree } from '../../domain/models/BehaviorTree';
|
||||
import './BehaviorTreeEditorPanel.css';
|
||||
|
||||
const logger = createLogger('BehaviorTreeEditorPanel');
|
||||
@@ -69,7 +70,9 @@ export const BehaviorTreeEditorPanel: React.FC<BehaviorTreeEditorPanelProps> = (
|
||||
useEffect(() => {
|
||||
try {
|
||||
const messageHub = Core.services.resolve(MessageHub);
|
||||
const unsubscribe = messageHub.subscribe('behavior-tree:file-opened', (data: { filePath: string; fileName: string }) => {
|
||||
|
||||
// 订阅文件打开事件
|
||||
const unsubscribeFileOpened = messageHub.subscribe('behavior-tree:file-opened', (data: { filePath: string; fileName: string }) => {
|
||||
setCurrentFilePath(data.filePath);
|
||||
setCurrentFileName(data.fileName);
|
||||
const loadedTree = useBehaviorTreeDataStore.getState().tree;
|
||||
@@ -77,11 +80,51 @@ export const BehaviorTreeEditorPanel: React.FC<BehaviorTreeEditorPanelProps> = (
|
||||
setHasUnsavedChanges(false);
|
||||
});
|
||||
|
||||
// 订阅节点属性更改事件
|
||||
const unsubscribePropertyChanged = messageHub.subscribe('behavior-tree:node-property-changed',
|
||||
(data: { nodeId: string; propertyName: string; value: any }) => {
|
||||
const state = useBehaviorTreeDataStore.getState();
|
||||
const node = state.getNode(data.nodeId);
|
||||
|
||||
if (node) {
|
||||
const newData = { ...node.data, [data.propertyName]: data.value };
|
||||
|
||||
// 更新节点数据
|
||||
const updatedNode = new BehaviorTreeNode(
|
||||
node.id,
|
||||
node.template,
|
||||
newData,
|
||||
node.position,
|
||||
Array.from(node.children)
|
||||
);
|
||||
|
||||
// 更新树
|
||||
const nodes = state.getNodes().map(n =>
|
||||
n.id === data.nodeId ? updatedNode : n
|
||||
);
|
||||
|
||||
const newTree = new BehaviorTree(
|
||||
nodes,
|
||||
state.getConnections(),
|
||||
state.getBlackboard(),
|
||||
state.getRootNodeId()
|
||||
);
|
||||
|
||||
state.setTree(newTree);
|
||||
setHasUnsavedChanges(true);
|
||||
|
||||
// 强制刷新画布
|
||||
state.triggerForceUpdate();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
unsubscribeFileOpened();
|
||||
unsubscribePropertyChanged();
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to subscribe to file-opened event:', error);
|
||||
logger.error('Failed to subscribe to events:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { IInspectorProvider, InspectorContext, MessageHub } from '@esengine/editor-core';
|
||||
import { IInspectorProvider, InspectorContext, MessageHub, FieldEditorRegistry, FieldEditorContext } from '@esengine/editor-core';
|
||||
import { Core } from '@esengine/ecs-framework';
|
||||
import { Node as BehaviorTreeNode } from '../domain/models/Node';
|
||||
import { PropertyDefinition } from '@esengine/behavior-tree';
|
||||
|
||||
@@ -18,6 +19,49 @@ const PropertyEditor: React.FC<PropertyEditorProps> = ({ property, value, onChan
|
||||
}, [property.name, onChange]);
|
||||
|
||||
const renderInput = () => {
|
||||
// 特殊处理 treeAssetId 字段使用 asset 编辑器
|
||||
if (property.name === 'treeAssetId') {
|
||||
const fieldRegistry = Core.services.resolve(FieldEditorRegistry);
|
||||
const assetEditor = fieldRegistry.getEditor('asset');
|
||||
|
||||
if (assetEditor) {
|
||||
const context: FieldEditorContext = {
|
||||
readonly: false,
|
||||
metadata: {
|
||||
fileExtension: '.btree',
|
||||
placeholder: '拖拽或选择行为树文件'
|
||||
}
|
||||
};
|
||||
|
||||
return assetEditor.render({
|
||||
label: '',
|
||||
value: value ?? property.defaultValue ?? null,
|
||||
onChange: handleChange,
|
||||
context
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有特定的字段编辑器类型
|
||||
if (property.fieldEditor) {
|
||||
const fieldRegistry = Core.services.resolve(FieldEditorRegistry);
|
||||
const editor = fieldRegistry.getEditor(property.fieldEditor.type);
|
||||
|
||||
if (editor) {
|
||||
const context: FieldEditorContext = {
|
||||
readonly: false,
|
||||
metadata: property.fieldEditor.options
|
||||
};
|
||||
|
||||
return editor.render({
|
||||
label: '',
|
||||
value: value ?? property.defaultValue,
|
||||
onChange: handleChange,
|
||||
context
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
switch (property.type) {
|
||||
case 'number':
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user