Unity visual node editor (GraphView, editor extension)

foreword

  A few days ago, I packaged and released my mentor’s project and handed in a draft. In the past half a week, apart from sewing and repairing the project (to be honest, it’s a bit boring to do the project later, it’s all trivial things and I can’t learn much. , It’s purely a waste of time) I found an interesting thing when I was bored in the Unity store. To be honest, I thought it was just a 2D animation that was simply drawn:

But after looking at it again, I found that this is actually a picture rendered by a 3D model, and the model is on the right:

  In fact, the cartoon rendering model in the scene is relatively ordinary, but the final rendering effect is quite amazing to me, so the first The first reaction is to see how it is realized and then do it in my future projects (of course, with my level and learning direction, the details may not be understood, but you can take a look at the general idea. If you have the opportunity in the future, you can learn related content). First, I flipped through the Frame Debugger, and I didn't understand it halfway. Then I flipped through and found this thing Visual Compositor, which is also related to the topic of the article: a process configuration node diagram.

  Then I found that it is basically the superposition of various image mixing post-processing effects, including stroke, edge light, image color transition, fill light, blur mixing, etc. If I really want to say it, it is actually not complicated (professional art makes it out ) Good first impression, envy ), but not quite suitable for free-view cartoon rendering. However, this node map is indeed an editor extension that I wanted to make a long time ago to facilitate the configuration of dialogue files in the project (the multiple ScriptableObjects used in the past are connected into a series of dialogues, which is not so convenient to use). A simple search found that this node graph Unity provides ready-made classes for easy expansion, keyword: GraphView , so referring to online blogs and plug-ins from the Unity store, I implemented a simple dialog configuration system in my own project.

Show results

  The implementation mainly includes four parts: the entire node graph (base class GraphView), a single node (base class Node), a window to display the graph (base class EditorWindow), and a file for storing each dialog data (base class ScriptableObject ). (Of course, a dialogue system for reading and displaying dialogue data is also required, but this is another part) The
  final effect is as follows:

According to actual project requirements, various configuration data can be placed in the node. In this project, each node corresponds to a number of sentences spoken by a certain person (not a person), and you can click the button to add or delete the dialogue content, etc.; right-click to call out the menu, and create a new node at the mouse position; right-click Click the node, except the start node, each node can add or delete the option (Output) that may appear after the end of this dialogue; by clicking the upper left corner or Ctrl+S, you can manually save the configuration data to the ScriptableObject.

implementation details

  There are many tutorials and complete codes when you search for GraphView on the Internet. The plug-ins in the Unity store are also good learning materials, so here is a brief introduction to the implementation of some functions in the project.
  1. There are two ways to realize the function of calling out the menu bar by right-clicking, one is to GraphView.RegisterCallback<MouseDownEvent>()create a menu by yourself (because the entire window is filled by GraphView, the same method is used to save the project with Ctrl+S), the other is to create a menu by yourself. One is to add options to the menu Nodein rewriting BuildContextualMenu, the second is actually used in the project, and the creation of nodes, interfaces, etc. are placed in it.

	graphView.RegisterCallback<MouseDownEvent>(MouseDown);
    private void MouseDown(MouseDownEvent _event)
    {
    
    
        if (_event.button == 1)
        {
    
    
            Vector2 _mousePos = _event.mousePosition;
            GenericMenu _menu = new GenericMenu();
            _menu.AddItem(new GUIContent("Create/New Node"), false, () => {
    
     /* do something */ });
            _menu.ShowAsContext();
        }
    }
	// in Node class
	public override void BuildContextualMenu(ContextualMenuPopulateEvent _event)
    {
    
    
        base.BuildContextualMenu(_event);
        _event.menu.AppendAction("Port/Add Port", (_a) => {
    
     /* do something */ });
    }

  2. Various UI elements in the node Container.Addare added through (inputContainer, mainContainer, etc.), the layout can adopt the default layout (if there is no demand), or use uss, uxml to customize the layout, which can be created in Create->UI Toolkit, The project uses uss (in fact, basically css) to define the layout. In uss, there are also VisualElementtypes that can be passed (such as Button, Label, etc.), names (corresponding in the code VisualElement.name, although the official document does not recommend that multiple elements be named the same, but this is still done in the project), classes (if you use uxml If the layout is predefined, it can be set, but it is not clear how to set it in the code) for layout.

	// .cs
	TextField _contentText = new TextField("", 400, true, false, '*');
    _contentText.name = "ContentText";
	/* .uss */
	TextField {
    
    
	    width: 303px;
	}
	
	#ContentText {
    
    
	    width: 280px;
	    height: 55px;
	}

  3. Unity cannot directly serialize this type of data for storage. Here, you can use the data structure that Unity can serialize to store the graph, and then load it from the stored data when you need to edit the graph. The project uses List to record the data of all nodes, and uses the index of the nodes in the List to record the connection between nodes. The part of storing data is mainly by GraphView.nodes.ToList()obtaining all the nodes in the graph, by GraphView.edges.ToList()or ((Port)Node.outputContainer[i]).connectionsobtaining the edges (Edges) connected between nodes, and then by Edge.input.nodeor Edge.output.nodeobtaining the connection relationship between nodes. Remember to use [System.Serializable] to declare the custom storage class as serializable, and use it after storing the data to let EditorUtility.SetDirty(ScriptableObject)Unity know that the data in the file has been changed and needs to be saved.

        List<DialogGraphNode> _dialogNodeList = graphView.nodes.ToList().Cast<DialogGraphNode>().ToList();

        dialogNodeSave = new List<DialogNodeSaveData>();
        foreach (DialogGraphNode _node in _dialogNodeList)
        {
    
    
            DialogNodeSaveData _nodeSaveData = new DialogNodeSaveData(_node.dialogNodeData, _node.title, _node.GetPosition());
            dialogNodeSave.Add(_nodeSaveData);
            if (_node == graphView.startNode) startNode = _nodeSaveData;
        }
		
		// ...
		
		EditorUtility.SetDirty(this);

Guess you like

Origin blog.csdn.net/qq_43459138/article/details/130311085