E Story 故事编辑器开发笔记 #3 创建基础节点

创建基础节点

了解节点基本结构

  • 主容器(mainContainer)
    • 标题容器(titleContainer)
      • 标题按钮容器(titleButtonContainer)
    • 顶部容器(topContainer)
      • 输入容器(inputContainer)
      • 输出容器(outputContainer)
    • 扩展容器(extensionContainer)

创建基础节点类

  1. 创建文件夹:Assets/E Tool/E Story/Editor/Scripts/UI Node
  2. 打卡UI Node文件夹,创建C#文件,命名为BaseNode
  3. 打开BaseNode.cs,将内容修改如下:
C#
using UnityEditor.Experimental.GraphView;
using UnityEngine;

namespace E.Story
{
    // 基类节点
    public class BaseNode : Node
    {
        // UI元素
        protected StoryGraphView graphView;

        // 初始化
        public void Init(StoryGraphView graphView, string title, Vector2 position)
        {
            this.graphView = graphView;
            this.title = title;
            SetPosition(new Rect(position, Vector2.zero));
        }
        
        // 绘制视图
        public virtual void Draw()
        {
            DrawMainContainer();
            DrawTitleContainer();
            DrawTitleButtonContainer();
            DrawTopContainer();
            DrawInputContainer();
            DrawOutputContainer();
            DrawExtensionContainer();
        }

        // 绘制主容器
        protected virtual void DrawMainContainer()
        {
        }

        // 绘制标题容器
        protected virtual void DrawTitleContainer()
        {
        }

        // 绘制标题按钮容器
        protected virtual void DrawTitleButtonContainer()
        {
        }

        // 绘制顶部容器
        protected virtual void DrawTopContainer()
        {
        }

        // 绘制输入容器
        protected virtual void DrawInputContainer() 
        {
        }

        // 绘制输出容器
        protected virtual void DrawOutputContainer()
        { 
        }

        // 绘制扩展容器
        protected virtual void DrawExtensionContainer() 
        {
        }
    }
}

向视图添加节点

自动添加默认节点

  1. 打开StoryGraphView.cs,新增CreateNodeAddDefaultNodes方法
C#
// 添加默认节点
private void AddDefaultNodes()
{
    AddElement(CreateNode("节点", new Vector2(200, 200)));
}

// 创建节点
public BaseNode CreateNode(string title, Vector2 position)
{
    // 创建对应节点
    BaseNode node = new();
    node.Init(this, title, position);
    node.Draw();

    return node;
}
  1. 调用AddDefaultNodes方法
C#
public StoryGraphView(StoryEditorWindow window)
{
    /* ... 此处代码已省略 ... */

    // 调用
    AddDefaultNodes();
}

手动添加节点

  1. 打开StoryGraphView.cs,重写BuildContextualMenu方法
C#
// 构建上下文菜单
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
{
    base.BuildContextualMenu(evt);

    // 点击按钮实现添加新节点
    evt.menu.AppendAction("添加节点", action =>
    {
        AddElement(CreateNode("节点", Vector2.zero));
    });
}
  1. 在视图空白处点击鼠标右键即可看见添加节点按钮

测试效果

打开插件窗口,当前效果如下:

自定义基础节点

创建节点类型枚举

  1. 依次创建以下文件夹:
  • Assets/E Tool/E Story/Runtime
  • Assets/E Tool/E Story/Runtime/Scripts
  • Assets/E Tool/E Story/Runtime/Scripts/Data
  1. 打开Data文件夹,创建C#文件,命名为NodeType
  2. 打开NodeType.cs,将内容修改如下:
C#
namespace E.Story
{
    // 节点类型
    public enum NodeType
    {
        // 基础
        Base = 0,
        // 零进零出
        ZeroInZeroOut = 1,
        // 零进单出
        ZeroInSingleOut = 2,
        // 零进多出
        ZeroInMultiOut = 3,
        // 单进零出
        SingleInZeroOut = 4,
        // 单进单出
        SingleInSingleOut = 5,
        // 单进多出
        SingleInMultiOut = 6,
        // 多进零出
        MultiInZeroOut = 7,
        // 多进单出
        MultiInSingleOut = 8,
        // 多进多出
        MultiInMultiOut = 9,
    }
}

新增实用方法

打开ElementUtility.cs,新增CreatePort方法

C#
// 创建端口
public static Port CreatePort(this BaseNode node, string portName = "", Orientation orientation = Orientation.Horizontal, Direction direction = Direction.Output, Port.Capacity capacity = Port.Capacity.Single)
{
    Port port = node.InstantiatePort(orientation, direction, capacity, typeof(bool));
    port.portName = portName;
    return port;
}

修改基础节点类

  1. 打开BaseNode.cs,新增以下字段和属性:
C#
// UI元素
protected VisualElement customDataContainer;
protected Foldout foldout;
protected Port input;
protected Port output;

// 节点GUID
public string GUID { get; set; }

// 节点类型
public NodeType Type { get; set; }

// 节点标题
public string Title { get; set; }

// 节点备注
public string Note { get; set; }
  1. 修改以下方法:
C#
public virtual void Init(StoryGraphView graphView, string title, Vector2 position)
{
    // 设置字段
    this.graphView = graphView;
    SetPosition(new Rect(position, Vector2.zero));

    // 设置属性默认值
    Type = NodeType.Base;
    GUID = UnityEditor.GUID.Generate().ToString();
    Title = title;
    Note = "备注信息";
}

public virtual void DrawTitleContainer()
{
    // 创建标题输入框
    TextField tfdTitle = ElementUtility.CreateTextField(Title, null, callback =>
    {
        // 更新标题
        Title = callback.newValue;
    });

    // 将标题输入框放在最左侧
    titleContainer.Insert(0, tfdTitle);
}

public override void DrawInputContainer()
{
    // 创建输入端口
    input = this.CreatePort("上个节点", Orientation.Horizontal, Direction.Input, Port.Capacity.Multi);
    inputContainer.Add(input);
}

public virtual void DrawOutputContainer()
{
    // 创建输出端口
    output = this.CreatePort("下个节点");
    outputContainer.Add(output);
}

public virtual void DrawExtensionContainer()
{
    // 创建自定义容器
    customDataContainer = new();

    // 创建折叠组
    foldout = ElementUtility.CreateFoldout("节点内容");

    // 创建备注输入框
    TextField tfdNote = ElementUtility.CreateTextArea(Note, null, callback =>
    {
        Note = callback.newValue;
    });
    
    // 将目标放到对应容器
    foldout.Add(tfdNote);
    customDataContainer.Add(foldout);
    extensionContainer.Add(customDataContainer);

    // 此方法必须调用
    RefreshExpandedState();
}

测试效果

打开插件窗口,最终效果如下:

相关链接

留下评论