Unity ScriptedImporter 教程:自定义资源导入器
Unity ScriptedImporter 教程:自定义资源导入器
目标:创建一个能导入自定义二进制格式(.my3d)的 ScriptedImporter,将数据转换为 Unity 的 Mesh 和 Material。
1. 准备工作
1.1 创建测试文件
- 创建一个二进制文件
test.my3d(可用文本编辑器保存为二进制,或用代码生成)。 - 示例内容(简单顶点数据):
(实际开发中建议用C#生成二进制文件,此处简化说明)// 文件结构:顶点数(4字节) + 顶点坐标(x,y,z 每个4字节) 00000002 // 顶点数=2 000000000000F03F000000000000F03F // 顶点1: (1.0, 1.0, 1.0) 00000000000000400000000000000040 // 顶点2: (2.0, 2.0, 2.0)
2. 创建 ScriptedImporter
2.1 新建 C# 脚本
- 在
Assets/Editor文件夹下创建MyCustomImporter.cs(必须放在Editor文件夹内)。
2.2 编写基础代码
using UnityEngine;
using UnityEditor.AssetImporters; // 必须引用// 标注为ScriptedImporter,版本1,处理.my3d文件
[ScriptedImporter(1, "my3d")]
public class MyCustomImporter : ScriptedImporter {public override void OnImportAsset(AssetImportContext ctx) {// 1. 读取文件数据byte[] fileData = System.IO.File.ReadAllBytes(ctx.assetPath);// 2. 解析二进制数据My3DData data = ParseMy3DData(fileData);// 3. 创建MeshMesh mesh = GenerateMesh(data);// 4. 创建MaterialMaterial material = GenerateMaterial();// 5. 将资源添加到导入上下文ctx.AddObjectToAsset("mesh", mesh);ctx.AddObjectToAsset("material", material);ctx.SetMainObject(mesh); // 设置主资源(在Project窗口显示的默认对象)}// 解析二进制数据private My3DData ParseMy3DData(byte[] fileData) {// 示例:解析顶点数(4字节)int vertexCount = System.BitConverter.ToInt32(fileData, 0);// 解析顶点坐标(每个顶点12字节:x,y,z各4字节)Vector3[] vertices = new Vector3[vertexCount];for (int i = 0; i < vertexCount; i++) {int offset = 4 + i * 12; // 跳过顶点数字节float x = System.BitConverter.ToSingle(fileData, offset);float y = System.BitConverter.ToSingle(fileData, offset + 4);float z = System.BitConverter.ToSingle(fileData, offset + 8);vertices[i] = new Vector3(x, y, z);}return new My3DData { vertices = vertices };}// 生成Meshprivate Mesh GenerateMesh(My3DData data) {Mesh mesh = new Mesh();mesh.vertices = data.vertices;mesh.triangles = new int[] { 0, 1, 2 }; // 示例:简单三角形(需根据实际数据调整)mesh.RecalculateNormals();return mesh;}// 生成Materialprivate Material GenerateMaterial() {Material mat = new Material(Shader.Find("Standard"));mat.color = Color.red;return mat;}
}// 自定义数据结构
public struct My3DData {public Vector3[] vertices;
}3. 测试导入器
3.1 导入文件
- 将
test.my3d文件放入Assets文件夹。 - Unity 会自动调用
MyCustomImporter导入文件。 - 在 Project 窗口中会生成一个
.asset文件(包含Mesh和Material)。
3.2 验证结果
- 双击生成的
.asset文件,检查Mesh的顶点数据是否正确。 - 创建一个新场景,拖入生成的
Mesh和Material,确认渲染效果。
4. 高级功能扩展
4.1 添加自定义 Inspector 属性
在 Inspector 中显示可配置参数(如材质颜色):
[ScriptedImporter(1, "my3d")]
public class MyCustomImporter : ScriptedImporter {public Color materialColor = Color.red; // 显示在Inspector中public override void OnImportAsset(AssetImportContext ctx) {// ...(原有代码)Material material = GenerateMaterial();material.color = materialColor; // 使用Inspector配置的颜色// ...}
}4.2 处理更复杂的数据
示例:支持三角形索引
修改 ParseMy3DData 和 GenerateMesh:
private My3DData ParseMy3DData(byte[] fileData) {int vertexCount = System.BitConverter.ToInt32(fileData, 0);Vector3[] vertices = new Vector3[vertexCount];// ...(解析顶点)// 解析三角形索引(假设接下来是int[] triangles)int triangleCount = System.BitConverter.ToInt32(fileData, 4 + vertexCount * 12);int[] triangles = new int[triangleCount * 3];for (int i = 0; i < triangleCount * 3; i++) {triangles[i] = System.BitConverter.ToInt32(fileData, 8 + vertexCount * 12 + i * 4);}return new My3DData { vertices = vertices, triangles = triangles };
}private Mesh GenerateMesh(My3DData data) {Mesh mesh = new Mesh();mesh.vertices = data.vertices;mesh.triangles = data.triangles; // 使用解析的三角形索引mesh.RecalculateNormals();return mesh;
}4.3 依赖资源导入
如果 .my3d 引用了外部纹理,可自动导入关联文件:
public override void OnImportAsset(AssetImportContext ctx) {// ...(解析数据)// 假设文件末尾存储了纹理路径string texturePath = ParseTexturePath(fileData);if (!string.IsNullOrEmpty(texturePath)) {TextureImporter textureImporter = AssetImporter.GetAtPath(texturePath) as TextureImporter;if (textureImporter != null) {textureImporter.textureType = TextureImporterType.Default;textureImporter.SaveAndReimport();}Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);material.mainTexture = texture;}
}5. 调试与优化
5.1 日志输出
在 OnImportAsset 中添加 Debug.Log:
public override void OnImportAsset(AssetImportContext ctx) {Debug.Log($"Importing {ctx.assetPath}");// ...
}5.2 性能优化
- 缓存解析结果:对频繁导入的文件缓存解析数据。
- 异步导入:使用
AssetPostprocessor的异步接口处理大型文件。
6. 完整代码示例
using UnityEngine;
using UnityEditor.AssetImporters;[ScriptedImporter(1, "my3d")]
public class MyCustomImporter : ScriptedImporter {public Color materialColor = Color.red;public override void OnImportAsset(AssetImportContext ctx) {byte[] fileData = System.IO.File.ReadAllBytes(ctx.assetPath);My3DData data = ParseMy3DData(fileData);Mesh mesh = GenerateMesh(data);Material material = GenerateMaterial();material.color = materialColor;ctx.AddObjectToAsset("mesh", mesh);ctx.AddObjectToAsset("material", material);ctx.SetMainObject(mesh);}private My3DData ParseMy3DData(byte[] fileData) {int vertexCount = System.BitConverter.ToInt32(fileData, 0);Vector3[] vertices = new Vector3[vertexCount];for (int i = 0; i < vertexCount; i++) {int offset = 4 + i * 12;float x = System.BitConverter.ToSingle(fileData, offset);float y = System.BitConverter.ToSingle(fileData, offset + 4);float z = System.BitConverter.ToSingle(fileData, offset + 8);vertices[i] = new Vector3(x, y, z);}// 简化:假设只有2个顶点,生成1个三角形int[] triangles = { 0, 1, 2 }; return new My3DData { vertices = vertices, triangles = triangles };}private Mesh GenerateMesh(My3DData data) {Mesh mesh = new Mesh();mesh.vertices = data.vertices;mesh.triangles = data.triangles;mesh.RecalculateNormals();return mesh;}private Material GenerateMaterial() {return new Material(Shader.Find("Standard"));}
}public struct My3DData {public Vector3[] vertices;public int[] triangles;
}