unity游戏内部多世界管理逻辑


1. 世界管理核心组件

世界管理逻辑由以下几个核心脚本协同实现:

  • WorldManager: 负责世界数据的核心管理,包括创建、保存、加载、删除世界,以及跟踪和管理场景中的动态对象。
  • WorldUploader: 处理世界的导入和导出功能,将世界数据序列化为文件并支持压缩存档。
  • SceneCacheManager: 缓存场景中的对象数据,优化场景加载和对象管理的性能。
  • PrefabNamingManager: 管理世界中预制体对象的命名,确保对象名称在世界中的唯一性。
  • MainMenuController: 提供用户界面交互,允许玩家通过UI创建、选择、加载和删除世界。

2. 世界数据结构

世界数据由 WorldManager.WorldData 类定义,包含以下关键字段:

  • worldName: 世界名称,用于标识世界。
  • baseSceneName: 世界基于的场景名称(如 BaseSceneForestScene)。
  • saveTime: 保存时间,记录世界的最后保存时间。
  • objectStates: 存储场景中动态对象的状态(位置、旋转、缩放、脚本等)。
  • trackedObjects: 跟踪当前世界中的动态对象(GameObject)。

每个对象的状态由 WorldManager.ObjectState 定义,包含:

  • name: 对象名称(代替GUID,确保唯一性)。
  • prefabPath: 对象的预制体路径。
  • position, rotation, scale: 对象的变换信息。
  • nativeScript, compiledScripts: 对象的脚本数据(原生脚本和编译后的脚本)。

3. 世界管理逻辑

3.1 世界创建

  • 触发方式: 通过 MainMenuController 的“新建世界”面板,用户输入世界名称并选择基础场景(BaseScene, ForestScene, DesertScene)。
  • 流程:
    1. MainMenuController 调用 WorldManager.CreateNewWorld,传入世界名称和基础场景名称。
    2. WorldManager 异步加载指定场景(SceneManager.LoadSceneAsync)。
    3. 创建新的 WorldData 实例,初始化世界名称、场景名称、保存时间等。
    4. 扫描场景中的根对象(通过 CollectSceneObjects),将动态对象添加到 trackedObjects
    5. 将新世界添加到 worlds 列表,更新 currentWorldIndex
    6. 保存世界列表(SaveWorldList)到持久化存储(使用 ES3)。
  • 命名管理: PrefabNamingManager 确保场景中对象的名称唯一,基于基础名称递增计数(如 Object (1), Object (2))。

3.2 世界保存

  • 触发方式: 通过 WorldManager.SaveCurrentWorld 手动触发,或在创建/加载世界时自动调用。
  • 流程:
    1. 获取当前世界(worlds[currentWorldIndex])和场景中的动态对象(trackedObjects)。
    2. 如果 trackedObjects 为空,重新扫描场景动态对象(CollectSceneObjects)。
    3. 遍历动态对象,生成 ObjectState(包括名称、预制体路径、变换信息、脚本等)。
    4. 分批保存对象状态(每批50个对象,优化性能)。
    5. 使用 ES3 将对象状态(World_{index}_Objects)和世界数据(World_{index})保存到持久化存储。
    6. 更新世界列表(SaveWorldList)。
  • 注意: 跳过带有 UnableToSave 标签的对象,确保只保存动态对象。

3.3 世界加载

  • 触发方式: 通过 MainMenuController 的世界列表选择世界,调用 WorldManager.LoadWorld
  • 流程:
    1. 异步加载目标世界的场景(baseSceneName)。
    2. 获取当前场景对象(CollectSceneObjects)和保存的对象状态(World_{index}_Objects)。
    3. 比较当前场景对象和保存状态的名称:
      • 删除多余对象(场景中有但保存状态中没有)。
      • 实例化缺失对象(保存状态中有但场景中没有),通过 Resources.Load 加载预制体。
    4. 恢复对象状态(位置、旋转、缩放、脚本等),分批处理(每批50个对象)。
    5. 更新 trackedObjectscurrentWorldIndex
  • 脚本恢复: 如果对象有脚本(nativeScriptcompiledScripts),通过 ScriptCompiler 重新编译并应用。

3.4 世界导入/导出

  • 导出WorldUploader.ExportWorld):
    1. 收集当前世界数据(ExportWorldData),包括世界名称、场景名称、保存时间、对象状态、预制体编号表(prefabNumberTable)和蓝图数据(blueprints)。
    2. 使用 ES3 保存数据到 .es3 文件。
    3. .es3 文件压缩为 .zip 文件,删除临时文件。
    4. 提供进度回调(onProgress)和完成回调(onComplete)。
  • 导入WorldUploader.ImportWorld):
    1. 解压 .zip 文件到临时目录,提取 .es3 文件。
    2. 加载 ExportWorldData,验证数据有效性(世界名称、场景名称等)。
    3. 创建新世界,生成唯一的世界名称(避免冲突,如 World (1))。
    4. 保存对象状态、预制体编号表和蓝图数据到 ES3。
    5. 更新世界列表,通知 PrefabNamingManager 切换世界。
    6. 删除临时目录。
  • 注意: 导入时会检查数据完整性(如 objectStatesprefabNumberTable 是否存在)。

3.5 世界删除

  • 触发方式: 通过 MainMenuController 的世界列表删除按钮,调用 WorldManager.DeleteWorld
  • 流程:
    1. 删除指定世界的持久化数据(World_{index}_ObjectsWorld_{index})。
    2. worlds 列表移除世界。
    3. 更新 currentWorldIndex(如果当前世界被删除)。
    4. 保存更新后的世界列表。
    5. PrefabNamingManager.DeleteWorldCounters 清除该世界的命名计数器。

4. 场景缓存管理

  • 作用: SceneCacheManager 缓存场景中的对象数据,减少重复扫描的开销。
  • 流程:
    1. 启动时加载缓存(ES3.Load("SceneCache")),如果不存在则生成。
    2. 生成缓存(GenerateSceneCache):
      • 异步加载模板场景(templateScenes)。
      • 扫描场景中的根对象,跳过 UnableToSave 标签的对象。
      • 记录对象名称和预制体路径(SceneObjectData)。
      • 保存缓存到 ES3。
    3. 提供 GetSceneObjectData 接口,供其他脚本查询场景对象数据。
  • 优化: 使用正则表达式清理对象名称(去除 (Clone)(1) 等后缀)。

5. 用户界面交互

  • 主菜单 (MainMenuController):
    • 显示主菜单、设置、退出按钮。
    • “进入世界”按钮显示世界管理面板。
  • 世界管理面板:
    • 列出所有世界(名称、保存时间、大小),支持加载和删除。
    • “新建世界”按钮显示创建世界面板。
  • 创建世界面板:
    • 输入世界名称,选择基础场景。
    • 验证输入(名称非空、场景已选)后调用 WorldManager.CreateNewWorld
  • 加载面板:
    • 显示加载进度(场景加载占50%,对象恢复占50%)。
    • 使用 loadingSliderloadingText 提供反馈。
  • 错误提示:
    • 显示错误消息(如“世界名称不能为空”),用户确认后关闭。

6. 关键优化

  • 分批处理: 保存和加载对象状态时分批处理(每批50个对象),避免性能瓶颈。
  • 异步加载: 使用 SceneManager.LoadSceneAsync 和协程(IEnumerator)异步加载场景和处理数据。
  • 持久化存储: 使用 ES3 序列化世界数据,确保数据在游戏重启后保留。
  • 名称唯一性: PrefabNamingManager 确保对象名称唯一,防止冲突。
  • 缓存机制: SceneCacheManager 缓存场景数据,减少运行时开销。


此方悬停
相册 小说 Ai