Storyline+Dialog初步
This commit is contained in:
37
Assets/Scripts/NewStorySystem/Save/StorySave.cs
Normal file
37
Assets/Scripts/NewStorySystem/Save/StorySave.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
/// <summary>
|
||||
/// 单章节的存档容器。
|
||||
/// <para>故事树的布局与连接完全由 <see cref="StoryData"/> 静态定义,故存档不再保存
|
||||
/// block 位置与连接线;进入章节时按"已完成集合 + 解锁条件"实时推导每个 block 的
|
||||
/// <see cref="StoryBlockState"/>(Completed / Current / Locked)。</para>
|
||||
/// <para>以单个 ES3 key 存储,按章节分文件,一次读写即可加载/保存整章进度。</para>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ChapterStorySave
|
||||
{
|
||||
public string chapterIndex;
|
||||
|
||||
// 已完成 block 的 id 列表(顺序无关,去重由 StoryTreeController 保证)
|
||||
public List<string> completedBlockIds = new();
|
||||
|
||||
// 章节内的对话选项记录(choiceKey -> 选中项索引)。选项不跨章节引用。
|
||||
public Dictionary<string, int> selectedChoices = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局剧情变量存档。float / string / bool 三类型与 Yarn Spinner 的
|
||||
/// <c>VariableStorageBehaviour.GetAllVariables/SetAllVariables</c> 契约一致,
|
||||
/// 供阶段 2 的 StoryVariableStorage 直接读写。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class StoryVariablesSave
|
||||
{
|
||||
public Dictionary<string, float> floatVariables = new();
|
||||
public Dictionary<string, string> stringVariables = new();
|
||||
public Dictionary<string, bool> boolVariables = new();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/NewStorySystem/Save/StorySave.cs.meta
Normal file
2
Assets/Scripts/NewStorySystem/Save/StorySave.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca9f4aa0dff5c774baf0eaab0d404115
|
||||
149
Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs
Normal file
149
Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Menu;
|
||||
using Ichni.Story;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧情存档模块。
|
||||
/// <para>故事树状态与对话选项 <b>按章节</b> 保存(单文件、单 ES3 key)。</para>
|
||||
/// <para>剧情变量 <b>全局</b> 保存,可跨章节引用,契合 Yarn 变量模型。</para>
|
||||
/// </summary>
|
||||
public class StorySaveModule
|
||||
{
|
||||
private const string ChapterStoryKey = "ChapterStory";
|
||||
private const string VariablesKey = "StoryVariables";
|
||||
|
||||
// 已加载章节的内存缓存(chapterIndex -> 章节存档)
|
||||
private readonly Dictionary<string, ChapterStorySave> _chapterSaves =
|
||||
new Dictionary<string, ChapterStorySave>();
|
||||
|
||||
// 全局剧情变量(float / string / bool 三类型,匹配 Yarn 契约)
|
||||
public StoryVariablesSave variables = new StoryVariablesSave();
|
||||
|
||||
private static string GetChapterSavePath(string chapterIndex) =>
|
||||
Application.persistentDataPath + "/StorySaves/" + chapterIndex + ".json";
|
||||
|
||||
private static string VariablesPath =>
|
||||
Application.persistentDataPath + "/StorySaves/StoryVariables.json";
|
||||
|
||||
// ── 故事树 + 选项(按章节)────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 判断某章节是否尚无存档(首次进入)。
|
||||
/// </summary>
|
||||
public bool IsNewChapter(string chapterIndex) =>
|
||||
!ES3.FileExists(GetChapterSavePath(chapterIndex));
|
||||
|
||||
/// <summary>
|
||||
/// 加载指定章节的存档容器;无存档时返回一个新的空容器。加载结果会写入内存缓存。
|
||||
/// </summary>
|
||||
public ChapterStorySave LoadChapter(string chapterIndex)
|
||||
{
|
||||
string path = GetChapterSavePath(chapterIndex);
|
||||
|
||||
ChapterStorySave save;
|
||||
if (ES3.FileExists(path) && ES3.KeyExists(ChapterStoryKey, path))
|
||||
{
|
||||
save = ES3.Load<ChapterStorySave>(ChapterStoryKey, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
save = new ChapterStorySave { chapterIndex = chapterIndex };
|
||||
}
|
||||
|
||||
_chapterSaves[chapterIndex] = save;
|
||||
return save;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存整章存档(blocks + connectors + choices),单次 ES3 写入。
|
||||
/// </summary>
|
||||
public void SaveChapter(ChapterStorySave save)
|
||||
{
|
||||
_chapterSaves[save.chapterIndex] = save;
|
||||
ES3.Save(ChapterStoryKey, save, GetChapterSavePath(save.chapterIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取内存中的章节存档;若未加载则从磁盘加载。
|
||||
/// </summary>
|
||||
public ChapterStorySave GetChapter(string chapterIndex)
|
||||
{
|
||||
return _chapterSaves.TryGetValue(chapterIndex, out ChapterStorySave save)
|
||||
? save
|
||||
: LoadChapter(chapterIndex);
|
||||
}
|
||||
|
||||
// ── 对话选项(按章节)──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 记录某章节内一次对话选项的选择结果。
|
||||
/// </summary>
|
||||
public void SetChoice(string chapterIndex, string choiceKey, int optionIndex)
|
||||
{
|
||||
GetChapter(chapterIndex).selectedChoices[choiceKey] = optionIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取某章节内已记录的对话选项选择结果。
|
||||
/// </summary>
|
||||
public bool TryGetChoice(string chapterIndex, string choiceKey, out int optionIndex)
|
||||
{
|
||||
return GetChapter(chapterIndex).selectedChoices.TryGetValue(choiceKey, out optionIndex);
|
||||
}
|
||||
|
||||
// ── 剧情变量(全局)──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 从磁盘加载全局剧情变量(启动时调用)。
|
||||
/// </summary>
|
||||
public void LoadVariables()
|
||||
{
|
||||
if (ES3.FileExists(VariablesPath) && ES3.KeyExists(VariablesKey, VariablesPath))
|
||||
{
|
||||
variables = ES3.Load<StoryVariablesSave>(VariablesKey, VariablesPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
variables = new StoryVariablesSave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将全局剧情变量写入磁盘。
|
||||
/// </summary>
|
||||
public void SaveVariables()
|
||||
{
|
||||
ES3.Save(VariablesKey, variables, VariablesPath);
|
||||
}
|
||||
|
||||
// ── 清档 ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有章节的故事树存档与全局剧情变量。
|
||||
/// </summary>
|
||||
public void ClearAllStoryline()
|
||||
{
|
||||
if (ChapterSelectionManager.instance != null)
|
||||
{
|
||||
foreach (ChapterSelectionUnit chapter in ChapterSelectionManager.instance.chapters)
|
||||
{
|
||||
string path = GetChapterSavePath(chapter.chapterIndex);
|
||||
if (ES3.FileExists(path))
|
||||
ES3.DeleteFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
_chapterSaves.Clear();
|
||||
|
||||
if (ES3.FileExists(VariablesPath))
|
||||
ES3.DeleteFile(VariablesPath);
|
||||
|
||||
variables = new StoryVariablesSave();
|
||||
|
||||
Debug.Log("Cleared all story saves (chapters + global variables).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e133cbb328fd394f820d7bc4136ffd7
|
||||
Reference in New Issue
Block a user