Hand off a model from Blender (via BlenderMCP) into Unity (via MCP for Unity) — export the current Blender model, import it through import_model_file, and place
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-blender-to-unity-90e902e88deb ,按照其中的说明把「blender-to-unity」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
Bring whatever model is currently in Blender into the open Unity scene. The seam is the local filesystem: Blender exports a file, Unity imports it. The two servers never talk directly.
mcp__blender__* tools and MCP for Unity tools are connected.mcp__blender__get_scene_info /
mcp__blender__get_object_info). If empty, stop and tell the user — this skill does not generate models.import_model_file is in the asset_gen tool group, which is off by default (only core
loads). Enable it first with manage_tools (enable the asset_gen group), or call the C#
handler straight through batch_execute —
{"tool":"import_model_file","params":{"sourcePath":...,"name":...,"outputFolder":...}} (camelCase)
— which dispatches by name regardless of group gating.mcpforunity://editor/state for the project
root (the editor dataPath's parent). Decide the export format:
animation_type set (Step 3).mcp__blender__execute_blender_code:
import bpy, os, tempfile
out = os.path.join(tempfile.gettempdir(), "blender_to_unity.fbx")
# Export the selection if any, else the whole scene:
bpy.ops.export_scene.fbx(filepath=out, use_selection=bool(bpy.context.selected_objects),
apply_unit_scale=True, bake_space_transform=True)
print(out)
(glTF branch: out_glb = os.path.join(tempfile.gettempdir(), "blender_to_unity.glb"), then
bpy.ops.export_scene.gltf(filepath=out_glb, export_format='GLB', use_active_scene=True)
— its default use_active_scene=False can silently export a different open scene.)import_model_file:
import_model_file(source_path=<temp path>, name=<asset name>, target_size=<final size in meters>).
For a rigged/animated FBX, also pass animation_type="generic" (or "humanoid"; "legacy"
targets the old Animation-component system) — the importer defaults to "none", which
deliberately imports the mesh with zero animation clips. GLB ignores this (glTFast imports
animation itself), so it's an FBX-only knob.
It returns { asset_path, asset_guid }. Pass target_size as the intended final size, but treat
it only as a hint: it rescales at import solely when the project's Auto-normalize pref is on,
and even then is unreliable for Blender FBX (see the Scale note). Step 4 does the reliable
normalization.manage_scene / manage_gameobject). Instantiate the model at the chosen position via
manage_gameobject(action="create", prefab_path=<asset_path>, name=<asset name>, position=[x,y,z]).
Then normalize its size deterministically — Blender FBX commonly imports ~100× too large — by
measuring the placed model's world bounds and scaling so its largest dimension equals your target
size. Run via execute_code (substitute your object name and target meters):
var go = GameObject.Find("<asset name>");
var rs = go.GetComponentsInChildren<Renderer>();
var b = rs[0].bounds; for (int i = 1; i < rs.Length; i++) b.Encapsulate(rs[i].bounds);
float maxDim = Mathf.Max(b.size.x, Mathf.Max(b.size.y, b.size.z));
float target = 2f; // intended size in meters
if (maxDim > 0.0001f) go.transform.localScale *= target / maxDim;
execute_blender_code:
import bpy, json
out = {}
for m in bpy.data.materials:
if not (m.use_nodes and m.node_tree): continue
col, s = (0, 0, 0), 0.0
p = next((n for n in m.node_tree.nodes if n.type == 'BSDF_PRINCIPLED'), None)
es = next((k for k in ('Emission Color', 'Emission') if p and k in p.inputs), None) # 4.x / 3.x name
if es:
col = tuple(p.inputs[es].default_value)[:3]
s = float(p.inputs['Emission Strength'].default_value)
e = next((n for n in m.node_tree.nodes if n.type == 'EMISSION'), None)
if e and s == 0:
col = tuple(e.inputs['Color'].default_value)[:3]; s = float(e.inputs['Strength'].default_value)
if s > 0 and sum(col) > 0.01:
out[m.name] = [round(col[0], 3), round(col[1], 3), round(col[2], 3), round(s, 3)]
print(json.dumps(out))
b. In Unity (execute_code): extract the FBX's materials so they're editable
(AssetDatabase.ExtractAsset per Material, then ImportAsset(fbx, ForceUpdate)), then for
each dumped name set _EmissionColor = color * Mathf.Clamp(strength*0.45f, 1.5f, 5f),
EnableKeyword("_EMISSION"), and globalIlluminationFlags = RealtimeEmissive. Match names
case-insensitively — FBX mangles case and can split one material into variants
(EdgeCyan → EdgeCyan + EDGE_CYAN); set every match. Add a global Bloom volume and enable
the camera's renderPostProcessing so the emission actually glows.manage_camera(action="screenshot", include_image=true) and report the
asset path + a screenshot.import_model_file's target_size only rescales at
import when the project's Auto-normalize pref is enabled, and its importer-level normalization is
unreliable for Blender FBX (it can over- or under-shoot, e.g. a target_size=2 model measured 200 m).
The robust fix is the Step 4 measure-bounds-then-set-localScale routine, which hits the target
size deterministically regardless of the import scale or the Auto-normalize pref.emissiveFactor + KHR_materials_emissive_strength
natively, so emission survives with no post-step (bpy.ops.export_scene.gltf(filepath=out, export_format='GLB', use_active_scene=True));
(b) with FBX, run Step 5 to dump Blender's emission and reapply it. Also check the mesh for a
color_attributes (vertex color) layer — the data survives FBX, but URP Lit won't display it;
that needs a vertex-color-reading shader, not _EmissionColor.ModelImporter.animationType = Generic to surface transform animation, and needs material/texture
extraction to assign an embedded texture. Both bake modifiers and carry geometry + vertex-color
data (URP Lit won't display vertex colors). Neither carries procedural/node materials — bake
them to image textures in Blender first.AnimatorController
drives it — the placed model has an Animator with a null controller, so it looks frozen. Build
one with ( → add the clip as a looping state →
onto the instance) rather than hand-rolling . See bridge-fidelity gotcha #7.manage_animationcontroller_createcontroller_assignexecute_codeimport_model_file copies only the single source file; for multi-file exports (a text .gltf with an external .bin, or an .obj with a sibling .mtl/textures), zip them first and pass the .zip — a bare .gltf/.obj will lose its sidecars.