E Story 故事编辑器开发笔记 #12 更新故事数据

添加或删除UI元素时更新数据

打开StoryGraphView.cs,新增以下方法并完成调用:

C#
// 当视图发生变化
private void OnGraphViewChanged()
{
    // 定义事件
    graphViewChanged = (changes) =>
    {
        // 当即将创建连线时
        if (changes.edgesToCreate != null)
        {
            // 遍历所有创建的连线
            foreach (Edge edge in changes.edgesToCreate)
            {
                OnCreateEdge(edge);
            }
        }

        // 当即将删除元素时
        if (changes.elementsToRemove != null)
        {
            // 遍历所有元素,执行对应事件
            foreach (GraphElement element in changes.elementsToRemove)
            {
                // 如果是节点
                if (element is BaseNode node)
                {
                    OnDeleteNode(node);
                }
                // 如果是分组
                else if (element is BaseGroup group)
                {
                    OnDeleteGroup(group);
                }
                // 如果是连线
                else if (element is Edge edge)
                {
                    OnDeleteEdge(edge);
                }
            }
        }

        // 当有元素移动后
        if (changes.movedElements != null)
        {
            //Debug.Log(changes.moveDelta);
        }

        return changes;
    };
}

// 即将创建连线时
private void OnCreateEdge(Edge edge)
{
    // 获取连线输入端的节点(下一个节点)
    BaseNode nextNode = (BaseNode)edge.input.node;
    // 获取连线输出端的节点(上一个节点)引用的选项数据
    ChoiceData choiceData = (ChoiceData)edge.output.userData;
    // 记录下一个节点的GUID
    choiceData.NextNodeID = nextNode.GUID;
}

// 即将删除节点时
private void OnDeleteNode(BaseNode node)
{
    //Debug.Log("正在删除节点");
}

// 即将删除分组时
private void OnDeleteGroup(BaseGroup group)
{
    //Debug.Log("正在删除分组");
}

// 即将删除连线时
private void OnDeleteEdge(Edge edge)
{
    //Debug.Log("正在删除连线");

    // 当连线的输出端口为空时,跳过
    if (edge.output == null) return;

    // 获取连线输出端的节点(上一个节点)引用的选项数据
    ChoiceData choiceData = (ChoiceData)edge.output.userData;
    // 清空记录
    choiceData.NextNodeID = "";
}

// 创建连线
public Edge CreateEdge(Port lastOutput, Port nextInput)
{
    Edge edge = lastOutput.ConnectTo(nextInput);
    AddElement(edge);
    OnCreateEdge(edge);  /* 在此处新增调用 */
    return edge;
}

// 构造器
public StoryGraphView(StoryEditorWindow window)
{
    /* ... 此处代码已省略 ... */

    OnGraphViewChanged();  /* 在此处新增调用 */
}

避免删除开始节点和结束节点

继续新增以下方法并完成调用:

C#
// 当元素准备删除时
private void OnElementsReadyDelete()
{
    // 在此处对选中的元素进行过滤
    deleteSelection = (operationName, askUser) =>
    {
        List<ISelectable> readyToDelete = new();

        // 遍历当前选中目标
        foreach (GraphElement element in selection)
        {
            // 如果是节点
            if (element is BaseNode node)
            {
                // 过滤掉开始节点
                if (node is StartNode)
                {
                    string str = "不可以删除开始节点。";
                    EditorUtility.DisplayDialog("警告", str, "明白");
                    continue;
                }

                // 过滤掉最后一个结束节点
                if (node is EndNode)
                {
                    if (GetEndNodesAmount() == 1)
                    {
                        string str = "不可以删除最后一个结束节点。";
                        EditorUtility.DisplayDialog("警告", str, "明白");
                        continue;
                    }
                }

                // 加入待删除列表
                readyToDelete.Add(node);
            }
            // 如果是分组
            else if (element is BaseGroup group)
            {
                readyToDelete.Add(group);
            }
            // 如果是连线
            else if (element is Edge edge)
            {
                readyToDelete.Add(edge);
            }
        }

        // 更新选中目标
        selection = readyToDelete;
        // 执行删除
        DeleteSelection();
    };
}

// 获取结束节点数量
private int GetEndNodesAmount()
{
    return graphElements.Where(e => e is EndNode).ToList().Count;
}

// 构造器
public StoryGraphView(StoryEditorWindow window)
{
    /* ... 此处代码已省略 ... */

    OnElementsReadyDelete();  /* 在此处新增调用 */
}

修改OnGraphViewChanged方法:

C#
// 当视图发生变化
private void OnGraphViewChanged()
{
    // 定义事件
    graphViewChanged = (changes) =>
    {
        /* ... 此处代码已省略 ... */

        // 当即将删除元素时
        if (changes.elementsToRemove != null)
        {
            /* 避免当开始节点和结束节点位于分组里时删除分组被一并删除 */
            // 获取开始节点
            GraphElement startNodes = changes.elementsToRemove.FirstOrDefault(n => n is StartNode);
            // 将其移出删除列表
            changes.elementsToRemove.Remove(startNodes);
            // 获取结束节点列表
            List<GraphElement> endNodes = changes.elementsToRemove.Where(n => n is EndNode).ToList();
            // 如果与视图中所有结束节点的总数相同
            if (endNodes.Count == GetEndNodesAmount())
            {
                // 获取最后一个结束节点
                GraphElement lastEndNode = endNodes[^1];
                // 将其移出删除列表
                changes.elementsToRemove.Remove(lastEndNode);
            }

            /* ... 此处代码已省略 ... */
        }

        /* ... 此处代码已省略 ... */

        return changes;
    };
}

将节点移入或移出分组时更新数据

继续新增以下方法并完成调用:

C#
// 当往分组里加入节点时
private void OnGroupElementsAdded()
{
    elementsAddedToGroup = (group, elements) =>
    {
        BaseGroup baseGroup = (BaseGroup)group;
        foreach (GraphElement element in elements)
        {
            // 如果添加的是节点
            if (element is BaseNode node)
            {
                // 记录节点所属分组
                node.Group = baseGroup;
            }
        }
    };
}

// 当从分组里移出节点时
private void OnGroupElementsRemoved()
{
    elementsRemovedFromGroup = (group, elements) =>
    {
        BaseGroup baseGroup = (BaseGroup)group;
        foreach (GraphElement element in elements)
        {
            // 如果添加的是节点
            if (element is BaseNode node)
            {
                // 清除分组信息
                node.Group = null;
            }
        }
    };
}

// 构造器
public StoryGraphView(StoryEditorWindow window)
{
    /* ... 此处代码已省略 ... */

    OnGroupElementsAdded();  /* 在此处新增调用 */
    OnGroupElementsRemoved();  /* 在此处新增调用 */
}

过滤重命名分组标题时的字符

打开StoryGraphView.cs,新增OnGroupRenamed方法并完成调用:

C#
// 当重命名分组时
private void OnGroupRenamed()
{
    groupTitleChanged = (group, newTitle) =>
    {
        BaseGroup baseGroup = (BaseGroup)group;

        // 清除无效字符
        string temp = newTitle;
        temp = temp.RemoveWhitespaces();
        temp = temp.RemoveSpecialCharacters();
        baseGroup.title = temp;
    };
}

// 构造器
public StoryGraphView(StoryEditorWindow window)
{
    /* ... 此处代码已省略 ... */

    OnGroupRenamed();  /* 在此处新增调用 */
}

测试效果

最终窗口效果如下:

相关链接

留下评论