读取故事数据
打开StoryEditorWindow.cs,新增以下方法:
C#
// 载入数据
private void LoadDatas(StoryDataSO storyData)
{
UpdateFileName(storyData.FileName);
Dictionary<string, BaseGroup> loadedGroups = LoadGroupDatas(storyData.GroupDatas);
Dictionary<string, BaseNode> loadedNodes = LoadNodeDatas(storyData.NodeDatas, loadedGroups);
LoadNodesConnections(loadedNodes);
}
// 载入分组数据
private Dictionary<string, BaseGroup> LoadGroupDatas(List<GroupData> groupDatas)
{
Dictionary<string, BaseGroup> loadedGroups = new();
// 遍历分组视图列表
foreach (GroupData groupGraph in groupDatas)
{
BaseGroup group = graphView.CreateGroup(groupGraph.Title, groupGraph.Position);
group.ID = groupGraph.GUID;
loadedGroups.Add(group.ID, group);
}
return loadedGroups;
}
// 载入节点数据
private Dictionary<string, BaseNode> LoadNodeDatas(List<NodeData> nodeDatas, Dictionary<string, BaseGroup> loadedGroups)
{
Dictionary<string, BaseNode> loadedNodes = new();
// 遍历节点视图列表
foreach (NodeData nodeData in nodeDatas)
{
// 创建节点
BaseNode node = graphView.CreateNode(nodeData.Title, nodeData.Type, nodeData.Position, null, false);
node.GUID = nodeData.GUID;
node.Note = nodeData.Note;
node.ChoiceDatas = DataUtility.CloneChoiceDatas(nodeData.ChoiceDatas);
// 检测节点类型并获取特定变量值
if (node.Type == NodeType.Dialogue)
{
DialogueNode dNode = node as DialogueNode;
dNode.RoleName = nodeData.RoleName;
dNode.SentenceDatas = DataUtility.CloneSentenceDatas(nodeData.SentenceDatas);
}
// 绘制节点
node.Draw();
// 获取所属分组分组
BaseGroup group = null;
if (!string.IsNullOrEmpty(nodeData.GroupID))
{
group = loadedGroups[nodeData.GroupID];
group.AddElement(node);
}
// 记录该节点已被加载
loadedNodes.Add(node.GUID, node);
}
return loadedNodes;
}
// 载入节点连线
private void LoadNodesConnections(Dictionary<string, BaseNode> loadedNodes)
{
// 遍历所有已载入的节点
foreach (KeyValuePair<string, BaseNode> loadedNode in loadedNodes)
{
// 遍历节点的所有输出端口
foreach (Port outputPort in loadedNode.Value.outputContainer.Children())
{
ChoiceData choiceData = (ChoiceData)outputPort.userData;
if (string.IsNullOrEmpty(choiceData.NextNodeID))
{
continue;
}
// 获取下个节点的输入端口
Port nextNodeInputPort = loadedNodes[choiceData.NextNodeID].Input;
// 创建连线
graphView.CreateEdge(outputPort, nextNodeInputPort);
}
// 刷新端口样式
loadedNode.Value.RefreshPorts();
}
}
// 更新文件名
private void UpdateFileName(string newFileName)
{
tfdFileName.value = newFileName;
}打开故事文件
通过文件选择框打开
打开StoryEditorWindow.cs,新增以下方法:
C#
// 打开故事
private void OpenStory()
{
// 获取文件路径
string filePath = EditorUtility.OpenFilePanel("打开故事", storyDatasFolderPath, "asset");
// 检测是否为空
if (string.IsNullOrEmpty(filePath))
{
return;
}
// 绝对路径转为相对路径
filePath = FileUtil.GetProjectRelativePath(filePath);
// 载入文件
StoryDataSO story = IOUtility.LoadAsset<StoryDataSO>(filePath);
// 检测是否为空
if (story == null)
{
string temp = "故事不存在:\n\n" +
$"{filePath}\n\n" +
"请确保你选择了正确的文件。";
EditorUtility.DisplayDialog("警告", temp, "明白");
return;
}
string str = "确认打开新故事并覆盖当前视图内容吗?未保存的数据将无法恢复。";
if (EditorUtility.DisplayDialog("警告", str, "确认", "取消"))
{
storyData = story;
graphView.ClearGraph();
LoadDatas(storyData);
// 提示消息
string message = $"故事已打开";
ShowNotification(new GUIContent(message));
}
}打开StoryGraphView.cs,新增ClearGraph方法:
C#
// 清除视图
public void ClearGraph()
{
graphElements.ForEach(e => RemoveElement(e));
}打开BaseNode.cs,新增以下属性:
C#
public Port Input { get => input; }
public Port Output { get => output; }修改CreateNode方法:
C#
// 创建节点
public BaseNode CreateNode(string title, NodeType type, Vector2 position, Group group = null, bool shouldDraw = true)
{
// 获取类型
Type nodeType = Type.GetType($"E.Story.{type}Node");
// 创建对应节点
BaseNode node = Activator.CreateInstance(nodeType) as BaseNode;
node.Init(this, title, position);
// 是否绘制
if (shouldDraw)
{
node.Draw();
}
if (group == null)
{
// 加入视图
AddElement(node);
}
else
{
// 加入分组
group.AddElement(node);
}
return node;
}打开StoryEditorWindow.cs,修改AddToolbar方法,实现调用:
C#
// 添加工具栏
private void AddToolbar()
{
/* ... 此处代码已省略 ... */
btnOpen = ElementUtility.CreateButton("打开", () => OpenStory());
/* ... 此处代码已省略 ... */
}双击故事文件直接打开
打开StoryEditorWindow.cs,新增OnDoubleClick方法:
C#
// 当双击资产文件时,打开故事编辑器读取故事数据
[OnOpenAsset()]
public static bool OnDoubleClick(int instanceID)
{
// 获取窗口实例并打开
StoryEditorWindow wnd = (StoryEditorWindow)GetWindow(typeof(StoryEditorWindow));
if (wnd == null)
{
Open();
}
wnd.RemoveNotification();
// 获取资产路径
string fullPath = AssetDatabase.GetAssetPath(instanceID);
// 检测是否目标资产类型
StoryDataSO storyData = IOUtility.LoadAsset<StoryDataSO>(fullPath);
if (storyData == null)
{
return false;
}
string str = "确认打开新故事并覆盖当前视图内容吗?未保存的数据将无法恢复。";
if (EditorUtility.DisplayDialog("警告", str, "确认", "取消"))
{
wnd.storyData = storyData;
wnd.graphView.ClearGraph();
wnd.LoadDatas(storyData);
// 提示消息
string message = $"故事已打开";
wnd.ShowNotification(new GUIContent(message));
}
return true;
}启动故事编辑器时自动打开
打开StoryEditorWindow.cs,新增以下方法:
C#
// 打开上次编辑的故事
private void OpenLastStory()
{
// 获取文件路径
string storyName = EditorPrefs.GetString(keyLastStoryName);
// 检测是否为空
if (string.IsNullOrEmpty(storyName))
{
return;
}
// 载入文件
StoryDataSO story = IOUtility.LoadAsset<StoryDataSO>(storyDatasFolderPath, storyName);
// 检测是否为空
string message;
if (story == null)
{
message = $"未找到上次编辑的故事";
ShowNotification(new GUIContent(message));
return;
}
storyData = story;
graphView.ClearGraph();
LoadDatas(storyData);
// 提示消息
message = $"已打开上次编辑的故事";
ShowNotification(new GUIContent(message));
}
// 记录当前的故事
private void RecordCurrentStory()
{
EditorPrefs.SetString(keyLastStoryName, storyData.FileName);
}修改以下方法,实现调用:
C#
// 打开故事
private void OpenStory()
{
/* ... 此处代码已省略 ... */
string str = "确认打开新故事并覆盖当前视图内容吗?未保存的数据将无法恢复。";
if (EditorUtility.DisplayDialog("警告", str, "确认", "取消"))
{
storyData = story;
RecordCurrentStory(); /* 在此处新增调用 */
/* ... 此处代码已省略 ... */
}
}
// 当双击资产文件时,打开故事编辑器读取故事数据
[OnOpenAsset()]
public static bool OnDoubleClick(int instanceID)
{
/* ... 此处代码已省略 ... */
string str = "确认打开新故事并覆盖当前视图内容吗?未保存的数据将无法恢复。";
if (EditorUtility.DisplayDialog("警告", str, "确认", "取消"))
{
wnd.storyData = storyData;
wnd.RecordCurrentStory(); /* 在此处新增调用 */
/* ... 此处代码已省略 ... */
}
return true;
}
private void CreateGUI()
{
/* ... 此处代码已省略 ... */
// 打开上个故事
OpenLastStory();
}测试效果
最终窗口效果如下:

相关链接
- 完整代码:https://gitee.com/helloestar/e-story/wikis/DevelopNotes/11
- 视频版本:制作中……


