使用VSCode开发HY-Motion 1.0插件:从零开始教程
1. 为什么选择VSCode开发HY-Motion插件
开发一个能与HY-Motion 1.0模型深度集成的VSCode插件,不是为了堆砌功能,而是要让3D动作生成真正走进日常开发工作流。我第一次用文本生成一段角色奔跑动画时,那种“输入一句话,几秒后看到专业级动作”的体验,彻底改变了我对工具价值的理解。
VSCode之所以成为首选,不只是因为它开源、轻量、插件生态丰富,更关键的是它天然支持多语言、调试一体化和实时预览——这些特性恰好匹配HY-Motion插件的核心需求:既要处理自然语言提示词,又要调用Python后端API,还要可视化渲染3D动作结果。你不需要切换三个窗口去写提示、跑模型、看效果,所有操作都在一个编辑器里完成。
更重要的是,VSCode的Extension API设计得非常务实。它不强迫你用某种框架,也不要求你理解复杂的底层协议。你只需要关注三件事:用户在编辑器里想做什么(比如右键菜单选“生成动作”)、背后需要调什么接口(比如调用本地运行的HY-Motion服务)、最后怎么把结果呈现出来(比如在侧边栏显示预览动图或导出SMPL-H格式)。这种“所见即所得+所想即所做”的开发节奏,让整个过程变得异常清晰。
如果你之前开发过VSCode插件,会发现这次特别顺手;如果完全没接触过,也别担心——接下来的每一步,我都会用实际代码和截图说明,不讲概念,只说怎么做。
2. 环境准备:5分钟搭好开发底座
2.1 基础工具安装
先确认你本地已安装以下三项,版本无需严格对齐,但建议不低于标注版本:
- Node.jsv18.17+(用于构建插件)
- VSCodev1.85+(推荐使用Insiders版,对新API支持更及时)
- Pythonv3.10+(HY-Motion官方要求,用于运行模型服务)
打开终端,依次执行检查命令:
node --version # 应输出 v18.17.x 或更高 code --version # 应输出 1.85.x 或更高 python3 --version # 应输出 3.10.x 或更高如果任一命令报错,请先完成对应环境安装。Node.js和VSCode直接官网下载安装包即可;Python推荐使用pyenv管理多版本,避免污染系统环境。
2.2 创建插件项目骨架
VSCode官方提供了yo code脚手架工具,我们用它快速生成标准结构:
# 全局安装脚手架(只需一次) npm install -g yo generator-code # 创建新插件项目 yo code在交互式提问中,按如下选择:
What type of extension do you want to create?→New Extension (TypeScript)What's the name of your extension?→hy-motion-explorerWhat's the identifier of your extension?→hy-motion-explorerWhat's the description of your extension?→A VSCode extension for generating and previewing 3D human motions with HY-Motion 1.0Initialize a git repository?→YesWhich package manager to use?→npm
几秒后,你会得到一个完整目录结构。进入项目:
cd hy-motion-explorer npm install此时运行npm run compile应无报错,说明TypeScript编译环境就绪。
2.3 启动开发环境
最关键的一步:让插件在VSCode中可调试。
在VSCode中打开hy-motion-explorer文件夹,按Ctrl+Shift+P(Windows/Linux)或Cmd+Shift+P(Mac),输入Tasks: Run Task,选择watch。这会启动TypeScript监听编译。
接着按F5,VSCode会自动拉起一个扩展开发主机(Extension Development Host)窗口。这个窗口就是你的“测试沙盒”,里面已经加载了刚写的插件,但还什么功能都没有——别急,我们马上加。
小贴士:开发主机窗口标题栏会显示
[Extension Development Host],关闭它不会影响源码窗口。每次修改代码保存后,开发主机里的插件会自动热更新,无需手动重装。
3. 核心功能实现:从API封装到UI交互
3.1 封装HY-Motion API客户端
HY-Motion 1.0官方提供HTTP API服务,我们需要一个健壮的客户端来调用它。在src/目录下新建hy-motion-client.ts:
// src/hy-motion-client.ts export interface MotionRequest { prompt: string; duration?: number; // 秒数,默认5秒 fps?: number; // 帧率,默认30 } export interface MotionResponse { id: string; status: 'pending' | 'success' | 'failed'; result?: { smplh_data: number[]; // 201维向量数组,每帧一个 frames: number; fps: number; }; error?: string; } class HYMotionClient { private baseUrl: string; constructor(baseUrl: string = 'http://localhost:8000') { this.baseUrl = baseUrl; } async generateMotion(request: MotionRequest): Promise<MotionResponse> { try { const response = await fetch(`${this.baseUrl}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(request), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('HY-Motion API call failed:', error); return { id: '', status: 'failed', error: error instanceof Error ? error.message : 'Unknown error', }; } } // 添加轮询方法,检查生成状态(因生成需时间) async pollStatus(id: string): Promise<MotionResponse> { try { const response = await fetch(`${this.baseUrl}/status/${id}`); return await response.json(); } catch (error) { console.error('Poll status failed:', error); return { id, status: 'failed', error: error instanceof Error ? error.message : 'Poll failed', }; } } } // 导出单例实例,避免重复创建 export const hyMotionClient = new HYMotionClient();这段代码做了三件事:定义请求/响应类型、封装POST生成接口、提供状态轮询能力。注意它没有硬编码URL,方便后续切换本地服务或远程部署。
3.2 添加右键菜单:一键触发生成
现在让插件“活”起来。打开package.json,找到contributes字段,在commands数组里添加新命令:
{ "command": "hy-motion-explorer.generateFromSelection", "title": "HY-Motion: Generate Motion from Selection" }然后在contributes.menus中,为编辑器文本选择添加右键项:
"editor/context": [ { "when": "editorTextFocus && editorHasSelection", "command": "hy-motion-explorer.generateFromSelection", "group": "navigation" } ]最后,在src/extension.ts中注册命令处理器:
// src/extension.ts import * as vscode from 'vscode'; import { hyMotionClient, MotionRequest, MotionResponse } from './hy-motion-client'; export function activate(context: vscode.ExtensionContext) { console.log('HY-Motion Explorer is now active!'); // 注册右键命令 let disposable = vscode.commands.registerCommand( 'hy-motion-explorer.generateFromSelection', async () => { const editor = vscode.window.activeTextEditor; if (!editor) return; const selection = editor.selection; const text = editor.document.getText(selection); if (!text.trim()) { vscode.window.showWarningMessage('请先选中一段文字作为动作描述'); return; } // 显示进度条 vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: '正在生成3D动作...', cancellable: true, }, async (progress, token) => { try { const request: MotionRequest = { prompt: text.trim(), duration: 5, fps: 30, }; // 调用API const response = await hyMotionClient.generateMotion(request); if (response.status === 'failed') { throw new Error(response.error || '生成失败'); } // 显示成功消息 vscode.window.showInformationMessage( ` 动作生成成功!ID: ${response.id}. 正在预览...` ); // 这里后续会接入预览逻辑 } catch (error) { vscode.window.showErrorMessage( ` 生成失败: ${(error as Error).message}` ); } } ); } ); context.subscriptions.push(disposable); } export function deactivate() {}保存后,回到开发主机窗口,按Ctrl+Shift+P输入Developer: Reload Window重启插件。打开任意文本文件,选中一段话(比如一个角色向前走,突然停住并挥手致意),右键就会出现HY-Motion: Generate Motion from Selection选项。点击它,你会看到右下角弹出进度通知——虽然还没真正调通后端,但命令链路已经跑通了。
3.3 构建动作预览面板:让3D结果看得见
纯文本反馈太单薄。我们要在VSCode侧边栏嵌入一个预览区,直观展示生成的动作。这需要两部分:Webview面板和前端渲染逻辑。
首先,在src/extension.ts中添加面板创建逻辑:
// src/extension.ts 续写 let previewPanel: vscode.WebviewPanel | undefined = undefined; function createPreviewPanel(context: vscode.ExtensionContext) { if (previewPanel) { previewPanel.reveal(); return; } previewPanel = vscode.window.createWebviewPanel( 'hyMotionPreview', // viewId 'HY-Motion 预览', // 标题 vscode.ViewColumn.Beside, // 在编辑器右侧 { enableScripts: true, retainContextWhenHidden: true, localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, 'media')], } ); // 设置HTML内容 previewPanel.webview.html = getWebviewContent(previewPanel.webview, context.extensionUri); previewPanel.onDidDispose(() => { previewPanel = undefined; }); } function getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { const scriptUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'media', 'preview.js') ); const styleUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'media', 'preview.css') ); return `<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HY-Motion Preview</title> <link href="${styleUri}" rel="stylesheet"> </head> <body> <div class="container"> <h2>3D动作预览</h2> <div id="status">等待生成...</div> <canvas id="motionCanvas" width="400" height="300"></canvas> <div class="controls"> <button id="playBtn">▶ 播放</button> <button id="pauseBtn">⏸ 暂停</button> <button id="resetBtn">↺ 重置</button> </div> </div> <script src="${scriptUri}"></script> </body> </html>`; }然后在src/同级创建media/文件夹,放入preview.js(简化版Three.js渲染):
// media/preview.js const canvas = document.getElementById('motionCanvas'); const ctx = canvas.getContext('2d'); const statusEl = document.getElementById('status'); const playBtn = document.getElementById('playBtn'); const pauseBtn = document.getElementById('pauseBtn'); const resetBtn = document.getElementById('resetBtn'); let isPlaying = false; let frameIndex = 0; let animationId = null; // 模拟一个简单的人体骨架(仅示意) function drawSkeleton(ctx, frameData) { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = '#4CAF50'; ctx.lineWidth = 2; // 简化为5个关键点:头、躯干、左右手、脚 const points = [ {x: 200, y: 50}, // 头 {x: 200, y: 120}, // 躯干 {x: 150, y: 100}, // 左手 {x: 250, y: 100}, // 右手 {x: 180, y: 220}, // 脚 ]; // 连线 ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[1].x, points[1].y); ctx.moveTo(points[1].x, points[1].y); ctx.lineTo(points[2].x, points[2].y); ctx.moveTo(points[1].x, points[1].y); ctx.lineTo(points[3].x, points[3].y); ctx.moveTo(points[1].x, points[1].y); ctx.lineTo(points[4].x, points[4].y); ctx.stroke(); // 绘制点 points.forEach(p => { ctx.beginPath(); ctx.arc(p.x, p.y, 4, 0, Math.PI * 2); ctx.fillStyle = '#2196F3'; ctx.fill(); }); } // 模拟动画循环 function animate() { if (!isPlaying) return; // 这里本应解析SMPL-H数据,当前用简单偏移模拟 const offset = Math.sin(frameIndex * 0.1) * 20; const mockFrame = [ 200 + offset, 50, // 头 200, 120, // 躯干 150 + offset, 100, // 左手 250 - offset, 100, // 右手 180, 220, // 脚 ]; drawSkeleton(ctx, mockFrame); frameIndex++; if (frameIndex > 100) frameIndex = 0; animationId = requestAnimationFrame(animate); } playBtn.addEventListener('click', () => { if (!isPlaying) { isPlaying = true; statusEl.textContent = '正在播放...'; animate(); } }); pauseBtn.addEventListener('click', () => { isPlaying = false; if (animationId) { cancelAnimationFrame(animationId); animationId = null; } statusEl.textContent = '已暂停'; }); resetBtn.addEventListener('click', () => { isPlaying = false; if (animationId) { cancelAnimationFrame(animationId); animationId = null; } frameIndex = 0; statusEl.textContent = '已重置'; drawSkeleton(ctx, []); });最后,在activate函数中添加面板触发入口:
// src/extension.ts 中 activate 函数内追加 vscode.commands.registerCommand('hy-motion-explorer.openPreview', () => { createPreviewPanel(context); });并在package.json的commands中添加该命令。现在,按Ctrl+Shift+P输入HY-Motion: Open Preview,就能打开预览面板了。虽然目前是模拟动画,但骨架已搭好——后续接入真实SMPL-H数据解析后,这里就是真正的3D预览中心。
4. 调试与优化:让插件稳定可靠
4.1 本地运行HY-Motion服务
插件需要后端服务支撑。官方推荐使用Python FastAPI启动服务。确保你已安装hy-motion包:
pip install hy-motion创建server.py(与插件项目平级):
# server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn import threading import time import numpy as np app = FastAPI() # 模拟生成队列 jobs = {} class GenerateRequest(BaseModel): prompt: str duration: int = 5 fps: int = 30 @app.post("/generate") def generate_motion(request: GenerateRequest): job_id = f"job_{int(time.time())}" jobs[job_id] = {"status": "pending", "prompt": request.prompt} # 启动后台线程模拟生成(实际应调用hy_motion.generate) def simulate_generation(): time.sleep(3) # 模拟3秒生成时间 # 生成随机SMPL-H数据(201维 * 150帧) fake_data = np.random.randn(150, 201).tolist() jobs[job_id] = { "status": "success", "result": { "smplh_data": fake_data, "frames": 150, "fps": 30 } } threading.Thread(target=simulate_generation, daemon=True).start() return {"id": job_id, "status": "pending"} @app.get("/status/{job_id}") def get_status(job_id: str): if job_id not in jobs: raise HTTPException(status_code=404, detail="Job not found") return jobs[job_id] if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)运行服务:
python server.py服务启动后,插件就能通过http://localhost:8000调用API了。你可以用curl测试:
curl -X POST http://localhost:8000/generate \ -H "Content-Type: application/json" \ -d '{"prompt":"一个人挥手打招呼","duration":3}'4.2 插件调试技巧
VSCode插件调试非常直观。在src/extension.ts中打上断点(比如在generateFromSelection函数开头),然后按F5启动开发主机。当触发右键命令时,代码会在断点处暂停,你可以查看变量、单步执行、甚至修改变量值再继续。
两个实用技巧:
- 调试API调用:在
hy-motion-client.ts的fetch调用前后加console.log,观察请求URL、参数和响应体。 - 查看Webview控制台:在预览面板中右键 →
Inspect,就能打开DevTools,调试preview.js中的JavaScript。
4.3 错误处理与用户体验优化
生产级插件必须优雅处理各种失败场景。我们在generateFromSelection中增强错误分支:
// src/extension.ts 中的命令处理器内 try { // ... 原有调用逻辑 if (response.status === 'failed') { throw new Error(response.error || '生成失败'); } // 成功后自动打开预览面板 createPreviewPanel(context); // 通知用户并提供操作按钮 const action = await vscode.window.showInformationMessage( ` 动作生成成功!ID: ${response.id}`, '打开预览', '复制ID', '查看日志' ); switch (action) { case '打开预览': createPreviewPanel(context); break; case '复制ID': await vscode.env.clipboard.writeText(response.id); vscode.window.showInformationMessage('ID已复制到剪贴板'); break; case '查看日志': vscode.window.showOutputChannel('HY-Motion Logs').appendLine( `Generated ${response.id} for "${text}"` ); break; } } catch (error) { const err = error as Error; vscode.window.showErrorMessage( ` 生成失败: ${err.message}`, '查看详细日志', '重试' ).then(choice => { if (choice === '查看详细日志') { vscode.window.showOutputChannel('HY-Motion Logs').appendLine( `Error at ${new Date().toISOString()}: ${err.stack}` ); } else if (choice === '重试') { // 重新触发命令 vscode.commands.executeCommand('hy-motion-explorer.generateFromSelection'); } }); }这样,用户遇到问题时,有明确的恢复路径,而不是面对一个冰冷的错误弹窗。
5. 打包与分发:让团队成员一键安装
开发完成,下一步是打包成.vsix文件供他人安装。
在项目根目录执行:
npm run package这会生成hy-motion-explorer-0.0.1.vsix文件。任何人双击该文件,VSCode就会自动安装插件。
如果你想发布到VSCode Marketplace,需先注册Publisher Account,然后使用vsce工具:
npm install -g vsce vsce login your-publisher-name vsce package vsce publish但对内部团队,直接共享.vsix文件更高效。你还可以在README.md中加入一行安装命令:
# 安装方式 1. 下载 [hy-motion-explorer-0.0.1.vsix](./hy-motion-explorer-0.0.1.vsix) 2. VSCode中按 `Ctrl+Shift+P` → 输入 `Extensions: Install from VSIX` 3. 选择下载的文件即可总结
回看整个开发过程,从创建空项目到拥有可交互的预览面板,其实没有魔法,只有清晰的分工:VSCode负责界面和用户交互,Python服务负责模型计算,而我们的插件,就是那根精准的“神经”,把两者无缝连接。
你可能会发现,很多步骤看似“绕路”——比如先写模拟渲染再接入真实数据,先做基础API调用再加轮询和错误处理。这恰恰是工程实践的智慧:用最小可行模块验证核心链路,再逐步叠加复杂度。比起一上来就追求完美,这种渐进式构建更能保证每个环节都扎实可靠。
现在,你的VSCode里已经有一个真正能干活的HY-Motion插件了。下次当你需要为游戏角色生成一段奔跑动画,或者为数字人设计一个打招呼动作,不再需要切出编辑器、打开命令行、粘贴参数、等待日志输出……一切就在指尖完成。
技术的价值,从来不在参数有多炫,而在于它是否让创造变得更自然、更专注、更少干扰。这个插件,就是朝那个方向迈出的实在一步。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。