You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity打包Windows遇CS0103错误:EditorUtility与AssetDatabase不存在

解决Unity打包Windows版本时EditorUtility和AssetDatabase不存在的错误

问题背景

开发面向牙医的Unity 3D牙齿移动项目,核心功能包括:

  • 导入含牙齿位置的CSV文件并复制到项目文件夹
  • 点击「Next Week」按钮保存牙齿新位置为Week + n格式CSV,读取最新CSV用于后续操作
  • 点击「Save and Export」导出STL文件

打包Windows版本时出现以下编译错误:

UnityEditor.BuildPlayerWindow+BuildMethodException: 4 errors
at UnityEditor.BuildPlayerWindow+DefaultBuildMethods.BuildPlayer (UnityEditor.BuildPlayerOptions options) [0x002da] in :0
at UnityEditor.BuildPlayerWindow.CallBuildMethods (System.Boolean askForBuildLocation, UnityEditor.BuildOptions defaultBuildOptions) [0x00080] in :0
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

error CS0103: The name 'EditorUtility' does not exist in the current context
error CS0103: The name 'AssetDatabase' does not exist in the current context
Build completed with a result of 'Failed' in 3 seconds (2931 ms)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

错误原因

EditorUtility和AssetDatabase属于UnityEditor命名空间,仅在Unity编辑器环境中可用,打包后的运行时程序不包含该命名空间的API,因此编译时会提示名称不存在。

解决方案

1. 隔离编辑器专属代码

用#if UNITY_EDITOR和#endif条件编译指令包裹仅在编辑器中运行的代码,确保打包时编译器忽略这部分代码。

2. 替换运行时资源加载逻辑

运行时不能使用AssetDatabase.LoadAssetAtPath,改用System.IO直接读取文件内容,或使用Resources.Load(需确保文件在Resources文件夹下并正确打包)。

3. 移除运行时不必要的AssetDatabase操作

打包后的程序无需调用AssetDatabase.Refresh(),该操作仅用于编辑器中刷新资源列表。

修改后的代码示例

修改SubmitManager.cs

using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using CsvManagement;
using System.IO;

namespace Submit
{
    public class SubmitManager : MonoBehaviour
    {
        public GameObject teeth;
        public CsvExampleObjectSpawner csv; // 直接引用场景实例,不要new
        
        public static int countweek;
        public static string filename;
        
        public void submit()
        {
            if(CsvExampleObjectSpawner.next == 1)
            {
                countweek = countweek + 1;
                filename = "week"+countweek+".csv";
                csv.wirteCSV(filename);     

                // 仅编辑器环境刷新资源库
                #if UNITY_EDITOR
                UnityEditor.AssetDatabase.Refresh();
                #endif

                StartCoroutine(NextScenes());
            }
            else
            {
                Debug.Log("Please select file");
            }
        }

        IEnumerator NextScenes()
        {
            yield return new WaitForSeconds(0.5f);        
            SceneManager.LoadScene("Scene2");
        }
    }
}

修改CsvExampleObjectSpawner.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LINQtoCSV;
using System.IO;
using System.Linq;
using UnityEngine.UI;

namespace CsvManagement
{
    public class CsvExampleObjectSpawner : MonoBehaviour
    {
        private Dictionary<string, GameObject> prefabs = new Dictionary<string, GameObject>();

        public GameObject[] objects;
        public GameObject myGameObjects;

        public TextAsset sceneDescriptor;
        string sourcePath;
        public static string filename;
        public Text FileName;
       
        public static Stack<string> stackOfFiles = new Stack<string>();
        public static int next; 

        public List<CsvSceneObject> posOfTeeth = new List<CsvSceneObject>();

        void Awake()
        {
            foreach(var obj in objects)
            {
                prefabs[obj.name] = obj;
            }
        }
      
        public void OpenFileExplorer()
        {
            #if UNITY_EDITOR
            sourcePath = UnityEditor.EditorUtility.OpenFilePanel("Select the CSV file", "", "csv");
            #else
            // 运行时替换为自定义文件选择逻辑,例如:
            // var dialog = new System.Windows.Forms.OpenFileDialog();
            // dialog.Filter = "CSV Files (*.csv)|*.csv";
            // if(dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            // {
            //     sourcePath = dialog.FileName;
            // }
            #endif
        
            if (!string.IsNullOrEmpty(sourcePath))
            {
                filename = Path.GetFileName(sourcePath);
                FileName.text = "File Name : " +filename;
               
                CheckEmptyFolderAndAddFile();
                next = 1;
            }
            else
            {
                if(stackOfFiles == null || stackOfFiles.Count == 0)
                {
                    next = 0;
                }
                else
                {
                    Debug.Log("The user unselected the file.");
                }
            }
        }

        public void CheckEmptyFolderAndAddFile()
        {
            string path = Application.dataPath + "/Resources/";
            
            if (Directory.GetFiles(path).Length == 0 && Directory.GetDirectories(path).Length == 0)
            {
                Debug.Log("Directory is empty.");
                loadCSV();
                readCSV(filename);
                stackOfFiles.Push(filename); 
            }
            else
            {          
                foreach (string file in Directory.GetFiles(path))
                {
                    File.Delete(file);
                }

                loadCSV();
                readCSV(filename);
                stackOfFiles.Push(filename);
            }
        }

        public void loadCSV()
        {
            string backupPath = Application.dataPath + "/Resources/";
            File.Copy(sourcePath, Path.Combine(backupPath, filename));
        }

        public void readCSV(string filename)
        { 
            // 运行时直接读取文件内容,无需通过AssetDatabase
            string filePath = Path.Combine(Application.dataPath, "Resources", filename);
            string csvContent = File.ReadAllText(filePath);
            
            CsvFileDescription inputFileDescription = new CsvFileDescription
            {
                SeparatorChar = ',', 
                FirstLineHasColumnNames = true
            };

            using (var ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(csvContent)))
            {
                using (var txtReader = new StreamReader(ms))
                {
                    CsvContext cc = new CsvContext();
                    cc.Read<CsvSceneObject>(txtReader, inputFileDescription)
                        .ToList()
                        .ForEach( so =>
                        {
                            if(prefabs.TryGetValue(so.PrefabName, out var toothObj))
                            {
                                toothObj.transform.position = new Vector3(so.X, so.Y, so.Z);
                                toothObj.transform.rotation = Quaternion.Euler(so.RoX, so.RoY, so.RoZ);
                            }
                        });
                }
            }
        }

        public void wirteCSV(string filename)
        {
            // 运行时使用持久化路径存储文件,避免权限问题
            string pathOfFile = Path.Combine(Application.persistentDataPath, "CsvWeek", filename);
            Directory.CreateDirectory(Path.GetDirectoryName(pathOfFile));
            
            posOfTeeth.Clear();
            for(int i = 0; i < objects.Length; i++)
            {
                posOfTeeth.Add(
                    new CsvSceneObject
                    {
                        PrefabName = objects[i].name.ToString(),
                        X = objects[i].transform.position.x,
                        Y = objects[i].transform.position.y,
                        Z = objects[i].transform.position.z,
                        RoX = objects[i].transform.rotation.eulerAngles.x,
                        RoY = objects[i].transform.rotation.eulerAngles.y,
                        RoZ = objects[i].transform.rotation.eulerAngles.z
                    }
                );
            }
           
            CsvFileDescription outputFileDescription = new CsvFileDescription
            {
                SeparatorChar = ',', 
                FirstLineHasColumnNames = true,
            };

            CsvContext cc = new CsvContext();
            cc.Write<CsvSceneObject>(posOfTeeth, pathOfFile, outputFileDescription);

            Debug.Log($"Successfully wrote the file to: {pathOfFile}");
        }
     
        // 确保CSV映射类已正确定义
        public class CsvSceneObject
        {
            [CsvColumn(Name = "PrefabName")]
            public string PrefabName { get; set; }
            [CsvColumn(Name = "X")]
            public float X { get; set; }
            [CsvColumn(Name = "Y")]
            public float Y { get; set; }
            [CsvColumn(Name = "Z")]
            public float Z { get; set; }
            [CsvColumn(Name = "RoX")]
            public float RoX { get; set; }
            [CsvColumn(Name = "RoY")]
            public float RoY { get; set; }
            [CsvColumn(Name = "RoZ")]
            public float RoZ { get; set; }
        }
    }
}

额外注意事项

  • 运行时文件路径:避免直接读写Assets文件夹,打包后该路径不存在,改用Application.persistentDataPath存储用户生成的文件。
  • 文件选择:运行时无法使用EditorUtility.OpenFilePanel,需替换为适合运行时的文件选择方案,比如Windows平台可引用System.Windows.Forms或使用Unity内置的文件对话框(Unity 2020+支持)。
  • 资源加载:如果需要在运行时加载CSV作为TextAsset,需确保文件在Resources文件夹下,打包前已正确导入,使用Resources.Load<TextAsset>(filenameWithoutExtension)加载。

内容的提问来源于stack exchange,提问作者Supreeya Kokheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 09:03:10