精华内容
下载资源
问答
  • 孙广东 2018.5.13第二部分 在Unity中序列化基于节点的编辑器重温基于节点的编辑器 上一篇文章 《在Unity中创建基于Node节点的编辑器 (一)》重温XML序列化 有许多序列化选项,例如JSON或Unity自己的序列化系统。...

    孙广东  2018.5.13

    csdn 的产品 , 真垃圾, 不想吐槽了, 文章保存就丢!     没办法  。    怎么不满意, 还是得继续用, 哎~~~

    第二部分 在Unity中序列化基于节点的编辑器

    重温基于节点的编辑器

    重温XML序列化
    有许多序列化选项,例如JSON或Unity自己的序列化系统。 我们以前已经介绍过XML序列化 我们将使用XMLOp 类和本文中的一些XML属性。

    添加一个菜单栏(添加保存, 加载等按钮)
    我们将从添加菜单栏开始,然后从控制台窗口clone中复制它(一起弄的一个 仿Unity Console窗口的脚本):
        private float menuBarHeight = 20f;
        private Rect menuBar;

        private void OnGUI()
        {
            DrawGrid(20, 0.2f, Color.gray);
            DrawGrid(100, 0.4f, Color.gray);
            DrawMenuBar();
     
            DrawNodes();
            DrawConnections();
     
            DrawConnectionLine(Event.current);
     
            ProcessNodeEvents(Event.current);
            ProcessEvents(Event.current);
     
            if (GUI.changed) Repaint();
        }
     
        private void DrawMenuBar()
        {
            menuBar = new Rect(0, 0, position.width, menuBarHeight);
     
            GUILayout.BeginArea(menuBar, EditorStyles.toolbar);
            GUILayout.BeginHorizontal();
     
            GUILayout.Button(new GUIContent("Save"), EditorStyles.toolbarButton, GUILayout.Width(35));
            GUILayout.Space(5);
            GUILayout.Button(new GUIContent("Load"), EditorStyles.toolbarButton, GUILayout.Width(35));
     
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }
    在第56行,我们调用DrawMenuBar()方法,并在第69-82行之间创建菜单栏。控制台窗口克隆有一个button和6个toggles,但由于我们只是序列化和反序列化,所以我们不需要两个以上的按钮。请记住,编辑器GUI系统具有绘制顺序,并按照您调用它们的顺序从后向前绘制元素。这就是我们绘制网格后绘制菜单栏的原因。否则,网格将被拖到菜单栏上。

    DrawMenuBar() 在 DrawGrid() 之前被调用,因此在菜单栏上有一个丑陋的网格。

    目前,保存和加载按钮什么都不做,但我们会做到。

    序列化
    接下来,我们需要准备我们的类(节点Node和连接Connection)进行序列化。让我们记住关于XML序列化的两个重要关键点:

    1. XML序列化程序只能序列化public 字段。
    2. 要序列化的类应该有一个无参数的构造函数。
    规则1号不会导致很多问题(它仍然会导致一些问题,但我们会做到这一点),但规则编号2是有问题的。我们的两个类都有带参数的构造函数。首先解决这个问题:
        public Node() { }
        
        public Node(Vector2 position, float width, float height, GUIStyle nodeStyle, GUIStyle selectedStyle, GUIStyle inPointStyle, GUIStyle outPointStyle, Action<ConnectionPoint> OnClickInPoint, Action<ConnectionPoint> OnClickOutPoint, Action<Node> OnClickRemoveNode)
        {
            rect = new Rect(position.x, position.y, width, height);
            style = nodeStyle;
            inPoint = new ConnectionPoint(this, ConnectionPointType.In, inPointStyle, OnClickInPoint);
            outPoint = new ConnectionPoint(this, ConnectionPointType.Out, outPointStyle, OnClickOutPoint);
            defaultNodeStyle = nodeStyle;
            selectedNodeStyle = selectedStyle;
            OnRemoveNode = OnClickRemoveNode;
        }

        public Connection() { }
        
        public Connection(ConnectionPoint inPoint, ConnectionPoint outPoint, Action<Connection> OnClickRemoveConnection)
        {
            this.inPoint = inPoint;
            this.outPoint = outPoint;
            this.OnClickRemoveConnection = OnClickRemoveConnection;
        }

    接下来,我们将忽略无法序列化或不需要序列化的属性。 例如,在Node 类中,GUIStyle可以不用序列化,因为它们已经由编辑器自己提供。 我们不需要isDraggedisSelected。 实际上,Node类只有一个需要序列化的属性:rect。 让我们来看看如何正确地忽略不必要和不可序列化的属性后Node类的样子:
    public class Node
    {
    public Rect rect;
     
    [XmlIgnore] public string title;
    [XmlIgnore] public bool isDragged;
    [XmlIgnore] public bool isSelected;
     
    [XmlIgnore] public ConnectionPoint inPoint;
    [XmlIgnore] public ConnectionPoint outPoint;
     
    [XmlIgnore] public GUIStyle style;
    [XmlIgnore] public GUIStyle defaultNodeStyle;
    [XmlIgnore] public GUIStyle selectedNodeStyle;
     
    [XmlIgnore] public Action<Node> OnRemoveNode;
     
    public Node()
    {
    }


    信不信由你,Node类已准备好在这一点上被序列化。 所以,我们来序列化节点!

    保存节点
    记得保存按钮什么也没做? 那么,它至少应该保存节点,因为它们现在是可序列化的。 节点保存的方法非常简单:
        private void Save()
        {
            XMLOp.Serialize(nodes, "Assets/Resources/nodes.xml");
        }

    我们将在用户点击保存按钮时调用该方法:
        private void DrawMenuBar()
        {
            menuBar = new Rect(0, 0, position.width, menuBarHeight);
     
            GUILayout.BeginArea(menuBar, EditorStyles.toolbar);
            GUILayout.BeginHorizontal();
     
            if (GUILayout.Button(new GUIContent("Save"), EditorStyles.toolbarButton, GUILayout.Width(35)))
            {
                Save();
            }
            
            GUILayout.Space(5);
            GUILayout.Button(new GUIContent("Load"), EditorStyles.toolbarButton, GUILayout.Width(35));
     
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }

    现在,打开基于节点的编辑器,放置几个节点,然后点击保存(在此之前,您必须在资源下有一个Resources 文件夹)。 Unity会在Resources 中创建一个名为nodes.xml的文件,如果您看不到它,只需右键单击资源,然后单击重新导入 Ctrl + R。 nodes.xml文件的内容应该是这样的:
    <?xml version="1.0" encoding="us-ascii"?>
    <ArrayOfNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Node>
    <rect>
    <x>166</x>
    <y>183</y>
    <position>
    <x>166</x>
    <y>183</y>
    </position>
    <center>
    <x>266</x>
    <y>208</y>
    </center>
    <min>
    <x>166</x>
    <y>183</y>
    </min>
    <max>
    <x>366</x>
    <y>233</y>
    </max>
    <width>200</width>
    <height>50</height>
    <size>
    <x>200</x>
    <y>50</y>
    </size>
    <xMin>166</xMin>
    <yMin>183</yMin>
    <xMax>366</xMax>
    <yMax>233</yMax>
    </rect>
    </Node>
    <Node>
    <rect>
    <x>345</x>
    <y>345</y>
    <position>
    <x>345</x>
    <y>345</y>
    </position>
    <center>
    <x>445</x>
    <y>370</y>
    </center>
    <min>
    <x>345</x>
    <y>345</y>
    </min>
    <max>
    <x>545</x>
    <y>395</y>
    </max>
    <width>200</width>
    <height>50</height>
    <size>
    <x>200</x>
    <y>50</y>
    </size>
    <xMin>345</xMin>
    <yMin>345</yMin>
    <xMax>545</xMax>
    <yMax>395</yMax>
    </rect>
    </Node>
    </ArrayOfNode>

    所以,现在我们的节点可以被序列化,如果我们可以序列化Connections,我们的节点编辑器将是完全可序列化的。 那么我们继续吧。

    序列化连接
    Connection类只有两个可以序列化的属性:inPoint (ConnectionPoint)outPoint (ConnectionPoint).。 但是,序列化这两个属性将毫无意义,因为对象在反序列化后不会保留对其他对象的引用。 这意味着,如果我们反序列化一个连接,它会创建两个连接点并连接它们,但这些连接点不属于它用来连接的节点(参见下图)。

    序列化破坏连接,反序列化不能解决这个问题。

    为了解决这个问题,我们需要某种连接点的标识符,即一个唯一的ID,这样在反序列化后,我们可以通过它们的ID来查找这些连接点,并为实际对象提供参考以恢复连接。
    public class ConnectionPoint
    {
        public string id;
        
        [XmlIgnore] public Rect rect;
     
        [XmlIgnore] public ConnectionPointType type;
     
        [XmlIgnore] public Node node;
     
        [XmlIgnore] public GUIStyle style;
     
        [XmlIgnore] public Action<ConnectionPoint> OnClickConnectionPoint;
     
        public ConnectionPoint() { }
        
        public ConnectionPoint(Node node, ConnectionPointType type, GUIStyle style, Action<ConnectionPoint> OnClickConnectionPoint, string id = null)
        {
            this.node = node;
            this.type = type;
            this.style = style;
            this.OnClickConnectionPoint = OnClickConnectionPoint;
            rect = new Rect(0, 0, 10f, 20f);
     
            this.id = id ?? Guid.NewGuid().ToString();
        }

    public class Connection
    {
        public ConnectionPoint inPoint;
        public ConnectionPoint outPoint;
        [XmlIgnore] public Action<Connection> OnClickRemoveConnection;

    public class Node
    {
    public Rect rect;
     
    [XmlIgnore] public string title;
    [XmlIgnore] public bool isDragged;
    [XmlIgnore] public bool isSelected;
     
    public ConnectionPoint inPoint;
    public ConnectionPoint outPoint;
     
    [XmlIgnore] public GUIStyle style;
    [XmlIgnore] public GUIStyle defaultNodeStyle;
    [XmlIgnore] public GUIStyle selectedNodeStyle;
     
    [XmlIgnore] public Action<Node> OnRemoveNode;
    当然,我们需要更新我们的Save方法以包含连接:
        private void Save()
        {
            XMLOp.Serialize(nodes, "Assets/Resources/nodes.xml");
            XMLOp.Serialize(connections, "Assets/Resources/connections.xml");
        }
    这就结束了序列化(坦率地说,很难)的一部分。 现在我们有一个基于节点的编辑器的当前状态的XML表示。 我们所要做的就是将其转换回来。

    反序列化
    首先要做的是:Load按钮应该是功能性的。

        private void DrawMenuBar()
        {
            menuBar = new Rect(0, 0, position.width, menuBarHeight);
     
            GUILayout.BeginArea(menuBar, EditorStyles.toolbar);
            GUILayout.BeginHorizontal();
     
            if (GUILayout.Button(new GUIContent("Save"), EditorStyles.toolbarButton, GUILayout.Width(35)))
            {
                Save();
            }
            
            GUILayout.Space(5);
     
            if (GUILayout.Button(new GUIContent("Load"), EditorStyles.toolbarButton, GUILayout.Width(35)))
            {
                Load();
            }
     
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }

    我们将在 Load() 方法中反序列化 XML 文件的内容,创建节点和连接并将它们分配给它们各自的属性。 反序列化XML文件是一个非常简单的过程; 我们所要做的就是调用XMLOp.Deserialize<T>(string):
        private void Load()
        {
            var nodesDeserialized = XMLOp.Deserialize<List<Node>>("Assets/Resources/nodes.xml");
            var connectionsDeserialized = XMLOp.Deserialize<List<Connection>>("Assets/Resources/connections.xml");
     
            nodes = new List<Node>();
            connections = new List<Connection>();
        }
    然而,单独对XML文件进行反序列化并不足以将编辑器恢复到最后一个状态,因为如上图所示,我们在序列化时打破了节点和连接之间的关系,我们需要重新连接它们。 此重新连接过程需要通过ID查找节点并在它们之间创建连接。 这就是为什么我们为ConnectionPoint类添加了唯一ID。 我们需要用这些ID重新创建ConnectionPoints,所以我们要添加另一个构造函数给Node类:
    public Node(Vector2 position, float width, float height, GUIStyle nodeStyle, GUIStyle selectedStyle, GUIStyle inPointStyle, GUIStyle outPointStyle, Action<ConnectionPoint> OnClickInPoint,
    Action<ConnectionPoint> OnClickOutPoint, Action<Node> OnClickRemoveNode, string inPointID, string outPointID)
    {
    rect = new Rect(position.x, position.y, width, height);
    style = nodeStyle;
    inPoint = new ConnectionPoint(this, ConnectionPointType.In, inPointStyle, OnClickInPoint, inPointID);
    outPoint = new ConnectionPoint(this, ConnectionPointType.Out, outPointStyle, OnClickOutPoint, outPointID);
    defaultNodeStyle = nodeStyle;
    selectedNodeStyle = selectedStyle;
    OnRemoveNode = OnClickRemoveNode;
    }

    这是一个新的构造函数,它将创建一个具有两个给定(而不是生成)ID的ConnectionPoint的Node。 现在我们将基于反序列化节点创建新节点:

        private void Load()
        {
            var nodesDeserialized = XMLOp.Deserialize<List<Node>>("Assets/Resources/nodes.xml");
            var connectionsDeserialized = XMLOp.Deserialize<List<Connection>>("Assets/Resources/connections.xml");
     
            nodes = new List<Node>();
            connections = new List<Connection>();
     
            foreach (var nodeDeserialized in nodesDeserialized)
            {
                nodes.Add(new Node(
                    nodeDeserialized.rect.position,
                    nodeDeserialized.rect.width,
                    nodeDeserialized.rect.height,
                    nodeStyle,
                    selectedNodeStyle,
                    inPointStyle,
                    outPointStyle,
                    OnClickInPoint,
                    OnClickOutPoint,
                    OnClickRemoveNode,
                    nodeDeserialized.inPoint.id,
                    nodeDeserialized.outPoint.id
                    )
                );
            }
        }
    继续尝试。 创建几个节点,保存它,关闭编辑器,再次打开并点击Load按钮。 你会看到你的节点回到他们的位置。 让我们反序列化连接并完成我们基于节点的编辑器:

        private void Load()
        {
            var nodesDeserialized = XMLOp.Deserialize<List<Node>>("Assets/Resources/nodes.xml");
            var connectionsDeserialized = XMLOp.Deserialize<List<Connection>>("Assets/Resources/connections.xml");
     
            nodes = new List<Node>();
            connections = new List<Connection>();
     
            foreach (var nodeDeserialized in nodesDeserialized)
            {
                nodes.Add(new Node(
                    nodeDeserialized.rect.position,
                    nodeDeserialized.rect.width,
                    nodeDeserialized.rect.height,
                    nodeStyle,
                    selectedNodeStyle,
                    inPointStyle,
                    outPointStyle,
                    OnClickInPoint,
                    OnClickOutPoint,
                    OnClickRemoveNode,
                    nodeDeserialized.inPoint.id,
                    nodeDeserialized.outPoint.id
                    )
                );
            }
     
            foreach (var connectionDeserialized in connectionsDeserialized)
            {
                var inPoint = nodes.First(n => n.inPoint.id == connectionDeserialized.inPoint.id).inPoint;
                var outPoint = nodes.First(n => n.outPoint.id == connectionDeserialized.outPoint.id).outPoint;
                connections.Add(new Connection(inPoint, outPoint, OnClickRemoveConnection));
            }
        }

    最后的话
    这就结束了 。 您现在有一个功能齐全的基于节点的编辑器,具有保存和加载功能。

    一如既往,下面是完整的脚本。 直到下一次。
    using UnityEngine;
    using UnityEditor;
    using System.Collections.Generic;
    using System.Linq;

    public class NodeBasedEditor : EditorWindow
    {
    private List<Node> nodes;
    private List<Connection> connections;

    private GUIStyle nodeStyle;
    private GUIStyle selectedNodeStyle;
    private GUIStyle inPointStyle;
    private GUIStyle outPointStyle;

    private ConnectionPoint selectedInPoint;
    private ConnectionPoint selectedOutPoint;

    private Vector2 offset;
    private Vector2 drag;
    private float menuBarHeight = 20f;
    private Rect menuBar;

    [MenuItem("Window/Node Based Editor")]
    private static void OpenWindow()
    {
    NodeBasedEditor window = GetWindow<NodeBasedEditor>();
    window.titleContent = new GUIContent("Node Based Editor");
    }

    private void OnEnable()
    {
    nodeStyle = new GUIStyle();
    nodeStyle.normal.background = EditorGUIUtility.Load("builtin skins/darkskin/images/node1.png") as Texture2D;
    nodeStyle.border = new RectOffset(12, 12, 12, 12);

    selectedNodeStyle = new GUIStyle();
    selectedNodeStyle.normal.background = EditorGUIUtility.Load("builtin skins/darkskin/images/node1 on.png") as Texture2D;
    selectedNodeStyle.border = new RectOffset(12, 12, 12, 12);

    inPointStyle = new GUIStyle();
    inPointStyle.normal.background = EditorGUIUtility.Load("builtin skins/darkskin/images/btn left.png") as Texture2D;
    inPointStyle.active.background = EditorGUIUtility.Load("builtin skins/darkskin/images/btn left on.png") as Texture2D;
    inPointStyle.border = new RectOffset(4, 4, 12, 12);

    outPointStyle = new GUIStyle();
    outPointStyle.normal.background = EditorGUIUtility.Load("builtin skins/darkskin/images/btn right.png") as Texture2D;
    outPointStyle.active.background = EditorGUIUtility.Load("builtin skins/darkskin/images/btn right on.png") as Texture2D;
    outPointStyle.border = new RectOffset(4, 4, 12, 12);
    }

    private void OnGUI()
    {
    DrawGrid(20, 0.2f, Color.gray);
    DrawGrid(100, 0.4f, Color.gray);
    DrawMenuBar();

    DrawNodes();
    DrawConnections();

    DrawConnectionLine(Event.current);

    ProcessNodeEvents(Event.current);
    ProcessEvents(Event.current);

    if (GUI.changed) Repaint();
    }

    private void DrawMenuBar()
    {
    menuBar = new Rect(0, 0, position.width, menuBarHeight);

    GUILayout.BeginArea(menuBar, EditorStyles.toolbar);
    GUILayout.BeginHorizontal();

    if (GUILayout.Button(new GUIContent("Save"), EditorStyles.toolbarButton, GUILayout.Width(35)))
    {
    Save();
    }
    GUILayout.Space(5);

    if (GUILayout.Button(new GUIContent("Load"), EditorStyles.toolbarButton, GUILayout.Width(35)))
    {
    Load();
    }

    GUILayout.EndHorizontal();
    GUILayout.EndArea();
    }

    private void DrawGrid(float gridSpacing, float gridOpacity, Color gridColor)
    {
    int widthDivs = Mathf.CeilToInt(position.width / gridSpacing);
    int heightDivs = Mathf.CeilToInt(position.height / gridSpacing);

    Handles.BeginGUI();
    Handles.color = new Color(gridColor.r, gridColor.g, gridColor.b, gridOpacity);

    offset += drag * 0.5f;
    Vector3 newOffset = new Vector3(offset.x % gridSpacing, offset.y % gridSpacing, 0);

    for (int i = 0; i < widthDivs; i++)
    {
    Handles.DrawLine(new Vector3(gridSpacing * i, -gridSpacing, 0) + newOffset, new Vector3(gridSpacing * i, position.height, 0f) + newOffset);
    }

    for (int j = 0; j < heightDivs; j++)
    {
    Handles.DrawLine(new Vector3(-gridSpacing, gridSpacing * j, 0) + newOffset, new Vector3(position.width, gridSpacing * j, 0f) + newOffset);
    }

    Handles.color = Color.white;
    Handles.EndGUI();
    }

    private void DrawNodes()
    {
    if (nodes != null)
    {
    for (int i = 0; i < nodes.Count; i++)
    {
    nodes[i].Draw();
    }
    }
    }

    private void DrawConnections()
    {
    if (connections != null)
    {
    for (int i = 0; i < connections.Count; i++)
    {
    connections[i].Draw();
    }
    }
    }

    private void ProcessEvents(Event e)
    {
    drag = Vector2.zero;

    switch (e.type)
    {
    case EventType.MouseDown:
    if (e.button == 0)
    {
    ClearConnectionSelection();
    }

    if (e.button == 1)
    {
    ProcessContextMenu(e.mousePosition);
    }
    break;

    case EventType.MouseDrag:
    if (e.button == 0)
    {
    OnDrag(e.delta);
    }
    break;
    }
    }

    private void ProcessNodeEvents(Event e)
    {
    if (nodes != null)
    {
    for (int i = nodes.Count - 1; i >= 0; i--)
    {
    bool guiChanged = nodes[i].ProcessEvents(e);

    if (guiChanged)
    {
    GUI.changed = true;
    }
    }
    }
    }

    private void DrawConnectionLine(Event e)
    {
    if (selectedInPoint != null && selectedOutPoint == null)
    {
    Handles.DrawBezier(
    selectedInPoint.rect.center,
    e.mousePosition,
    selectedInPoint.rect.center + Vector2.left * 50f,
    e.mousePosition - Vector2.left * 50f,
    Color.white,
    null,
    2f
    );

    GUI.changed = true;
    }

    if (selectedOutPoint != null && selectedInPoint == null)
    {
    Handles.DrawBezier(
    selectedOutPoint.rect.center,
    e.mousePosition,
    selectedOutPoint.rect.center - Vector2.left * 50f,
    e.mousePosition + Vector2.left * 50f,
    Color.white,
    null,
    2f
    );

    GUI.changed = true;
    }
    }

    private void ProcessContextMenu(Vector2 mousePosition)
    {
    GenericMenu genericMenu = new GenericMenu();
    genericMenu.AddItem(new GUIContent("Add node"), false, () => OnClickAddNode(mousePosition));
    genericMenu.ShowAsContext();
    }

    private void OnDrag(Vector2 delta)
    {
    drag = delta;

    if (nodes != null)
    {
    for (int i = 0; i < nodes.Count; i++)
    {
    nodes[i].Drag(delta);
    }
    }

    GUI.changed = true;
    }

    private void OnClickAddNode(Vector2 mousePosition)
    {
    if (nodes == null)
    {
    nodes = new List<Node>();
    }

    nodes.Add(new Node(mousePosition, 200, 50, nodeStyle, selectedNodeStyle, inPointStyle, outPointStyle, OnClickInPoint, OnClickOutPoint, OnClickRemoveNode));
    }

    private void OnClickInPoint(ConnectionPoint inPoint)
    {
    selectedInPoint = inPoint;

    if (selectedOutPoint != null)
    {
    if (selectedOutPoint.node != selectedInPoint.node)
    {
    CreateConnection();
    ClearConnectionSelection();
    }
    else
    {
    ClearConnectionSelection();
    }
    }
    }

    private void OnClickOutPoint(ConnectionPoint outPoint)
    {
    selectedOutPoint = outPoint;

    if (selectedInPoint != null)
    {
    if (selectedOutPoint.node != selectedInPoint.node)
    {
    CreateConnection();
    ClearConnectionSelection();
    }
    else
    {
    ClearConnectionSelection();
    }
    }
    }

    private void OnClickRemoveNode(Node node)
    {
    if (connections != null)
    {
    List<Connection> connectionsToRemove = new List<Connection>();

    for (int i = 0; i < connections.Count; i++)
    {
    if (connections[i].inPoint == node.inPoint || connections[i].outPoint == node.outPoint)
    {
    connectionsToRemove.Add(connections[i]);
    }
    }

    for (int i = 0; i < connectionsToRemove.Count; i++)
    {
    connections.Remove(connectionsToRemove[i]);
    }

    connectionsToRemove = null;
    }

    nodes.Remove(node);
    }

    private void OnClickRemoveConnection(Connection connection)
    {
    connections.Remove(connection);
    }

    private void CreateConnection()
    {
    if (connections == null)
    {
    connections = new List<Connection>();
    }

    connections.Add(new Connection(selectedInPoint, selectedOutPoint, OnClickRemoveConnection));
    }

    private void ClearConnectionSelection()
    {
    selectedInPoint = null;
    selectedOutPoint = null;
    }

    private void Save()
    {
    XMLOp.Serialize(nodes, "Assets/Resources/nodes.xml");
    XMLOp.Serialize(connections, "Assets/Resources/connections.xml");
    }

    private void Load()
    {
    var nodesDeserialized = XMLOp.Deserialize<List<Node>>("Assets/Resources/nodes.xml");
    var connectionsDeserialized = XMLOp.Deserialize<List<Connection>>("Assets/Resources/connections.xml");

    nodes = new List<Node>();
    connections = new List<Connection>();

    foreach (var nodeDeserialized in nodesDeserialized)
    {
    nodes.Add(new Node(
    nodeDeserialized.rect.position,
    nodeDeserialized.rect.width,
    nodeDeserialized.rect.height,
    nodeStyle,
    selectedNodeStyle,
    inPointStyle,
    outPointStyle,
    OnClickInPoint,
    OnClickOutPoint,
    OnClickRemoveNode,
    nodeDeserialized.inPoint.id,
    nodeDeserialized.outPoint.id
    )
    );
    }

    foreach (var connectionDeserialized in connectionsDeserialized)
    {
    var inPoint = nodes.First(n => n.inPoint.id == connectionDeserialized.inPoint.id).inPoint;
    var outPoint = nodes.First(n => n.outPoint.id == connectionDeserialized.outPoint.id).outPoint;
    connections.Add(new Connection(inPoint, outPoint, OnClickRemoveConnection));
    }
    }
    }

    using System;
    using System.Xml.Serialization;
    using UnityEditor;
    using UnityEngine;

    public class Connection
    {
    public ConnectionPoint inPoint;
    public ConnectionPoint outPoint;
    [XmlIgnore] public Action<Connection> OnClickRemoveConnection;

    public Connection() { }
    public Connection(ConnectionPoint inPoint, ConnectionPoint outPoint, Action<Connection> OnClickRemoveConnection)
    {
    this.inPoint = inPoint;
    this.outPoint = outPoint;
    this.OnClickRemoveConnection = OnClickRemoveConnection;
    }

    public void Draw()
    {
    Handles.DrawBezier(
    inPoint.rect.center,
    outPoint.rect.center,
    inPoint.rect.center + Vector2.left * 50f,
    outPoint.rect.center - Vector2.left * 50f,
    Color.white,
    null,
    2f
    );

    if (Handles.Button((inPoint.rect.center + outPoint.rect.center) * 0.5f, Quaternion.identity, 4, 8, Handles.RectangleCap))
    {
    if (OnClickRemoveConnection != null)
    {
    OnClickRemoveConnection(this);
    }
    }
    }
    }

    using System;
    using System.Xml.Serialization;
    using UnityEditor;
    using UnityEngine;

    public enum ConnectionPointType { In, Out }

    public class ConnectionPoint
    {
    public string id;
    [XmlIgnore] public Rect rect;

    [XmlIgnore] public ConnectionPointType type;

    [XmlIgnore] public Node node;

    [XmlIgnore] public GUIStyle style;

    [XmlIgnore] public Action<ConnectionPoint> OnClickConnectionPoint;

    public ConnectionPoint() { }
    public ConnectionPoint(Node node, ConnectionPointType type, GUIStyle style, Action<ConnectionPoint> OnClickConnectionPoint, string id = null)
    {
    this.node = node;
    this.type = type;
    this.style = style;
    this.OnClickConnectionPoint = OnClickConnectionPoint;
    rect = new Rect(0, 0, 10f, 20f);

    this.id = id ?? Guid.NewGuid().ToString();
    }

    public void Draw()
    {
    rect.y = node.rect.y + (node.rect.height * 0.5f) - rect.height * 0.5f;

    switch (type)
    {
    case ConnectionPointType.In:
    rect.x = node.rect.x - rect.width + 8f;
    break;

    case ConnectionPointType.Out:
    rect.x = node.rect.x + node.rect.width - 8f;
    break;
    }

    if (GUI.Button(rect, "", style))
    {
    if (OnClickConnectionPoint != null)
    {
    OnClickConnectionPoint(this);
    }
    }
    }
    }

    using System;
    using System.Xml.Serialization;
    using UnityEditor;
    using UnityEngine;

    public class Node
    {
    public Rect rect;

    [XmlIgnore] public string title;
    [XmlIgnore] public bool isDragged;
    [XmlIgnore] public bool isSelected;

    public ConnectionPoint inPoint;
    public ConnectionPoint outPoint;

    [XmlIgnore] public GUIStyle style;
    [XmlIgnore] public GUIStyle defaultNodeStyle;
    [XmlIgnore] public GUIStyle selectedNodeStyle;

    [XmlIgnore] public Action<Node> OnRemoveNode;

    public Node()
    {
    }

    public Node(Vector2 position, float width, float height, GUIStyle nodeStyle, GUIStyle selectedStyle, GUIStyle inPointStyle, GUIStyle outPointStyle, Action<ConnectionPoint> OnClickInPoint,
    Action<ConnectionPoint> OnClickOutPoint, Action<Node> OnClickRemoveNode)
    {
    rect = new Rect(position.x, position.y, width, height);
    style = nodeStyle;
    inPoint = new ConnectionPoint(this, ConnectionPointType.In, inPointStyle, OnClickInPoint);
    outPoint = new ConnectionPoint(this, ConnectionPointType.Out, outPointStyle, OnClickOutPoint);
    defaultNodeStyle = nodeStyle;
    selectedNodeStyle = selectedStyle;
    OnRemoveNode = OnClickRemoveNode;
    }

    public Node(Vector2 position, float width, float height, GUIStyle nodeStyle, GUIStyle selectedStyle, GUIStyle inPointStyle, GUIStyle outPointStyle, Action<ConnectionPoint> OnClickInPoint,
    Action<ConnectionPoint> OnClickOutPoint, Action<Node> OnClickRemoveNode, string inPointID, string outPointID)
    {
    rect = new Rect(position.x, position.y, width, height);
    style = nodeStyle;
    inPoint = new ConnectionPoint(this, ConnectionPointType.In, inPointStyle, OnClickInPoint, inPointID);
    outPoint = new ConnectionPoint(this, ConnectionPointType.Out, outPointStyle, OnClickOutPoint, outPointID);
    defaultNodeStyle = nodeStyle;
    selectedNodeStyle = selectedStyle;
    OnRemoveNode = OnClickRemoveNode;
    }

    public void Drag(Vector2 delta)
    {
    rect.position += delta;
    }

    public void Draw()
    {
    inPoint.Draw();
    outPoint.Draw();
    GUI.Box(rect, title, style);
    }

    public bool ProcessEvents(Event e)
    {
    switch (e.type)
    {
    case EventType.MouseDown:
    if (e.button == 0)
    {
    if (rect.Contains(e.mousePosition))
    {
    isDragged = true;
    GUI.changed = true;
    isSelected = true;
    style = selectedNodeStyle;
    }
    else
    {
    GUI.changed = true;
    isSelected = false;
    style = defaultNodeStyle;
    }
    }

    if (e.button == 1 && isSelected && rect.Contains(e.mousePosition))
    {
    ProcessContextMenu();
    e.Use();
    }
    break;

    case EventType.MouseUp:
    isDragged = false;
    break;

    case EventType.MouseDrag:
    if (e.button == 0 && isDragged)
    {
    Drag(e.delta);
    e.Use();
    return true;
    }
    break;
    }

    return false;
    }

    private void ProcessContextMenu()
    {
    GenericMenu genericMenu = new GenericMenu();
    genericMenu.AddItem(new GUIContent("Remove node"), false, OnClickRemoveNode);
    genericMenu.ShowAsContext();
    }

    private void OnClickRemoveNode()
    {
    if (OnRemoveNode != null)
    {
    OnRemoveNode(this);
    }
    }
    }

    using System.IO;
    using System.Xml.Serialization;

    public class XMLOp
    {
    public static void Serialize(object item, string path)
    {
    XmlSerializer serializer = new XmlSerializer(item.GetType());
    StreamWriter writer = new StreamWriter(path);
    serializer.Serialize(writer.BaseStream, item);
    writer.Close();
    }

    public static T Deserialize<T>(string path)
    {
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    StreamReader reader = new StreamReader(path);
    T deserialized = (T)serializer.Deserialize(reader.BaseStream);
    reader.Close();
    return deserialized;
    }
    }

    展开全文
  • libuavcan不直接使用堆分配内存,而是一次性分配一片内存作为内存池来使用,需要我们在创建Node对象时,传入一个表示内存池大小的模板参数,libuavcan会自己基于这片内存池维护内存块的分配和释放。如果我们没有指定...

    节点uavcan::Node是所有API的基础,它是一个模板类。

    内存管理

    1、动态内存

    libuavcan不直接使用堆分配内存,而是一次性分配一片内存作为内存池来使用,需要我们在创建Node对象时,传入一个表示内存池大小的模板参数,libuavcan会自己基于这片内存池维护内存块的分配和释放。如果我们没有指定模板参数,则要给Node节点的构造函数传入一个我们自定义的分配器的引用,这将在后面讨论。

    1.1 内存总量计算

    关于这个大小有个计算方式:
    主要是考虑该节点要订阅的数据和要发布的数据,比如下面的例子,总共有两个CAN接口,节点要发布数据类型A和B,A、B类型数据序列化后的最大长度是100和24,发布A的发布者有两个,发布B的发布者有32个,直接进行算数运算:
    2 * ((3 * 100 + 32 * 24)

    对于接收的数据也一样,只不过接收的接口数要比实际加1,估计是留有余量。
    在这里插入图片描述
    需要的内存总数是:

    2 * ((3 * 100 + 32 * 24) + (2 + 1) * (256 + 10)) = 3732 bytes
    Round up to 64: 3776 bytes.
    

    该结果要64字节对齐。

    1.2 内存获取

    libuavcan提供了查询当前内存使用量以及峰值使用量的方法,通过PoolAllocator<>类来获取。

    - uint16_t getNumUsedBlocks() const - 当前内存块使用量
    - uint16_t getNumFreeBlocks() const - 当前内存块空闲数量
    - uint16_t getPeakNumUsedBlocks() const - 峰值使用量
    

    那么这个PoolAllocator类哪来呢,很显然,必定是节点Node的一个成员,所以可以这样获取:

    std::cout << node.getAllocator().getNumUsedBlocks()     << std::endl;
    std::cout << node.getAllocator().getNumFreeBlocks()     << std::endl;
    std::cout << node.getAllocator().getPeakNumUsedBlocks() << std::endl;
    

    备注:上面的node是一个Node对象。

    1.3 定制内存分配器

    PoolAllcator就是我们前面所说的内存分配器,在创建Node节点的时候,如果传入了内存总数,则Node内部会自己创建内存分配器;如果我们不传入内存总数,则需要自己创建PoolAllcator,然后传给Node。

    前面说节点采用一次性静态内存分配的方式来,这只不过是默认选择,其实libuavcan也实现了其他可选的内存分配器,如基于堆的动态内存分配,见uavcan/helpers/heap_based_pool_allocator.hpp,我们通常没有这个需求,所以不讨论。

    2、栈内存

    消息和服务对象在栈上分配,也就是说通过回调函数传递给应用的消息对象在出了回调函数之后就消失了。

    展开全文
  • 关于Node2Vec的介绍有很多了,...创建Node2Vec模型 训练和测试数据 TSNE降维后可视化 完整代码如下: import torch import matplotlib.pyplot as plt from sklearn.manifold import TSNE from torch_geometric.data

    关于Node2Vec的介绍有很多了,这里就不细述。本文主要是介绍如何用PyTorch Geometric快速实现Node2Vec节点分类,并对其结果进行可视化。

    整个过程包含四个步骤:

    • 导入图数据(这里以Cora为例)
    • 创建Node2Vec模型
    • 训练和测试数据
    • TSNE降维后可视化

    完整代码如下:

    import torch
    import matplotlib.pyplot as plt
    from sklearn.manifold import TSNE
    from torch_geometric.datasets import Planetoid
    from torch_geometric.nn import Node2Vec
    
    dataset = Planetoid(root='/tmp/Cora', name='Cora')
    data = dataset[0]
    
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model = Node2Vec(data.edge_index, embedding_dim=128, walk_length=20,
                     context_size=10, walks_per_node=10, num_negative_samples=1,
                     sparse=True).to(device)
    loader = model.loader(batch_size=128, shuffle=True, num_workers=4)
    optimizer = torch.optim.SparseAdam(model.parameters(), lr=0.01)
    
    
    def train():
        model.train()
        total_loss = 0
        for pos_rw, neg_rw in loader:
            optimizer.zero_grad()
            loss = model.loss(pos_rw.to(device), neg_rw.to(device))
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        return total_loss / len(loader)
    
    
    @torch.no_grad()
    def test():
        model.eval()
        z = model()
        acc = model.test(z[data.train_mask], data.y[data.train_mask],
                         z[data.test_mask], data.y[data.test_mask], max_iter=150)
        return acc
    
    
    for epoch in range(1, 101):
        loss = train()
        acc = test()
        print(f'Epoch: {epoch:02d}, Loss: {loss:.4f}, Acc: {acc:.4f}')
    
    
    @torch.no_grad()
    def plot_points(colors):
        model.eval()
        z = model(torch.arange(data.num_nodes, device=device))
        z = TSNE(n_components=2).fit_transform(z.cpu().numpy())
        y = data.y.cpu().numpy()
    
        plt.figure(figsize=(8, 8))
        for i in range(dataset.num_classes):
            plt.scatter(z[y == i, 0], z[y == i, 1], s=20, color=colors[i])
        plt.axis('off')
        plt.show()
    
    
    colors = ['#ffc0cb', '#bada55', '#008080', '#420420', '#7fe5f0', '#065535', '#ffd700']
    plot_points(colors)
    

    输出结果如下:

    Epoch: 01, Loss: 8.0489, Acc: 0.1780
    Epoch: 02, Loss: 6.0371, Acc: 0.2000
    Epoch: 03, Loss: 4.9573, Acc: 0.2190
    Epoch: 04, Loss: 4.1341, Acc: 0.2540
    Epoch: 05, Loss: 3.4723, Acc: 0.2920
    Epoch: 06, Loss: 2.9627, Acc: 0.3250
    Epoch: 07, Loss: 2.5404, Acc: 0.3550
    Epoch: 08, Loss: 2.2190, Acc: 0.3840
    Epoch: 09, Loss: 1.9515, Acc: 0.4200
    Epoch: 10, Loss: 1.7386, Acc: 0.4470
    Epoch: 11, Loss: 1.5671, Acc: 0.4670
    Epoch: 12, Loss: 1.4274, Acc: 0.4890
    Epoch: 13, Loss: 1.3167, Acc: 0.5060
    Epoch: 14, Loss: 1.2284, Acc: 0.5340
    Epoch: 15, Loss: 1.1606, Acc: 0.5530
    Epoch: 16, Loss: 1.1018, Acc: 0.5580
    Epoch: 17, Loss: 1.0573, Acc: 0.5850
    Epoch: 18, Loss: 1.0241, Acc: 0.5970
    Epoch: 19, Loss: 0.9943, Acc: 0.6070
    Epoch: 20, Loss: 0.9701, Acc: 0.6080
    Epoch: 21, Loss: 0.9523, Acc: 0.6230
    Epoch: 22, Loss: 0.9342, Acc: 0.6290
    Epoch: 23, Loss: 0.9200, Acc: 0.6360
    Epoch: 24, Loss: 0.9104, Acc: 0.6460
    Epoch: 25, Loss: 0.9003, Acc: 0.6350
    Epoch: 26, Loss: 0.8936, Acc: 0.6440
    Epoch: 27, Loss: 0.8859, Acc: 0.6590
    Epoch: 28, Loss: 0.8776, Acc: 0.6570
    Epoch: 29, Loss: 0.8730, Acc: 0.6590
    Epoch: 30, Loss: 0.8696, Acc: 0.6660
    Epoch: 31, Loss: 0.8658, Acc: 0.6740
    Epoch: 32, Loss: 0.8609, Acc: 0.6740
    Epoch: 33, Loss: 0.8586, Acc: 0.6750
    Epoch: 34, Loss: 0.8559, Acc: 0.6770
    Epoch: 35, Loss: 0.8524, Acc: 0.6800
    Epoch: 36, Loss: 0.8516, Acc: 0.6880
    Epoch: 37, Loss: 0.8494, Acc: 0.6820
    Epoch: 38, Loss: 0.8481, Acc: 0.6770
    Epoch: 39, Loss: 0.8464, Acc: 0.6800
    Epoch: 40, Loss: 0.8435, Acc: 0.6850
    Epoch: 41, Loss: 0.8423, Acc: 0.6890
    Epoch: 42, Loss: 0.8401, Acc: 0.6870
    Epoch: 43, Loss: 0.8392, Acc: 0.6900
    Epoch: 44, Loss: 0.8390, Acc: 0.6850
    Epoch: 45, Loss: 0.8365, Acc: 0.6880
    Epoch: 46, Loss: 0.8363, Acc: 0.6920
    Epoch: 47, Loss: 0.8354, Acc: 0.6990
    Epoch: 48, Loss: 0.8345, Acc: 0.6970
    Epoch: 49, Loss: 0.8352, Acc: 0.6970
    Epoch: 50, Loss: 0.8350, Acc: 0.7040
    Epoch: 51, Loss: 0.8333, Acc: 0.6970
    Epoch: 52, Loss: 0.8320, Acc: 0.6980
    Epoch: 53, Loss: 0.8321, Acc: 0.6960
    Epoch: 54, Loss: 0.8325, Acc: 0.6940
    Epoch: 55, Loss: 0.8312, Acc: 0.7010
    Epoch: 56, Loss: 0.8298, Acc: 0.7040
    Epoch: 57, Loss: 0.8294, Acc: 0.6990
    Epoch: 58, Loss: 0.8296, Acc: 0.6960
    Epoch: 59, Loss: 0.8302, Acc: 0.7050
    Epoch: 60, Loss: 0.8286, Acc: 0.7030
    Epoch: 61, Loss: 0.8298, Acc: 0.7020
    Epoch: 62, Loss: 0.8292, Acc: 0.7010
    Epoch: 63, Loss: 0.8288, Acc: 0.7090
    Epoch: 64, Loss: 0.8284, Acc: 0.6990
    Epoch: 65, Loss: 0.8267, Acc: 0.6970
    Epoch: 66, Loss: 0.8274, Acc: 0.6950
    Epoch: 67, Loss: 0.8279, Acc: 0.6940
    Epoch: 68, Loss: 0.8274, Acc: 0.6940
    Epoch: 69, Loss: 0.8278, Acc: 0.7000
    Epoch: 70, Loss: 0.8258, Acc: 0.7000
    Epoch: 71, Loss: 0.8283, Acc: 0.6990
    Epoch: 72, Loss: 0.8257, Acc: 0.6990
    Epoch: 73, Loss: 0.8262, Acc: 0.7060
    Epoch: 74, Loss: 0.8260, Acc: 0.7120
    Epoch: 75, Loss: 0.8259, Acc: 0.7140
    Epoch: 76, Loss: 0.8266, Acc: 0.7060
    Epoch: 77, Loss: 0.8254, Acc: 0.7070
    Epoch: 78, Loss: 0.8261, Acc: 0.7030
    Epoch: 79, Loss: 0.8258, Acc: 0.6980
    Epoch: 80, Loss: 0.8253, Acc: 0.6950
    Epoch: 81, Loss: 0.8256, Acc: 0.7050
    Epoch: 82, Loss: 0.8252, Acc: 0.7070
    Epoch: 83, Loss: 0.8238, Acc: 0.7060
    Epoch: 84, Loss: 0.8253, Acc: 0.7060
    Epoch: 85, Loss: 0.8251, Acc: 0.7070
    Epoch: 86, Loss: 0.8255, Acc: 0.7090
    Epoch: 87, Loss: 0.8251, Acc: 0.7160
    Epoch: 88, Loss: 0.8247, Acc: 0.7140
    Epoch: 89, Loss: 0.8246, Acc: 0.7020
    Epoch: 90, Loss: 0.8245, Acc: 0.7050
    Epoch: 91, Loss: 0.8250, Acc: 0.7160
    Epoch: 92, Loss: 0.8249, Acc: 0.7100
    Epoch: 93, Loss: 0.8249, Acc: 0.7040
    Epoch: 94, Loss: 0.8245, Acc: 0.7060
    Epoch: 95, Loss: 0.8252, Acc: 0.7030
    Epoch: 96, Loss: 0.8244, Acc: 0.6990
    Epoch: 97, Loss: 0.8242, Acc: 0.7030
    Epoch: 98, Loss: 0.8244, Acc: 0.7050
    Epoch: 99, Loss: 0.8236, Acc: 0.6990
    Epoch: 100, Loss: 0.8233, Acc: 0.7030
    

    可视化效果如下:

    在这里插入图片描述

    Cora数据集中一共有七种节点,所以使用了七种颜色。从分类结果看,准确率只有0.7左右,并不是很高,所以可视化效果也不是特别好,有些类别混杂在一起了。可以考虑使用其他方法进行改进。

    参考文献
    node2vec:算法原理,实现和应用
    TSNE——目前最好的降维方法

    展开全文
  • 构造函数是使用了默认数据域为0,默认指针域为NULL #include<iostream>...//用结构体类型表示一个节点 class Node { public: typedef double value_type; Node(const value_type&...

     构造函数是使用了默认数据域为0,默认指针域为NULL 

    #include<iostream>
    #include<cstdlib>//size_t
    using namespace std;
    //用结构体类型表示一个节点
    
    class Node {
    public:
    	typedef double value_type;
    	Node(const value_type& init_data = 0, Node* init_link = NULL) {//默认构造函数的数据域为0
    		data_field = init_data;
    		link_field = init_link;
    	}
    	void set_data(const value_type& value) {
    		data_field = value;
    	}
    	void set_link(Node* link) {
    		link_field = link;
    	}
    	value_type data() {
    		return data_field;
    	}
    	Node* link() {
    		return link_field;
    	}
    private:
    	value_type data_field;
    	Node* link_field;
    };
    Node* create_list(int num) {
    	Node::value_type head_data;
    	cin >> head_data;
    	Node* head_ptr = new Node(head_data);//我的头节点是有东西的
    	Node* pre_ptr = head_ptr;//定义一个前向节点为头节点,后面就通过这个指针连起来
    	for (int i = 1; i < num; ++i) {
    		Node::value_type temp;
    		cin >> temp;
    		Node* new_ptr = new Node(temp);
    		pre_ptr->set_link(new_ptr);//将前一个指针指向新节点
    		pre_ptr = new_ptr;//然后更新该节点
    	}
    	return head_ptr;
    }
    //输出链表
    void display_list(Node* head_ptr) {
    	Node* cursor = head_ptr;
    	for (; cursor != NULL; cursor = cursor->link()) {
    		cout << cursor->data()<<' ';
    	}
    	cout << endl;
    }
    int main() {
    	int n = 5;
    	Node* head_ptr = create_list(n);//输入1 2 3 4 5
    	display_list(head_ptr);//1 2 3 4 5
            while(1);
    }

    解析:

    首先创建头节点,输入头节点数据域,指针域由构造函数可知指向NULL,然后循环创建节点,通过pre_ptr连接所以节点即可

    步骤一、pre_ptr指向头节点,创建新节点

    步骤二、通过pre_ptr连接节点:设置pre_ptr的指针域为新节点,然后更新pre_ptr

    步骤三、重复步骤二,知道创建的节点到达预设的num即可.

    (完)

    展开全文
  • 用于创建AST节点。 请考虑关注该项目的作者 ,并考虑为该项目以显示您的 :red_heart_selector: 和支持。 安装 使用安装: $ npm install --save snapdragon-node 用法 const Node = require ( 'snapdragon-...
  • 用过Nuke的童鞋都知道我们可以拖拽mov或者文件夹素材到Nuke的Node Graph中,它可以自动分类这些素材形成几个Read节点,并自动识别素材的帧数范围以及Metadata关于素材的所有信息。那么在编写工具的时候如果遇到需要...
  • 上一期我们讲到了Node-RED中存储file分类节点的使用,利用存储节点我们可以创建文件,追加内容到指定文件, 监听一些文件的写入 本篇文章我来给大家讲一下Node-RED中网络network分类下的节点,该分类下有12个节点, ...
  • 1.怎样写代码创建一个节点 1.1初始化一个节点对象$node = new stdClass(); //创建一个节点对象$node->type = "page"; // 指定节点的类型$node->title = "你的节点的标题";$node->language = LANGUAGE_NONE; // 如果...
  • 所以首先我们需要一个结点,以定义数据类型和指针域,结点代码如下: public class Node { //数据 int data; //下一个节点 Node next; Node before; //创建一个无参数构造方法,用于初始化 public Node...
  • #include "stdafx.h" #include #include using namespace std; //二叉排序树,完成创建节点,插入节点,删除节点,查找节点,中序遍历的功能 //节点类定义 class Node{ public: int
  • /单向链表操作/ 节点对换 ...创建node节点类ListNode,与链表类LinkList,属性均为public#include using namespace std; #define ok 0 #define error -1 class ListNode { public: int data; ListNode *next; List
  • //Node.h #include using namespace std; class Node { public: Node(int data) { this->data = data; this->next = NULL; } ~Node() {} static Node * pHead;//因为头尾指针不属于某一个
  • Java实现创建简单的链表操作

    千次阅读 2019-03-27 10:49:10
    1、创建Node节点类 //构建节点类 public class Node { int data; int np; String names; Node next; //节点声明的构造函数 public Node(int data, int np, String names) { this.data = data; this.np =...
  • 1.创建nodeEnvironment,节点的环境信息 2.读取jvm信息 jvmInfo 3.创建pluginService对象,会加载所有的模块和插件 4.然后调用pluginService更新settings配置文件 5.再初始化最终的environment 6.创建一个...
  • Unity 可视化编辑工具 树节点 Tree Node Editor 最近想写一个行为树编辑器,终于找到编辑显示节点和连线的...创建一个节点类 NodeRoot 不需要放在 Editor 文件夹下 using System.Collections.Generic; using Uni...
  • 创建节点类 class Node(object): def init(self, item): self.item = item self.lchild = None # 左孩子 self.rchild = None # 右孩子 创建树类 class BinaryTree(object): def init(self, node=None): self....
  • //Node.h #include <iostream> using namespace std; class Node { public: Node(int data) { this->data = data; this->next = NULL; } ~Node() {} static N...
  • 栈的创建-----用链表实现栈

    千次阅读 2018-11-01 09:09:03
    1、创建Node节点类(存储链表的前节点和本节点存储的元素) 2、节点存储的是泛型数据 3、创建一个栈的接口----定义如下函数: 4、接口实现类(栈顶元素指针和链表元素计数器) 代码实现: 接口类:StackADT ...
  • 文章目录二叉树的节点表示树的创建 ... """节点类""" # elem为本身的值,lchild为左孩子,rchild为有孩子 def __init__(self, elem=-1, lchild=None, rchild=None): self.elem = elem self.lchil...
  • 1: Action是动作命令,我们创建Action,然后节点运行action就能够执行Action的动作; 2: Action分为两: (1) 瞬时就完成的ActionInstant, (2) 要一段时间后才能完成ActionIntervial; 3: cc.Node runAction: 节点...
  • Node.js堆栈 Node.js堆栈提供了开发应用程序的一致方法。 作为异步事件驱动JavaScript运行时,Node旨在构建可扩展的网络应用程序。 该堆栈基于Node.js v12运行时,并允许您使用Appsody开发新的或现有的Node.js应用...
  • <div><p>摘要:【CITA】新增普通节点出错 ...前提条 操作步骤:...可以正常创建普通节点。 实际结果:创建普通节点报错。</p><p>该提问来源于开源项目:citahub/cita</p></div>
  • ROS2 的主函数的一般形式如下面的代码所示,实际使用时,当我在里面创建了一个成员函数的时候,发现无法调用到该函数。 int main(int argc, char **argv) { // RCLCPP_INFO(LOGGER, "Initialize node"); rclcpp:...
  • #通过使用Node类中定义三个属性,分别为elem本身的值,还有lchild左孩子和rchild右孩子 class Node(object): ...#树的创建,创建一个树的,并给一个root根节点,一开始为空,随后添加节点 cl...
  • 学生党一枚。最近课程设计做了个多叉树的书目分类管理系统。但愿有点用吧。... 原创 可能会有很多不足 请多指教 public class Node { private String id; //节点id private String name; //节点名称 private
  • Ogre中与节点动画相关的: (1)Ogre::Animation (2)Ogre::AnimationState (3)Ogre::NodeAnimationTrack (4)Ogre::TransformKeyFrame 节点动画的使用步骤: //1.创建动画对象...
  • //创建Node节点类,带权值 //它可以使继承他的类进行比较大小,只需要调用实现类的compareTo方法即可 class Node implements Comparable<Node>{ public Byte data;//存放数据本身,字符a转化成ASCII码97,...
  • 模板存储库,用于创建针对现代Node.JS(> = 14)的程序包,并支持本机ECMAScript模块,JSDoc TypeScript输入,私有字段和其他ES2020功能。 ESLint 7配置为支持ES2020功能,包括随附的提案(通过@ typescript-...
  • 节点

    2019-07-18 07:59:02
    创建元素节点. 首先我说一下插入新的节点方法:appendChild(),插入节点appendChild()是在指定节点的最后一个子节点列表之后添加一个新的子节点。它语法是:appendChild(newnode),所以它的参数也就是newnode,而new...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 760
精华内容 304
关键字:

创建node节点类