跳转至

Unity MCP 材质操作指南

最后更新: 2026-05-03
适用环境: Unity 2022.3.22f1 + MCP Server 3.2.4
着色器: lilToon (Hidden/lilToonOutline)

概述

本文档记录通过 MCP 协议远程操作 Unity Editor 材质球的完整工作流,覆盖 lilToon 着色器多层贴图叠加、中文路径编码问题、MCP 认证等核心场景。


MCP 服务器协议

1. 初始化会话

必须先调用 initialize 获取 Session ID:

init_payload = {
    "jsonrpc": "2.0",
    "id": 0,
    "method": "initialize",
    "params": {
        "protocolVersion": "2024-11-05",
        "capabilities": {},
        "clientInfo": {"name": "hermes", "version": "1.0.0"}
    }
}

headers = {
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream"
}

response = requests.post(base_url + "/mcp", json=init_payload, headers=headers)
session_id = response.headers.get("Mcp-Session-Id")

⚠️ 关键协议要求: - Accept 头必须同时包含 application/jsontext/event-stream - 服务器返回 406 Not Acceptable 表示 Accept 头不完整 - 后续所有请求必须携带 Mcp-Session-Id

2. 执行 C# 代码

payload = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "execute_code",
        "arguments": {
            "action": "execute",
            "code": "/* C# code here */"
        }
    }
}

headers = {
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
    "Mcp-Session-Id": session_id
}

3. 解析响应

application/json, text/event-stream",
    "Mcp-Session-Id": session_id
}

3. 解析响应

```python# Server-Sent Events (SSE) 格式响应 data = json.loads(response.text.split('data: ')[1].split('\r\n')[0]) result = json.loads(data['result']['content'][0]['text'])

result['data']['result'] 包含 C# 代码返回值

---

## 中文路径编码问题

### 问题现象

Unity AssetDatabase 返回中文路径时为 UTF-8 编码乱码:

| 实际路径 | AssetDatabase 返回 |
|---------|-------------------|
| `Assets/!改变` | `Assets/!æ¹å¤` |
| `Assets/!改变/R18カスタムディーテール` | `Assets/!æ¹å¤/R18ãã¯ã¹ãã£ã¼` |

### 解决方案:使用 GUID 加载资产

**不要直接使用中文路径**,改用 GUID 两步加载:

```csharp
// 步骤 1: 扫描获取 GUID
var guids = UnityEditor.AssetDatabase.FindAssets("");
foreach (var guid in guids) {
    var path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
    if (path.Contains("!")) {
        // 记录 guid|type|path 映射
    }
}

// 步骤 2: 用 GUID 加载资产(完全避开中文路径)
var r18Guid = "afa7bb1e008aaa44d812dc35a3a97afa";
var path = UnityEditor.AssetDatabase.GUIDToAssetPath(r18Guid);
var tex = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(path);

lilToon 材质球贴图规则

; var tex = UnityEditor.AssetDatabase.LoadAssetAtPath(path);

---

## lilToon 材质球贴图规则### 多层贴图叠加机制

lilToon 支持多层贴图叠加,**额外贴图通常不是替换 `_MainTex`**,而是作为叠加层添加:

| 新贴图类型 | 目标插槽 | 启用开关 | 混合模式参数 | 说明 |
|-----------|---------|---------|-------------|------|
| 第二层身体/服装贴图 | `_Main2ndTex` | `_UseMain2ndTex = 1` | `_Main2ndTexBlendMode` | 叠加在原始贴图上,0=Normal |
| 第三层细节贴图 | `_Main3rdTex` | `_UseMain3rdTex = 1` | `_Main3rdTexBlendMode` | 额外叠加层 |
| AO/阴影遮罩 | `_ShadowStrengthMask` | `_UseShadow = 1` | `_ShadowStrength` | 控制阴影强度分布 |
| MatCap 材质捕获 | `_MatCapTex` | `_UseMatCap = 1` | `_MatCapBlend` | 改变表面光泽/质感 |
| 第二 MatCap | `_MatCap2ndTex` | `_UseMatCap2nd = 1` | `_MatCap2ndBlend` | 额外材质捕获层 |
| 法线贴图 | `_BumpMap` | `_UseBumpMap = 1` | `_BumpScale` | 表面凹凸细节 |
| 自发光贴图 | `_EmissionMap` | `_UseEmission = 1` | `_EmissionBlend` | 发光效果 |

### 贴图推断规则表

| 文件名关键词 | 推断类型 | 目标插槽 | 优先级 |
|-------------|---------|---------|-------|
| `AO`, `Occlusion`, `ShadowMask` | 环境光遮蔽 | `_ShadowStrengthMask` | 高 |
| `Matcap`, `MatCap` | 材质捕获 | `_MatCapTex` | 高 |
| `Normal`, `Bump` | 法线贴图 | `_BumpMap` | 高 |
| `Emission`, `Emissive` | 自发光 | `_EmissionMap` | 高 |
| `Body`, `Cloth`, 日期戳 | 主体贴图 | **先试** `_Main2ndTex`(叠加),不对再试 `_MainTex`(替换) | 中 |
missionMap` | 高 |
| `Body`, `Cloth`, 日期戳 | 主体贴图 | **先试** `_Main2ndTex`(叠加),不对再试 `_MainTex`(替换) | 中 |### 应用步骤(完整代码)

```csharp
// 1. 加载材质球
var bodyMat = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(
    "Assets/Cazalis/Material/Cazalis_Body.mat");

// 2. 用 GUID 加载贴图
var r18Guid = "afa7bb1e008aaa44d812dc35a3a97afa";
var r18Path = UnityEditor.AssetDatabase.GUIDToAssetPath(r18Guid);
var r18Tex = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(r18Path);

// 3. 应用到第二层(叠加)
bodyMat.SetFloat("_UseMain2ndTex", 1);
bodyMat.SetTexture("_Main2ndTex", r18Tex);
bodyMat.SetFloat("_Main2ndTexBlendMode", 0); // 0=Normal

// 4. 保存修改
UnityEditor.EditorUtility.SetDirty(bodyMat);
UnityEditor.AssetDatabase.SaveAssets();


材质球修改最佳实践

✅ 正确做法

// 直接加载并修改材质球文件(永久生效)
var mat = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(path);
mat.SetTexture("_Main2ndTex", newTex);
UnityEditor.EditorUtility.SetDirty(mat);
UnityEditor.AssetDatabase.SaveAssets();

❌ 错误做法

// renderer.material 会在编辑模式创建实例,导致材质泄露
var mat = renderer.material; // 触发 "Instantiating material" 警告

Console 警告信息

Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.


ll leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.`

---## 工具环境区分

工具 执行环境 语言 用途
execute_code (Hermes) Python 沙箱 Python 发送 HTTP 请求调用 MCP 服务器
execute_code (Unity MCP) Unity Editor C# 操作 Unity 资产和场景

调用链
Hermes PythonHTTP RequestUnity MCP ServerC# CodeUnity Editor


完整工作流程

flowchart TD
    A[建立 MCP 连接] --> B[Initialize 获取 Session ID]
    B --> C[扫描目标资产获取 GUID]
    C --> D[读取材质球属性列表]
    D --> E{判断贴图类型}
    E -->|AO/阴影| F[→ _ShadowStrengthMask]
    E -->|MatCap| G[→ _MatCapTex]
    E -->|主体贴图| H[先试 _Main2ndTex 叠加]
    F --> I[保存材质球]
    G --> I
    H --> J{效果正确?}
    J -->|是| I
    J -->|否| K[改用 _MainTex 替换]
    K --> I
    I --> L[验证材质球状态]
  1. 建立 MCP 连接 → 调用 initialize 获取 Session ID
  2. 扫描目标资产 → 用 GUID 避开中文路径问题
  3. 读取材质球属性 → 确认着色器类型和可用插槽
  4. 应用贴图 → 按 lilToon 规则选择正确插槽
  5. 验证状态 → 读取材质球确认贴图已加载
  6. 保存SetDirty + SaveAssets

常见问题排查

问题 HTTP 状态码 原因 解决方案
贴图加载返回 NULL - 中文路径乱码 改用 GUID 加载资产
Not Acceptable 406 Accept 头不完整 添加 text/event-stream 到 Accept
Bad Request 400 缺少 Session ID initialize 获取 Session ID
Console 红色警告 - 使用了 renderer.material 改用 sharedMaterial 或直接修改文件
贴图显示不对 - 插槽选错 查看 lilToon 规则表,尝试 _Main2ndTex
代码执行成功但返回空 - C# 代码未 return 确保代码末尾有 return result;

附录:Cazalis 模型材质结构

in2ndTex| | 代码执行成功但返回空 | - | C# 代码未 return | 确保代码末尾有return result;` |


附录:Cazalis 模型材质结构### 模型层级

Cazalis
├── Body_Base (SkinnedMeshRenderer)
│   └── Cazalis_Body.mat
│       ├── _MainTex → Cazalis_Body.png (原始身体)
│       ├── _Main2ndTex → R18 叠加层
│       ├── _ShadowStrengthMask → AOmap.png
│       └── _MatCapTex → matcap 1.png
└── Body (SkinnedMeshRenderer)
    ├── Cazalis_Face.mat → _MainTex → Cazalis_Face.png
    └── Cazalis_Face_Alpha.mat → _MainTex → Cazalis_Face.png

原始贴图清单

贴图 路径 用途
Cazalis_Body.png Assets/Cazalis/Texture/ 原始身体贴图
Cazalis_Face.png Assets/Cazalis/Texture/ 脸部贴图
Cazalis_Hair.png Assets/Cazalis/Texture/ 头发贴图
Cazalis_Cloth.png Assets/Cazalis/Texture/ 服装贴图
Cazalis_Butterfly.png Assets/Cazalis/Texture/ 蝴蝶装饰贴图

MatCap 贴图清单

贴图 路径 用途
Cazalis_Matcap_Hair.png Assets/Cazalis/Texture/Matcap/ 头发材质捕获
Cazalis_Matcap_Hairpin.png Assets/Cazalis/Texture/Matcap/ 发饰材质捕获
Cazalis_Matcap_Pearl.png Assets/Cazalis/Texture/Matcap/ 珍珠材质捕获
Cazalis_Matcap_Shoes.jpg Assets/Cazalis/Texture/Matcap/ 鞋子材质捕获
Cazalis_Matcap_Whitemetal.png Assets/Cazalis/Texture/Matcap/ 白金属材质捕获