Unity基于GraphView的可视化关卡编辑器开发指南

news2025/6/7 12:18:57

一、GraphView技术基础与应用场景

1. GraphView核心组件

组件功能描述关卡编辑应用
GraphView画布容器关卡拓扑结构编辑区
Node基础节点房间/敌人/道具等关卡元素
Edge节点连接线路径/依赖关系
Port连接端口入口/出口标记
Blackboard属性面板元素参数配置
Minimap缩略图导航大型关卡导航

2. 关卡编辑器核心功能规划

图表

节点创建

连接编辑

属性配置

实时预览

数据序列化

场景生成


二、基础编辑器框架实现

1. 编辑器窗口创建

对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀

using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;

public class LevelGraphWindow : EditorWindow
{
    private LevelGraphView _graphView;
    
    [MenuItem("Tools/Level Graph Editor")]
    public static void OpenWindow()
    {
        var window = GetWindow<LevelGraphWindow>();
        window.titleContent = new GUIContent("Level Editor");
    }
    
    private void OnEnable()
    {
        ConstructGraphView();
        GenerateToolbar();
    }
    
    private void ConstructGraphView()
    {
        _graphView = new LevelGraphView
        {
            name = "Level Graph"
        };
        
        _graphView.StretchToParentSize();
        rootVisualElement.Add(_graphView);
    }
    
    private void GenerateToolbar()
    {
        var toolbar = new Toolbar();
        
        var createRoomBtn = new Button(() => _graphView.CreateRoomNode("Room"))
        {
            text = "Create Room"
        };
        toolbar.Add(createRoomBtn);
        
        var saveBtn = new Button(() => SaveGraph())
        {
            text = "Save"
        };
        toolbar.Add(saveBtn);
        
        rootVisualElement.Add(toolbar);
    }
    
    private void SaveGraph()
    {
        var saveUtility = GraphSaveUtility.GetInstance(_graphView);
        saveUtility.SaveGraph("LevelDesign");
    }
}

2. 自定义GraphView

public class LevelGraphView : GraphView
{
    public LevelGraphView()
    {
        // 基础设置
        SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
        this.AddManipulator(new ContentDragger());
        this.AddManipulator(new SelectionDragger());
        this.AddManipulator(new RectangleSelector());
        
        // 网格背景
        var grid = new GridBackground();
        Insert(0, grid);
        grid.StretchToParentSize();
        
        // 样式设置
        var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Editor/LevelGraph.uss");
        styleSheets.Add(styleSheet);
    }
    
    public RoomNode CreateRoomNode(string nodeName)
    {
        var roomNode = new RoomNode(this, nodeName);
        AddElement(roomNode);
        return roomNode;
    }
    
    // 创建节点连接关系
    public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
    {
        var compatiblePorts = new List<Port>();
        ports.ForEach(port => {
            // 禁止自连接
            if (startPort.node == port.node) return;
            
            // 输入只能连输出
            if (startPort.direction == port.direction) return;
            
            // 不同类型端口不能连接
            if (startPort.portType != port.portType) return;
            
            compatiblePorts.Add(port);
        });
        return compatiblePorts;
    }
}

三、关卡节点系统实现

1. 房间节点实现

public class RoomNode : Node
{
    public string GUID;
    public RoomType RoomType;
    public Vector2 Position;
    
    private LevelGraphView _graphView;
    
    public RoomNode(LevelGraphView graphView, string title)
    {
        GUID = Guid.NewGuid().ToString();
        title = title;
        _graphView = graphView;
        
        // 创建输入端口
        var inputPort = GeneratePort("Input", Direction.Input, Port.Capacity.Multi);
        inputContainer.Add(inputPort);
        
        // 创建输出端口
        var outputPort = GeneratePort("Output", Direction.Output, Port.Capacity.Multi);
        outputContainer.Add(outputPort);
        
        // 房间类型下拉菜单
        var roomTypeField = new EnumField(RoomType.Normal);
        roomTypeField.RegisterValueChangedCallback(evt => {
            RoomType = (RoomType)evt.newValue;
        });
        mainContainer.Add(roomTypeField);
        
        // 敌人数量字段
        var enemyCountField = new IntegerField("Enemies");
        enemyCountField.value = 0;
        enemyCountField.RegisterValueChangedCallback(evt => {
            // 保存到节点数据
        });
        mainContainer.Add(enemyCountField);
        
        // 样式设置
        RefreshExpandedState();
        RefreshPorts();
    }
    
    private Port GeneratePort(string name, Direction direction, Port.Capacity capacity)
    {
        return InstantiatePort(
            Orientation.Horizontal, 
            direction, 
            capacity, 
            typeof(float) // 使用虚拟类型
        );
    }
}

2. 特殊节点类型

public class SpawnNode : Node
{
    public SpawnPointType SpawnType;
    
    public SpawnNode()
    {
        title = "Spawn Point";
        
        // 玩家/敌人选择
        var typeField = new EnumField(SpawnPointType.Player);
        typeField.RegisterValueChangedCallback(evt => {
            SpawnType = (SpawnPointType)evt.newValue;
        });
        mainContainer.Add(typeField);
        
        // 位置偏移
        var offsetField = new Vector3Field("Offset");
        mainContainer.Add(offsetField);
    }
}

public class BossRoomNode : RoomNode
{
    public BossRoomNode(LevelGraphView graphView) : base(graphView, "Boss Room")
    {
        // 添加特殊属性
        var bossTypeField = new EnumField(BossType.Dragon);
        mainContainer.Add(bossTypeField);
        
        // 样式覆盖
        AddToClassList("boss-node");
    }
}

四、数据持久化与场景生成

1. 序列化数据结构

[System.Serializable]
public class NodeSaveData
{
    public string GUID;
    public string Type;
    public Vector2 Position;
    public string AdditionalData; // JSON序列化扩展数据
}

[System.Serializable]
public class EdgeSaveData
{
    public string InputNodeGUID;
    public string OutputNodeGUID;
}

[System.Serializable]
public class GraphSaveData
{
    public List<NodeSaveData> Nodes = new List<NodeSaveData>();
    public List<EdgeSaveData> Edges = new List<EdgeSaveData>();
}

2. 序列化管理器

public class GraphSaveUtility
{
    private LevelGraphView _graphView;
    
    public static GraphSaveUtility GetInstance(LevelGraphView graphView)
    {
        return new GraphSaveUtility {
            _graphView = graphView
        };
    }
    
    public void SaveGraph(string fileName)
    {
        var saveData = new GraphSaveData();
        
        // 收集节点数据
        foreach (var node in _graphView.nodes.ToList().Cast<BaseNode>())
        {
            saveData.Nodes.Add(new NodeSaveData {
                GUID = node.GUID,
                Position = node.GetPosition().position,
                Type = node.GetType().Name,
                AdditionalData = JsonUtility.ToJson(node.GetSaveData())
            });
        }
        
        // 收集连接数据
        foreach (var edge in _graphView.edges.ToList())
        {
            var inputNode = edge.input.node as BaseNode;
            var outputNode = edge.output.node as BaseNode;
            
            saveData.Edges.Add(new EdgeSaveData {
                InputNodeGUID = inputNode.GUID,
                OutputNodeGUID = outputNode.GUID
            });
        }
        
        // 保存到文件
        string json = JsonUtility.ToJson(saveData, true);
        string path = $"Assets/LevelDesign/{fileName}.level";
        File.WriteAllText(path, json);
        AssetDatabase.Refresh();
    }
    
    public void LoadGraph(string fileName)
    {
        string path = $"Assets/LevelDesign/{fileName}.level";
        if (!File.Exists(path)) return;
        
        string json = File.ReadAllText(path);
        var saveData = JsonUtility.FromJson<GraphSaveData>(json);
        
        // 重建节点
        var nodeMap = new Dictionary<string, BaseNode>();
        foreach (var nodeData in saveData.Nodes)
        {
            BaseNode node = CreateNodeFromType(nodeData.Type);
            node.GUID = nodeData.GUID;
            node.SetPosition(new Rect(nodeData.Position, Vector2.zero));
            node.LoadData(JsonUtility.FromJson(nodeData.AdditionalData, node.GetSaveType()));
            nodeMap.Add(nodeData.GUID, node);
            _graphView.AddElement(node);
        }
        
        // 重建连接
        foreach (var edgeData in saveData.Edges)
        {
            var inputNode = nodeMap[edgeData.InputNodeGUID];
            var outputNode = nodeMap[edgeData.OutputNodeGUID];
            
            Port inputPort = inputNode.GetInputPort();
            Port outputPort = outputNode.GetOutputPort();
            
            var edge = inputPort.ConnectTo(outputPort);
            _graphView.AddElement(edge);
        }
    }
}

3. 场景生成器

public class LevelGenerator
{
    public void GenerateLevel(GraphSaveData levelData)
    {
        // 创建根对象
        var levelRoot = new GameObject("GeneratedLevel");
        
        // 实例化房间
        var roomMap = new Dictionary<string, GameObject>();
        foreach (var nodeData in levelData.Nodes)
        {
            if (nodeData.Type == "RoomNode")
            {
                var roomData = JsonUtility.FromJson<RoomSaveData>(nodeData.AdditionalData);
                var roomPrefab = GetRoomPrefab(roomData.RoomType);
                var roomObj = PrefabUtility.InstantiatePrefab(roomPrefab) as GameObject;
                roomObj.transform.SetParent(levelRoot.transform);
                roomObj.transform.position = roomData.Position;
                roomMap.Add(nodeData.GUID, roomObj);
            }
        }
        
        // 创建连接通道
        foreach (var edgeData in levelData.Edges)
        {
            var startRoom = roomMap[edgeData.OutputNodeGUID];
            var endRoom = roomMap[edgeData.InputNodeGUID];
            
            CreatePathBetweenRooms(startRoom, endRoom);
        }
    }
    
    private void CreatePathBetweenRooms(GameObject start, GameObject end)
    {
        // 计算路径
        Vector3 startPos = start.transform.position;
        Vector3 endPos = end.transform.position;
        Vector3 direction = (endPos - startPos).normalized;
        
        // 实例化路径预制体
        var pathPrefab = Resources.Load<GameObject>("PathSegment");
        float distance = Vector3.Distance(startPos, endPos);
        int segments = Mathf.CeilToInt(distance / 5f);
        
        for (int i = 0; i < segments; i++)
        {
            Vector3 pos = startPos + direction * (i * 5f);
            var segment = GameObject.Instantiate(pathPrefab, pos, Quaternion.LookRotation(direction));
            segment.transform.SetParent(start.transform.parent);
        }
    }
}

五、实时预览系统实现

1. 场景视图渲染

[InitializeOnLoad]
public class LevelPreviewRenderer
{
    static LevelPreviewRenderer()
    {
        SceneView.duringSceneGui += RenderLevelPreview;
    }

    private static void RenderLevelPreview(SceneView sceneView)
    {
        if (_graphView == null) return;
        
        Handles.BeginGUI();
        
        // 绘制房间
        foreach (var node in _graphView.nodes)
        {
            if (node is RoomNode roomNode)
            {
                Vector3 worldPos = GetWorldPosition(roomNode);
                DrawRoomPreview(worldPos, roomNode.RoomType);
            }
        }
        
        // 绘制连接
        foreach (var edge in _graphView.edges)
        {
            var startNode = edge.output.node as RoomNode;
            var endNode = edge.input.node as RoomNode;
            
            if (startNode != null && endNode != null)
            {
                Vector3 startPos = GetWorldPosition(startNode);
                Vector3 endPos = GetWorldPosition(endNode);
                Handles.DrawDottedLine(startPos, endPos, 5f);
            }
        }
        
        Handles.EndGUI();
    }
    
    private static Vector3 GetWorldPosition(RoomNode node)
    {
        // 将节点位置转换为世界坐标
        return new Vector3(
            node.Position.x, 
            0, 
            node.Position.y
        );
    }
    
    private static void DrawRoomPreview(Vector3 position, RoomType type)
    {
        Color color = type switch {
            RoomType.Start => Color.green,
            RoomType.Boss => Color.red,
            RoomType.Treasure => Color.yellow,
            _ => Color.gray
        };
        
        Handles.color = color;
        Handles.DrawWireCube(position, Vector3.one * 10);
        Handles.Label(position + Vector3.up * 6, type.ToString());
    }
}

2. 3D小地图实现

public class LevelMinimap : EditorWindow
{
    [MenuItem("Tools/Level Minimap")]
    public static void ShowWindow()
    {
        GetWindow<LevelMinimap>("Level Minimap");
    }
    
    void OnGUI()
    {
        if (_graphView == null) return;
        
        // 计算视图参数
        Rect viewRect = GetLevelBounds();
        float scale = Mathf.Min(
            position.width / viewRect.width,
            position.height / viewRect.height
        );
        
        // 绘制背景
        EditorGUI.DrawRect(new Rect(0, 0, position.width, position.height), Color.black);
        
        // 绘制房间
        foreach (var node in _graphView.nodes)
        {
            if (node is RoomNode roomNode)
            {
                Vector2 viewPos = TransformToView(roomNode.Position, viewRect, scale);
                DrawRoomMinimap(viewPos, roomNode.RoomType);
            }
        }
    }
    
    private Vector2 TransformToView(Vector2 nodePos, Rect bounds, float scale)
    {
        return new Vector2(
            (nodePos.x - bounds.x) * scale,
            (nodePos.y - bounds.y) * scale
        );
    }
    
    private void DrawRoomMinimap(Vector2 position, RoomType type)
    {
        // 绘制逻辑类似场景预览
    }
}

六、进阶功能扩展

1. 自动布局算法

public class LevelLayoutOrganizer
{
    public void AutoArrange(LevelGraphView graphView)
    {
        // 1. 分组处理
        var roomGroups = FindConnectedGroups(graphView);
        
        // 2. 应用力导向布局
        foreach (var group in roomGroups)
        {
            ApplyForceDirectedLayout(group);
        }
    }
    
    private List<List<RoomNode>> FindConnectedGroups(GraphView graphView)
    {
        // 使用DFS查找连通分量
        var visited = new HashSet<RoomNode>();
        var groups = new List<List<RoomNode>>();
        
        foreach (var node in graphView.nodes.OfType<RoomNode>())
        {
            if (!visited.Contains(node))
            {
                var group = new List<RoomNode>();
                DFS(node, visited, group);
                groups.Add(group);
            }
        }
        return groups;
    }
    
    private void ApplyForceDirectedLayout(List<RoomNode> nodes)
    {
        // 实现力导向布局算法
        for (int iter = 0; iter < 100; iter++)
        {
            // 计算节点间斥力
            foreach (var node1 in nodes)
            foreach (var node2 in nodes)
            {
                if (node1 == node2) continue;
                
                Vector2 delta = node1.Position - node2.Position;
                float distance = delta.magnitude;
                if (distance > 0)
                {
                    Vector2 force = delta.normalized * RepulsionForce(distance);
                    node1.Position += force * Time.deltaTime;
                }
            }
            
            // 计算连接点引力
            foreach (var edge in _graphView.edges)
            {
                var start = edge.output.node as RoomNode;
                var end = edge.input.node as RoomNode;
                
                if (start != null && end != null)
                {
                    Vector2 delta = end.Position - start.Position;
                    Vector2 force = delta * AttractionForce(delta.magnitude);
                    start.Position += force * Time.deltaTime;
                    end.Position -= force * Time.deltaTime;
                }
            }
        }
    }
}

2. 规则验证系统

public class LevelRuleValidator
{
    public List<string> Validate(LevelGraphView graphView)
    {
        var errors = new List<string>();
        
        // 检查起点存在性
        if (!graphView.nodes.Any(n => n is RoomNode r && r.RoomType == RoomType.Start))
        {
            errors.Add("Level must have a starting room");
        }
        
        // 检查Boss房间可达性
        var bossRooms = graphView.nodes.OfType<RoomNode>()
            .Where(r => r.RoomType == RoomType.Boss);
        
        foreach (var bossRoom in bossRooms)
        {
            if (!IsReachableFromStart(bossRoom))
            {
                errors.Add($"Boss room {bossRoom.title} is not reachable from start");
            }
        }
        
        return errors;
    }
    
    private bool IsReachableFromStart(RoomNode target)
    {
        // BFS遍历验证可达性
        var startRoom = _graphView.nodes.OfType<RoomNode>()
            .FirstOrDefault(r => r.RoomType == RoomType.Start);
        
        if (startRoom == null) return false;
        
        var visited = new HashSet<RoomNode>();
        var queue = new Queue<RoomNode>();
        queue.Enqueue(startRoom);
        
        while (queue.Count > 0)
        {
            var current = queue.Dequeue();
            if (current == target) return true;
            
            foreach (var edge in _graphView.edges)
            {
                if (edge.output.node == current)
                {
                    var nextRoom = edge.input.node as RoomNode;
                    if (nextRoom != null && !visited.Contains(nextRoom))
                    {
                        visited.Add(nextRoom);
                        queue.Enqueue(nextRoom);
                    }
                }
            }
        }
        
        return false;
    }
}

七、完整项目参考

  1. 官方示例
    Package Manager > GraphView Samples > State Machine

  2. 开源关卡编辑器
    Unity-Level-Editor
    核心功能:

    • 可视化节点编辑

    • 3D实时预览

    • 一键场景生成


八、总结与最佳实践

1. 性能优化建议

场景优化策略
大型关卡分区块加载/动态卸载
复杂节点虚拟化渲染/按需加载
实时预览LOD细节分级

2. 扩展方向

  • AI路径规划:集成A*算法可视化

  • 动态事件系统:基于节点的脚本触发器

  • 多人协作:实时同步编辑状态

通过GraphView构建的可视化关卡编辑器,可提升关卡设计效率3-5倍,特别适合复杂地牢、开放世界等场景。关键点在于平衡可视化编辑能力与运行时数据转换效率,建议结合ScriptableObject实现灵活的数据驱动架构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL解析——list的使用

目录 1.简介 2.构造函数 3.迭代器 3.1封装 3.2迭代器分类 4.排序性能 4.1链式与数组 4.2缓存读取 1.简介 STL容器中提供的list容器也是一种顺序容器&#xff0c;底层实现方式是带头双向链表&#xff0c;这种实现方式能比单链表更高效的访问数据。 下面围绕部分重要接口…

MQTTX连接阿里云的物联网配置

本文的目标是通过MQTTX的客户端&#xff0c;连接到阿里云的物联网的平台&#xff0c;发送温度信息&#xff0c;在阿里云的平台中显示出来。阿里云免费注册&#xff0c;免费有一个MQTT的服务器。有数量限制&#xff0c;但是对于测试来讲&#xff0c;已经足够。 1、注册阿里云的物…

20250606-C#知识:匿名函数、Lambda表达式与闭包

C#知识&#xff1a;匿名方法、Lambda表达式与闭包 闭包乍一听感觉很复杂&#xff0c;其实一点也不简单 1、匿名方法 没有方法名的方法一般用于委托和事件 Func<int, int, int> myAction delegate(int a, int b) { return a b; }; Console.WriteLine( myAction(1, 2)…

衡量嵌入向量的相似性的方法

衡量嵌入向量的相似性的方法 一、常见相似性计算方法对比 方法核心原理公式优点缺点适用场景余弦相似度计算向量夹角的余弦值,衡量方向相似性,与向量长度无关。$\text{cos}\theta = \frac{\mathbf{a} \cdot \mathbf{b}}{\mathbf{a}\mathbf{b}欧氏距离计算向量空间中的直线距离…

API是什么意思?如何实现开放API?

目录 一、API 是什么 &#xff08;一&#xff09;API 的定义 &#xff08;二&#xff09;API 的作用 二、API 的类型 &#xff08;一&#xff09;Web API 1. RESTful API 2. SOAP API &#xff08;二&#xff09;操作系统 API &#xff08;三&#xff09;数据库 API …

Python训练第四十六天

DAY 46 通道注意力(SE注意力) 知识点回顾&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插入的位置通…

第2天:认识LSTM

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: pytorch &#xff08;二&#xff09;具体步骤…

自动化提示生成框架(AutoPrompt)

自动化提示生成框架(AutoPrompt) 一、核心创新点 自动化提示生成框架(AutoPrompt) 创新本质:提出基于梯度引导搜索的自动化提示生成方法,替代人工设计模板的传统模式。技术路径: 将提示视为可训练的离散token序列,通过优化提示向量(prompt embedding)搜索语义空间。利…

中国首套1公里高分辨率大气湿度指数数据集(2003~2020)

时间分辨率&#xff1a;月空间分辨率&#xff1a;100m - 1km共享方式&#xff1a;开放获取数据大小&#xff1a;34.79 GB数据时间范围&#xff1a;2003-01-01 — 2020-12-31元数据更新时间&#xff1a;2023-07-26 数据集摘要 中国首套1公里高分辨率大气湿度指数数据集&#xf…

计算机视觉顶刊《International Journal of Computer Vision》2025年5月前沿热点可视化分析

追踪计算机视觉领域的前沿热点是把握技术发展方向、推动创新落地的关键&#xff0c;分析这些热点&#xff0c;不仅能洞察技术趋势&#xff0c;更能为科研选题和工程实践提供重要参考。本文对计算机视觉顶刊《International Journal of Computer Vision》2025年5月前沿热点进行了…

python学习打卡day45

DAY 45 Tensorboard使用介绍 知识点回顾&#xff1a; tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 效果展示如下&#xff0c;很适合拿去组会汇报撑页数&#xff1a; 作业&#xff1a;对resnet18在cifar10上采用微调策…

Verilog编程技巧01——如何编写三段式状态机

前言 Verilog编程技巧系列文章将聚焦于介绍Verilog的各种编程范式或者说技巧&#xff0c;编程技巧和编程规范有部分重合&#xff0c;但并非完全一样。规范更注重编码的格式&#xff0c;像变量命名、缩进、注释风格等&#xff0c;而编程技巧则更偏重更直观易读、更便于维护、综合…

智启未来:当知识库遇见莫奈的调色盘——API工作流重构企业服务美学

目录 引言 一、初识蓝耘元生代MaaS平台 1.1 平台架构 1.2 平台的优势 1.3 应用场景 二、手把手教你如何在蓝耘进行注册 &#xff08;1&#xff09;输入手机号&#xff0c;将验证码正确填入即可快速完成注册 &#xff08;2&#xff09;进入下面的页面表示已经成功注册&…

如何在 Windows 11 中永久更改默认浏览器:阻止 Edge 占据主导地位

在 Windows 11 中更改默认浏览器对于新手或技术不太熟练的用户来说可能会令人沮丧。 为什么要在 Windows 11 中更改默认浏览器? 这是一个重要的问题:你为什么要从 Microsoft Edge 切换过来? 生态系统集成:如果你已经在广泛使用 Google 服务,Chrome 可以提供无缝集成。同…

量子比特实现方式

经典计算机是通过电子电路运转起来的。使用硅制半导体制成的名为晶体管的小元件发挥了开关的作用&#xff0c;将其与金属布线组合起来即可实现逻辑门&#xff0c;再将逻辑门集成起来就能制造出经典计算机。量子计算机的制造过程则要复杂许多&#xff0c;因为量子计算机既需要量…

智慧水务发展迅猛:从物联网架构到AIoT系统的跨越式升级

AI大模型引领智慧水务迈入新纪元 2025年5月25日&#xff0c;水利部自主研发的“水利标准AI大模型”正式发布&#xff0c;它标志着水务行业智能化进程的重大突破。该模型集成1800余项水利标准、500余项法规及海量科研数据&#xff0c;支持立项、编制、审查等全流程智能管理&…

Java高级 | 【实验五】Spring boot+mybatis操作数据库

隶书文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…

在MATLAB中使用自定义的ROS2消息

简明结论&#xff1a; 无论ROS2节点和MATLAB运行在哪&#xff0c;MATLAB本机都必须拥有自定义消息源码并本地用ros2genmsg生成&#xff0c;才能在Simulink里订阅这些消息。只要你想让MATLAB或Simulink能识别自定义消息&#xff0c;必须把消息包源码(.msg等)拷到本机指定目录&a…

【MATLAB去噪算法】基于ICEEMDAN联合小波阈值去噪算法

ICEEMDAN联合小波阈值去噪算法相关文献 &#xff08;注&#xff1a;目前相关论文较少&#xff0c;应用该套代码可发直接一些水刊&#xff09; 一、CEEMDAN的局限性 模式残留噪声问题&#xff1a;原始CEEMDAN在计算每个IMF时直接对噪声扰动的信号进行模态分解并平均。 后果&a…

XXTEA,XTEA与TEA

TEA、XTEA和XXTEA都是分组加密算法&#xff0c;它们在设计、安全性、性能等方面存在显著区别。以下是它们的主要区别&#xff1a; 密钥长度 TEA&#xff1a;使用128位密钥。 XTEA&#xff1a;通常使用128位或256位密钥。 XXTEA&#xff1a;密钥长度更灵活&#xff0c;可以使用任…