修复插件生成代码报错问题

This commit is contained in:
YHH
2025-06-18 20:22:17 +08:00
parent aaa2a8ed2c
commit f48ebb65ba
10 changed files with 521 additions and 69 deletions

View File

@@ -94,7 +94,7 @@ ${comments}
export class ${className} extends ${options.systemType} { export class ${className} extends ${options.systemType} {
constructor() { constructor() {
super(${matcherSetup}${options.systemType === 'IntervalSystem' ? ', 1000 / 60 // 60fps' : ''}); super(${matcherSetup}${options.systemType === 'IntervalSystem' ? ', 1000 / 60' : ''})${options.systemType === 'IntervalSystem' ? '; // 60fps' : ';'}
} }
${processMethod} ${processMethod}

View File

@@ -14,30 +14,21 @@ export class BehaviorTreeHandler {
const projectPath = Editor.Project.path; const projectPath = Editor.Project.path;
const command = 'npm install @esengine/ai'; const command = 'npm install @esengine/ai';
console.log(`Installing Behavior Tree AI to project: ${projectPath}`);
return new Promise((resolve) => { return new Promise((resolve) => {
exec(command, { cwd: projectPath }, (error, stdout, stderr) => { exec(command, { cwd: projectPath }, (error, stdout, stderr) => {
console.log('Install stdout:', stdout);
if (stderr) console.log('Install stderr:', stderr);
if (error) { if (error) {
console.error('Behavior Tree AI installation failed:', error); console.error('AI系统安装失败:', error.message);
resolve(false); resolve(false);
} else { } else {
console.log('Behavior Tree AI installation completed successfully');
// 验证安装是否成功 // 验证安装是否成功
const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai'); const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai');
const installSuccess = fs.existsSync(nodeModulesPath); const installSuccess = fs.existsSync(nodeModulesPath);
if (installSuccess) { if (!installSuccess) {
console.log('Behavior Tree AI installed successfully'); console.warn('安装完成但未找到AI系统目录请检查网络连接');
resolve(true);
} else {
console.warn('Behavior Tree AI directory not found after install');
resolve(false);
} }
resolve(installSuccess);
} }
}); });
}); });
@@ -50,30 +41,21 @@ export class BehaviorTreeHandler {
const projectPath = Editor.Project.path; const projectPath = Editor.Project.path;
const command = 'npm update @esengine/ai'; const command = 'npm update @esengine/ai';
console.log(`Updating Behavior Tree AI in project: ${projectPath}`);
return new Promise((resolve) => { return new Promise((resolve) => {
exec(command, { cwd: projectPath }, (error, stdout, stderr) => { exec(command, { cwd: projectPath }, (error, stdout, stderr) => {
console.log('Update stdout:', stdout);
if (stderr) console.log('Update stderr:', stderr);
if (error) { if (error) {
console.error('Behavior Tree AI update failed:', error); console.error('AI系统更新失败:', error.message);
resolve(false); resolve(false);
} else { } else {
console.log('Behavior Tree AI update completed successfully');
// 验证更新是否成功 // 验证更新是否成功
const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai'); const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai');
const updateSuccess = fs.existsSync(nodeModulesPath); const updateSuccess = fs.existsSync(nodeModulesPath);
if (updateSuccess) { if (!updateSuccess) {
console.log('Behavior Tree AI updated successfully'); console.warn('更新完成但未找到AI系统目录');
resolve(true);
} else {
console.warn('Behavior Tree AI directory not found after update');
resolve(false);
} }
resolve(updateSuccess);
} }
}); });
}); });
@@ -96,7 +78,7 @@ export class BehaviorTreeHandler {
return '@esengine/ai' in dependencies; return '@esengine/ai' in dependencies;
} catch (error) { } catch (error) {
console.error('Error checking Behavior Tree AI installation:', error); console.error('检查AI系统安装状态失败:', error);
return false; return false;
} }
} }

View File

@@ -93,15 +93,25 @@ export const methods: { [key: string]: (...any: any) => any } = {
/** /**
* 安装行为树AI系统 * 安装行为树AI系统
*/ */
'install-behavior-tree'() { async 'install-behavior-tree'() {
BehaviorTreeHandler.install(); try {
return await BehaviorTreeHandler.install();
} catch (error) {
console.error('安装行为树AI系统失败:', error);
return false;
}
}, },
/** /**
* 更新行为树AI系统 * 更新行为树AI系统
*/ */
'update-behavior-tree'() { async 'update-behavior-tree'() {
BehaviorTreeHandler.update(); try {
return await BehaviorTreeHandler.update();
} catch (error) {
console.error('更新行为树AI系统失败:', error);
return false;
}
}, },
/** /**

View File

@@ -347,9 +347,113 @@ export function useBehaviorTreeEditor() {
} }
}; };
// 复制到剪贴板
const copyToClipboard = async () => {
try {
const code = computedProps.exportedCode();
await navigator.clipboard.writeText(code);
// 显示成功消息
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: #4caf50;
color: white;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 10001;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
`;
toast.textContent = '已复制到剪贴板!';
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '1';
toast.style.transform = 'translateX(0)';
}, 10);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 2000);
} catch (error) {
alert('复制到剪贴板失败: ' + error);
}
};
// 保存到文件
const saveToFile = () => {
const code = computedProps.exportedCode();
const format = appState.exportFormat.value;
const extension = format === 'json' ? '.json' : '.ts';
const mimeType = format === 'json' ? 'application/json' : 'text/typescript';
// 创建文件并下载
const blob = new Blob([code], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `behavior_tree_config${extension}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 显示成功消息
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: #4caf50;
color: white;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 10001;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
`;
toast.textContent = `文件已保存: behavior_tree_config${extension}`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '1';
toast.style.transform = 'translateX(0)';
}, 10);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 3000);
};
onMounted(() => { onMounted(() => {
// 自动检查安装状态
installation.checkInstallStatus();
const appContainer = document.querySelector('#behavior-tree-app'); const appContainer = document.querySelector('#behavior-tree-app');
if (appContainer) { if (appContainer) {
(appContainer as any).loadFileContent = fileOps.loadFileContent; (appContainer as any).loadFileContent = fileOps.loadFileContent;
@@ -457,6 +561,8 @@ export function useBehaviorTreeEditor() {
autoLayout, autoLayout,
validateTree, validateTree,
clearAllConnections, clearAllConnections,
copyToClipboard,
saveToFile,
// 节点选择相关 // 节点选择相关
selectNode: (nodeId: string) => { selectNode: (nodeId: string) => {
// 选中普通节点时,取消条件节点的选中 // 选中普通节点时,取消条件节点的选中

View File

@@ -19,6 +19,7 @@ export function useInstallation(
isInstalled.value = result.installed; isInstalled.value = result.installed;
version.value = result.version; version.value = result.version;
} catch (error) { } catch (error) {
console.error('检查AI系统安装状态失败:', error);
isInstalled.value = false; isInstalled.value = false;
version.value = null; version.value = null;
} finally { } finally {
@@ -30,10 +31,23 @@ export function useInstallation(
const handleInstall = async () => { const handleInstall = async () => {
isInstalling.value = true; isInstalling.value = true;
try { try {
await installBehaviorTreeAI(Editor.Project.path); const result = await installBehaviorTreeAI(Editor.Project.path);
if (result) {
// 等待文件系统更新
await new Promise(resolve => setTimeout(resolve, 2000));
await checkInstallStatus(); await checkInstallStatus();
// 如果第一次检查失败,再次尝试
if (!isInstalled.value) {
await new Promise(resolve => setTimeout(resolve, 1000));
await checkInstallStatus();
}
} else {
console.error('AI系统安装失败');
}
} catch (error) { } catch (error) {
// 安装失败时静默处理 console.error('安装AI系统时发生错误:', error);
} finally { } finally {
isInstalling.value = false; isInstalling.value = false;
} }

View File

@@ -50,9 +50,9 @@ export function getInstallStatusText(
isInstalled: boolean, isInstalled: boolean,
version: string | null version: string | null
): string { ): string {
if (isChecking) return '检查中...'; if (isChecking) return '检查状态中...';
if (isInstalling) return '安装中...'; if (isInstalling) return '正在安装AI系统...';
return isInstalled ? `AI系统已安装 (v${version})` : 'AI系统未安装'; return isInstalled ? 'AI系统已安装' : 'AI系统未安装';
} }
/** /**
@@ -70,19 +70,13 @@ export function getInstallStatusClass(
* 安装行为树AI系统 * 安装行为树AI系统
* 通过发送消息到主进程来执行真实的npm安装命令 * 通过发送消息到主进程来执行真实的npm安装命令
*/ */
export async function installBehaviorTreeAI(projectPath: string): Promise<void> { export async function installBehaviorTreeAI(projectPath: string): Promise<boolean> {
try { try {
// 通过Editor.Message发送安装消息到主进程
// 主进程会执行实际的npm install @esengine/ai命令
const result = await Editor.Message.request('cocos-ecs-extension', 'install-behavior-tree'); const result = await Editor.Message.request('cocos-ecs-extension', 'install-behavior-tree');
return Boolean(result);
if (!result) {
throw new Error('安装请求失败,未收到主进程响应');
}
// 安装完成
} catch (error) { } catch (error) {
throw error; console.error('请求安装AI系统失败:', error);
return false;
} }
} }
@@ -90,18 +84,12 @@ export async function installBehaviorTreeAI(projectPath: string): Promise<void>
* 更新行为树AI系统 * 更新行为树AI系统
* 通过发送消息到主进程来执行真实的npm更新命令 * 通过发送消息到主进程来执行真实的npm更新命令
*/ */
export async function updateBehaviorTreeAI(projectPath: string): Promise<void> { export async function updateBehaviorTreeAI(projectPath: string): Promise<boolean> {
try { try {
// 通过Editor.Message发送更新消息到主进程
// 主进程会执行实际的npm update @esengine/ai命令
const result = await Editor.Message.request('cocos-ecs-extension', 'update-behavior-tree'); const result = await Editor.Message.request('cocos-ecs-extension', 'update-behavior-tree');
return Boolean(result);
if (!result) {
throw new Error('更新请求失败,未收到主进程响应');
}
// 更新完成
} catch (error) { } catch (error) {
throw error; console.error('请求更新AI系统失败:', error);
return false;
} }
} }

View File

@@ -73,3 +73,139 @@
min-height: 400px; min-height: 400px;
} }
} }
/* 编辑器容器禁用状态 */
.editor-container.disabled {
position: relative;
pointer-events: none;
opacity: 0.3;
filter: blur(1px);
}
/* 安装遮罩层 */
.install-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(26, 32, 44, 0.95);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
pointer-events: all;
}
.overlay-content {
text-align: center;
padding: 40px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
backdrop-filter: blur(20px);
max-width: 400px;
width: 90%;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.overlay-icon {
font-size: 60px;
margin-bottom: 20px;
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}
.overlay-content h3 {
font-size: 24px;
font-weight: 600;
margin-bottom: 12px;
color: #ffffff;
}
.overlay-content p {
font-size: 16px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 30px;
line-height: 1.5;
}
.overlay-actions {
margin-bottom: 20px;
}
.overlay-install-btn {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 14px 28px;
background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
border: none;
border-radius: 12px;
color: white;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
min-width: 200px;
}
.overlay-install-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(34, 197, 94, 0.4);
}
.overlay-install-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}
.overlay-install-btn.installing {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
animation: installing-glow 2s infinite;
}
@keyframes installing-glow {
0%, 100% {
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
}
50% {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
}
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 8px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.overlay-note {
padding: 12px;
background: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.3);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
}
.overlay-note small {
font-size: 14px;
}

View File

@@ -59,11 +59,13 @@
font-size: 12px; font-size: 12px;
} }
.tool-btn:hover { .tool-btn:hover:not(:disabled) {
background: rgba(255,255,255,0.2); background: rgba(255,255,255,0.2);
transform: translateY(-1px); transform: translateY(-1px);
} }
.tool-btn.has-changes { .tool-btn.has-changes {
background: rgba(255, 107, 107, 0.2); background: rgba(255, 107, 107, 0.2);
border-color: #ff6b6b; border-color: #ff6b6b;
@@ -79,11 +81,176 @@
} }
} }
.toolbar-right .install-status { /* 安装状态容器 */
.install-status-container {
position: relative;
}
.install-status {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 12px;
min-width: 280px;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.install-status:hover {
background: rgba(255,255,255,0.15);
border-color: rgba(255,255,255,0.3);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
/* 状态信息 */
.status-info {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 8px 16px; flex: 1;
background: rgba(255,255,255,0.1); }
border-radius: 8px;
.status-icon {
font-size: 18px;
line-height: 1;
}
.status-text {
display: flex;
flex-direction: column;
gap: 2px;
}
.main-text {
font-size: 13px;
font-weight: 500;
color: white;
}
.version-text {
font-size: 11px;
color: rgba(255,255,255,0.7);
}
/* 安装按钮 */
.install-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
border: none;
border-radius: 8px;
color: white;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.install-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.4);
}
.install-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
}
.install-btn.installing {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}
.btn-icon {
font-size: 14px;
line-height: 1;
}
.btn-text {
font-size: 12px;
}
/* 加载点动画 */
.loading-dots {
display: flex;
gap: 2px;
margin-left: 4px;
}
.loading-dots span {
width: 3px;
height: 3px;
background: white;
border-radius: 50%;
animation: loading-dots 1.4s infinite ease-in-out;
}
.loading-dots span:nth-child(1) { animation-delay: -0.32s; }
.loading-dots span:nth-child(2) { animation-delay: -0.16s; }
@keyframes loading-dots {
0%, 80%, 100% {
transform: scale(0);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
/* 刷新按钮 */
.refresh-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 6px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
font-size: 12px;
}
.refresh-btn:hover {
background: rgba(255,255,255,0.2);
transform: rotate(180deg);
}
/* 状态颜色 */
.install-status.installed {
border-color: rgba(34, 197, 94, 0.5);
background: rgba(34, 197, 94, 0.1);
}
.install-status.not-installed {
border-color: rgba(239, 68, 68, 0.5);
background: rgba(239, 68, 68, 0.1);
}
.install-status.installing {
border-color: rgba(59, 130, 246, 0.5);
background: rgba(59, 130, 246, 0.1);
animation: installing-pulse 2s infinite;
}
@keyframes installing-pulse {
0%, 100% {
box-shadow: 0 0 5px rgba(59, 130, 246, 0.3);
}
50% {
box-shadow: 0 0 15px rgba(59, 130, 246, 0.6);
}
} }

View File

@@ -24,16 +24,64 @@
</div> </div>
</div> </div>
<div class="toolbar-right"> <div class="toolbar-right">
<div class="install-status-container">
<div class="install-status" :class="installStatusClass()"> <div class="install-status" :class="installStatusClass()">
<span>{{ installStatusText() }}</span> <div class="status-info">
<button v-if="!isInstalled" @click="handleInstall" :disabled="isInstalling"> <span class="status-icon">{{ isInstalled ? '✅' : (isInstalling ? '⏳' : '❌') }}</span>
{{ isInstalling ? '安装中...' : '安装AI系统' }} <div class="status-text">
<span class="main-text">{{ installStatusText() }}</span>
<span v-if="version && isInstalled" class="version-text">版本 {{ version }}</span>
</div>
</div>
<button
v-if="!isInstalled"
@click="handleInstall"
:disabled="isInstalling"
class="install-btn"
:class="{ 'installing': isInstalling }"
>
<span class="btn-icon">{{ isInstalling ? '⏳' : '📦' }}</span>
<span class="btn-text">{{ isInstalling ? '安装中...' : '立即安装' }}</span>
<div v-if="isInstalling" class="loading-dots">
<span></span><span></span><span></span>
</div>
</button> </button>
<button
v-if="isInstalled && !checkingStatus"
@click="checkInstallStatus"
class="refresh-btn"
title="检查更新"
>
🔄
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="editor-container"> <div class="editor-container" :class="{ 'disabled': !isInstalled }">
<!-- 未安装遮罩 -->
<div v-if="!isInstalled" class="install-overlay">
<div class="overlay-content">
<div class="overlay-icon">🤖</div>
<h3>需要安装AI系统</h3>
<p>行为树编辑器需要安装AI系统才能正常使用</p>
<div class="overlay-actions">
<button
@click="handleInstall"
:disabled="isInstalling"
class="overlay-install-btn"
:class="{ 'installing': isInstalling }"
>
<span>{{ isInstalling ? '⏳ 安装中...' : '📦 立即安装AI系统' }}</span>
<div v-if="isInstalling" class="loading-spinner"></div>
</button>
</div>
<div class="overlay-note">
<small>📝 安装完成后编辑器将自动可用</small>
</div>
</div>
</div>
<!-- 左侧节点面板 --> <!-- 左侧节点面板 -->
<div class="nodes-panel"> <div class="nodes-panel">
<div class="panel-header"> <div class="panel-header">

View File

@@ -14,6 +14,7 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@esengine/ai/-/ai-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@esengine/ai/-/ai-2.0.1.tgz",
"integrity": "sha512-qGGYc4kYlSJzCkBDJa+p5OruOnDvnL2oJ/ciKSHsPJVdn1tIefPEkUofJyMVGo4my5ubGr2ky6igTLtLYmhzRg==", "integrity": "sha512-qGGYc4kYlSJzCkBDJa+p5OruOnDvnL2oJ/ciKSHsPJVdn1tIefPEkUofJyMVGo4my5ubGr2ky6igTLtLYmhzRg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=16.0.0"
} }