Unity Editor如何在点击Play按钮前预加载大量游戏信息表?
嘿,这个问题我太熟了——很多做大型游戏的开发者都会遇到Play模式启动加载配置表慢的问题,完全可以在点击Play按钮前就把这些表格预加载好,把那一分钟的等待砍掉。下面给你几个实用的方案,按实现难度和灵活性排序:
方案1:利用Editor预加载并序列化到ScriptableObject
这是最常用也最省心的方案:把表格数据在Editor模式下提前解析好,序列化到ScriptableObject(SO)里,Play模式启动时直接读这个SO,完全跳过IO和解析步骤。
具体步骤:
- 先定义一个用来存储表格数据的
ScriptableObject类 - 写一个Editor工具类,加个菜单选项手动触发预加载(或者自动触发)
- 在工具里读取表格文件、解析数据,把结果存到SO里并保存
- Play模式下直接引用这个SO,不用再做任何加载操作
代码示例:
using UnityEditor; using UnityEngine; using System.IO; using System.Collections.Generic; // 存储表格数据的ScriptableObject,右键可以直接创建实例 [CreateAssetMenu(fileName = "GameDataTable", menuName = "ScriptableObjects/GameDataTable")] public class GameDataTable : ScriptableObject { // 替换成你实际的数据结构 public List<PlayerStatsData> playerStatsList; } // 示例数据结构,根据你的表格定义调整 public class PlayerStatsData { public int id; public string playerName; public int maxHp; } // Editor预加载工具 public class PreloadTableTool { // 给Editor加个菜单选项,手动触发预加载 [MenuItem("Tools/预加载游戏配置表")] public static void PreloadGameTables() { // 表格文件路径(根据你的项目结构调整) string csvPath = Path.Combine(Application.dataPath, "GameTables/PlayerStats.csv"); string csvContent = File.ReadAllText(csvPath); // 解析CSV内容到数据结构(替换成你实际的解析逻辑,比如用CsvHelper库) List<PlayerStatsData> parsedData = ParseCsvToData(csvContent); // 加载或创建ScriptableObject实例 GameDataTable dataTable = AssetDatabase.LoadAssetAtPath<GameDataTable>("Assets/Resources/GameDataTable.asset"); if (dataTable == null) { dataTable = CreateInstance<GameDataTable>(); AssetDatabase.CreateAsset(dataTable, "Assets/Resources/GameDataTable.asset"); } // 赋值并保存 dataTable.playerStatsList = parsedData; EditorUtility.SetDirty(dataTable); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log("配置表预加载完成!Play模式可以直接用啦"); } // 示例CSV解析方法,实际项目可以用成熟的库(比如CsvHelper)简化 private static List<PlayerStatsData> ParseCsvToData(string csvContent) { List<PlayerStatsData> result = new List<PlayerStatsData>(); string[] lines = csvContent.Split('\n'); // 跳过表头行 for (int i = 1; i < lines.Length; i++) { string[] columns = lines[i].Split(','); if (columns.Length < 3) continue; result.Add(new PlayerStatsData { id = int.Parse(columns[0]), playerName = columns[1], maxHp = int.Parse(columns[2]) }); } return result; } }
进阶:自动触发预加载
不想每次手动点菜单?可以加个自动触发逻辑,比如项目加载完成时、或者表格文件修改时自动预加载:
public class AutoPreloadTrigger { // 项目加载完成时自动执行 [InitializeOnLoadMethod] static void OnProjectLoaded() { // 这里可以加判断:比如检查表格文件的修改时间,只有更新过才重新预加载 bool needUpdate = CheckIfTableModified(); if (needUpdate) { PreloadTableTool.PreloadGameTables(); } } // 检查表格是否修改的逻辑(示例) private static bool CheckIfTableModified() { string csvPath = Path.Combine(Application.dataPath, "GameTables/PlayerStats.csv"); string soPath = "Assets/Resources/GameDataTable.asset"; FileInfo csvFile = new FileInfo(csvPath); AssetImporter soImporter = AssetImporter.GetAtPath(soPath); if (soImporter == null) return true; return csvFile.LastWriteTime > soImporter.assetTimeStamp; } }
方案2:Editor异步预加载(超大型表格专用)
如果你的表格大到同步解析会卡Editor界面,可以拆成异步分帧处理,避免阻塞操作:
public class AsyncPreloadTool { private static int currentLineIndex = 0; private static string[] allCsvLines; private static GameDataTable targetDataTable; private static List<PlayerStatsData> tempDataList = new List<PlayerStatsData>(); [MenuItem("Tools/异步预加载大型配置表")] public static void StartAsyncPreload() { string csvPath = Path.Combine(Application.dataPath, "GameTables/HugePlayerStats.csv"); allCsvLines = File.ReadAllLines(csvPath); currentLineIndex = 1; // 跳过表头 targetDataTable = AssetDatabase.LoadAssetAtPath<GameDataTable>("Assets/Resources/HugeGameDataTable.asset"); if (targetDataTable == null) { targetDataTable = CreateInstance<GameDataTable>(); AssetDatabase.CreateAsset(targetDataTable, "Assets/Resources/HugeGameDataTable.asset"); } tempDataList.Clear(); // 注册Editor每帧更新回调,分帧处理 EditorApplication.update += ProcessNextBatch; Debug.Log("开始异步预加载,不会卡Editor啦"); } private static void ProcessNextBatch() { // 每帧处理100行,可根据性能调整 int batchSize = 100; int endIndex = Mathf.Min(currentLineIndex + batchSize, allCsvLines.Length); for (; currentLineIndex < endIndex; currentLineIndex++) { string[] columns = allCsvLines[currentLineIndex].Split(','); if (columns.Length < 3) continue; tempDataList.Add(new PlayerStatsData { id = int.Parse(columns[0]), playerName = columns[1], maxHp = int.Parse(columns[2]) }); } // 加载完成时保存数据并取消回调 if (currentLineIndex >= allCsvLines.Length) { targetDataTable.playerStatsList = tempDataList; EditorUtility.SetDirty(targetDataTable); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorApplication.update -= ProcessNextBatch; Debug.Log("异步预加载完成!"); } } }
方案3:用Addressables/Resource预打包
如果你的表格是二进制或已序列化格式,可以提前用Addressables打包,Play模式加载已打包的资源会比原始文件快很多:
- 把表格文件(或预生成的SO)加入Addressables Group
- 在Editor里点击Build按钮提前打包资源
- Play模式下用
Addressables.LoadAssetAsync<GameDataTable>()加载,因为已经是打包好的格式,加载速度会大幅提升
注意事项
- 一定要加缓存判断:只有当表格文件修改时才重新预加载,不然每次打开项目都重复操作会浪费时间
- 如果表格是加密的,预加载时要在Editor里完成解密和解析,Play模式直接用解密后的数据
- 测试时记得对比Play模式启动时间,确保预加载确实起到了优化效果
内容的提问来源于stack exchange,提问作者Hyunjik Bae
相关产品推荐
相关产品推荐

