Unity视觉小说存档脚本更新无效问题求助及代码咨询
视觉小说存档数据异常与脚本热更新实现问题
问题背景
开发视觉小说时遇到存档数据异常:存档后修改脚本逻辑,加载旧存档时旧逻辑依然生效。比如存档时角色向左移动,修改脚本为向右移动后加载存档,角色仍停在左侧。
计划用FileSystemWatcher监听脚本文件变更,手动重新加载存档关联的脚本,但不确定具体实现细节。另外在菜单界面修改脚本时会触发游戏引擎管理器空引用异常,考虑用Coroutine等待管理器实例初始化完成后再执行操作,附上当前实现代码,寻求技术优化建议。
using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using VISUALNOVEL; public class ScriptUpdater : MonoBehaviour { [SerializeField] private TextAsset file; private const string ASSET_FORMAT = "txt"; private string filePath => $"{Application.dataPath}/{FilePaths.main_path}{FilePaths.dialogue_path}{file.name}.{ASSET_FORMAT}"; private FileSystemWatcher watcher; private bool is_updating => co_updating != null; private Coroutine co_updating = null; // Start is called before the first frame update void Start() { watcher = new FileSystemWatcher(); watcher.Path = Path.GetDirectoryName(filePath); watcher.Filter = Path.GetFileName(filePath); watcher.Changed += OnFileChanged; watcher.EnableRaisingEvents = true; } private void OnFileChanged(object sender, FileSystemEventArgs e) { ScriptUpdate(); Debug.Log("changed!"); } private void ScriptUpdate() { if (is_updating) { StopCoroutine(co_updating); } co_updating = StartCoroutine(Updating()); } private IEnumerator Updating() { Debug.Log("Started Updating Coroutine"); while (DIALOGUE.DialogueSystem.instance == null) { Debug.Log("Waiting for DialogueSystem instance..."); yield return null; } Debug.Log("DialogueSystem instance found"); if (DIALOGUE.DialogueSystem.instance != null) { Debug.Log("Setting Conversation Data"); VNGameSave.activeFile.SetConversationData(); } Debug.Log("Finished Updating Coroutine"); co_updating = null; yield return null; } }
问题分析与优化方案
1. Coroutine等待实例的思路是可行的
你用Coroutine循环等待DialogueSystem.instance初始化的核心逻辑没问题,但可以补充超时机制避免无限等待:
private IEnumerator Updating() { Debug.Log("Started Updating Coroutine"); float waitTimeout = 10f; float elapsedTime = 0f; while (DIALOGUE.DialogueSystem.instance == null) { elapsedTime += Time.deltaTime; if (elapsedTime >= waitTimeout) { Debug.LogError("等待DialogueSystem实例超时,终止更新"); co_updating = null; yield break; } Debug.Log("Waiting for DialogueSystem instance..."); yield return null; } // 后续逻辑... }
2. 修复FileSystemWatcher重复触发问题
Changed事件会因文件保存时的临时写入触发多次,需要加防抖处理:
private float debounceDelay = 0.5f; private float lastTriggerTime; private void OnFileChanged(object sender, FileSystemEventArgs e) { lastTriggerTime = Time.time; StartCoroutine(DebounceUpdate()); Debug.Log("文件已变更"); } private IEnumerator DebounceUpdate() { yield return new WaitForSeconds(debounceDelay); if (Time.time - lastTriggerTime >= debounceDelay) { ScriptUpdate(); } }
3. 完善存档脚本的重新加载逻辑
当前代码仅调用SetConversationData(),可能未覆盖角色移动这类逻辑的更新,需要补充脚本重解析与存档数据同步:
private IEnumerator Updating() { // 等待实例逻辑... Debug.Log("DialogueSystem instance found"); // 重新读取并解析脚本文件 if (File.Exists(filePath)) { string newScriptContent = File.ReadAllText(filePath); // 调用对话系统的脚本解析方法(需根据你的引擎实现调整) DIALOGUE.DialogueSystem.instance.ParseScript(newScriptContent); // 更新存档中的脚本数据 VNGameSave.activeFile.UpdateScriptContent(newScriptContent); // 如果当前存档处于加载状态,立即应用新逻辑 if (VNGameSave.activeFile.IsLoaded) { DIALOGUE.DialogueSystem.instance.ReApplySaveLogic(); } } else { Debug.LogError($"未找到脚本文件:{filePath}"); } Debug.Log("更新协程执行完成"); co_updating = null; yield return null; }
4. 菜单场景的待更新标记处理
在菜单界面时DialogueSystem未初始化,可添加全局标记延迟执行更新:
private bool isPendingUpdate = false; private IEnumerator DebounceUpdate() { yield return new WaitForSeconds(debounceDelay); if (Time.time - lastTriggerTime >= debounceDelay) { if (DIALOGUE.DialogueSystem.instance == null) { isPendingUpdate = true; Debug.Log("脚本更新已挂起,将在DialogueSystem就绪后执行"); } else { ScriptUpdate(); } } } void Update() { if (isPendingUpdate && DIALOGUE.DialogueSystem.instance != null) { isPendingUpdate = false; ScriptUpdate(); } }
内容的提问来源于stack exchange,提问作者mary jane
相关产品推荐
相关产品推荐

