【Unity3D编辑器开发】Unity3D编辑器开发基础性框架结构【全面总结】

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址
  • 我的个人博客

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

嗨,大家好,我是恬静的小魔龙

同学们国庆节好呀,放假了有没有好好学习呢。

最近学习了Unity3D编辑器方面的知识,在学习的过程中,发现一些比较容易混杂的点,特意总结了一下,方便自己和同学们学习,做了一份Unity3D编辑器开发脉络图,恳请敬请批评指正。

二、Unity3D编辑器开发

2-1、Unity3D编辑器开发脉络图

首先,放一张脉络图。
在这里插入图片描述
大图,可放大查看。

看着这张图,是不是感觉有些不知道从哪里开始看起好呢,接下来就来分析一下如何查看。

2-2、Unity3D编辑器开发分类

博主刚开始学习编辑器开发也是一脸懵的样子,为啥一会用OnGUI绘制窗口,一会用Editor绘制窗口,一会用EditorWindows绘制窗口,还有继承PropertyDrawer后进行属性绘制的。

它们之间有什么区别与联系呢。

  • 它们简单可以分成窗口绘制检视器绘制场景绘制属性绘制
  • 窗口绘制需要继承与EditorWindow类,然后在OnGUI里面进行窗口绘制。
  • 检视器绘制需要继承与Editor类,然后在OnInspectorGUI里面进行窗口绘制。
  • 场景绘制需要继承与Editor类,然后在OnSceneGUI里面进行绘制。
  • 属性绘制需要继承与PropertyDrawer类,然后在OnGUI里面进行绘制。
  • 还有一个检视器属性,这个单独来说。

这么一分析,是不是有些明了了,就是如果要绘制窗口的话,就需要继承EditorWindow类,然后在OnGUI里面进行窗口绘制。

重新绘制检视器窗口也就是Inspector窗口,或者场景Scene窗口就需要继承Editor类,然后在OnInspectorGUI里面进行Inspector窗口绘制,在OnSceneGUI里面进行Scene窗口绘制。

2-3、检视器属性

2-3-1、HideInInspector

介绍:可以隐藏公共成员变量,防止Inspector的值影响到他,同时保证脚本中变量的可访问度。

举个例子:

不加[HideInInspector]

using UnityEngine;public class Test01 : MonoBehaviour
{public string Name;//注意这是public访问权限
}

在这里插入图片描述

加[HideInInspector]

using UnityEngine;public class Test01 : MonoBehaviour
{[HideInInspector]public string Name;//注意这是public访问权限
}

在这里插入图片描述

2-3-2、SerializeField

介绍:将私有变量设置为检视面板可见可修改,Unity会将对象进行序列化存储,即使是私有的,标记为可序列化后也会显示,公有变量默认是可序列化的。

举个例子:

不加[SerializeField]

using UnityEngine;public class Test01 : MonoBehaviour
{private string Name;
}

在这里插入图片描述

加[SerializeField]

using UnityEngine;public class Test01 : MonoBehaviour
{[SerializeField]private string Name;
}

在这里插入图片描述

2-3-3、Space

介绍:在当前成员变量上方留 50 像素空白区域

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[Space]public string Name;
}

在这里插入图片描述

2-3-4、Header

介绍:在当前成员变量上方加入一个标题文字

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[Header("标题")]public string Name;
}

在这里插入图片描述

2-3-5、Tooltip

介绍:添加变量悬浮提示,当鼠标放入后会有提示

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[Tooltip("输入名字")]public string Name;
}

在这里插入图片描述

2-3-6、Range

介绍:给数值设定范围。

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[Range(0,10)]public int Age;
}

在这里插入图片描述

2-3-7、Multiline

介绍:指定输入行字符,参数为行数。

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[Multiline(5)]public string Name;
}

在这里插入图片描述

2-3-8、TextArea

介绍:设置默认显示 5 行,最多显示 10 行内容,再多用滚动条控制显示。

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[TextArea(5,10)]//(最小行数,最大行数)public string Name;
}

在这里插入图片描述

2-3-9、ContextMenu

介绍:在小齿轮中添加一个回调函数,参数为函数名称,用于调用该特性标记的方法。

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[ContextMenu("CallBack")]public void CallBackFun(){Debug.Log("回调函数");}
}

在这里插入图片描述
在这里插入图片描述

2-3-10、ContextMenuItem

介绍:给一个变量添加右键菜单,第一个参数是菜单名称,第二个参数是回调函数。

举个例子:

using UnityEngine;public class Test01 : MonoBehaviour
{[ContextMenuItem("点击调用函数", "CallBackFun")]public string Name;public void CallBackFun(){Debug.Log("回调函数");}
}

在这里插入图片描述

在这里插入图片描述

2-3-11、AddComponentMenu

介绍:在编辑器添加一个用于添加组件的菜单项,将拥有该属性的脚本添加到选中的物体上。第一个参数:分类名/组件名,第二个参数:列表中显示的顺序。

举个例子:

using UnityEngine;[AddComponentMenu("点击添加组件函数")]
public class Test01 : MonoBehaviour
{
}

在这里插入图片描述

2-3-12、ExecuteInEditMode

介绍:使生命周期函数,在编辑器状态下可以执行,游戏中也可以正常使用,Update()在场景中对象发生变化或项目组织发生变化时会在编辑器下执行。也就是说,不在运行状态,也可以运行Start、Awake函数。

举个例子:

using UnityEngine;[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{private void Awake(){Debug.Log("Awake");}private void Start(){Debug.Log("Start");}private void Update(){Debug.Log("Update");}
}

在这里插入图片描述

2-3-13、RequireComponent

介绍:依赖、绑定,作用是,当我们把一个Script绑定到GameObject上时会同时把需要依赖的脚本也一起绑定(添加)上去。

举个例子:

using UnityEngine;[RequireComponent(typeof(Rigidbody))]
public class Test01 : MonoBehaviour
{
}

在这里插入图片描述

2-3-14、CanEditMultipleObjects

介绍:告诉 Unity 可以使用此编辑器来选择多个对象并同时更改所有对象。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;public class Test01 : MonoBehaviour
{public int m_MyInt = 75;public Vector3 m_MyVector = new Vector3(20, 1, 0);public GameObject m_MyGameObject;
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{SerializedProperty m_IntProp;SerializedProperty m_VectorProp;SerializedProperty m_GameObjectProp;void OnEnable(){m_IntProp = serializedObject.FindProperty("m_MyInt");m_VectorProp = serializedObject.FindProperty("m_MyVector");m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");}public override void OnInspectorGUI(){EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));serializedObject.ApplyModifiedProperties();}
}

这时候,给多个对象挂载Test01脚本,然后选中多个对象修改脚本,就会显示Multi-object editing not supported.不支持多对象编辑
在这里插入图片描述
这时候修改Test01Editor.cs脚本:

using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{SerializedProperty m_IntProp;SerializedProperty m_VectorProp;SerializedProperty m_GameObjectProp;void OnEnable(){m_IntProp = serializedObject.FindProperty("m_MyInt");m_VectorProp = serializedObject.FindProperty("m_MyVector");m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");}public override void OnInspectorGUI(){EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));serializedObject.ApplyModifiedProperties();}
}

就可以同时编辑多个对象了:
在这里插入图片描述

2-3-15、MenuItem

介绍:在顶部显示"工具"菜单。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEditor;
using UnityEngine;public class Test01 : MonoBehaviour
{[MenuItem("Test/顶部菜单")]public static void CallBackFun(){}
}

在这里插入图片描述

2-3-16、CustomEditor

介绍:自定义编辑器,可以修改所关联组件检视面板的属性,然后重新绘制,Editor目录下建立编辑器脚本,将编辑器脚本与原始脚本关联。

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;public class Test01 : MonoBehaviour
{public int m_MyInt = 75;public Vector3 m_MyVector = new Vector3(20, 1, 0);public GameObject m_MyGameObject;
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{SerializedProperty m_IntProp;SerializedProperty m_VectorProp;SerializedProperty m_GameObjectProp;void OnEnable(){m_IntProp = serializedObject.FindProperty("m_MyInt");m_VectorProp = serializedObject.FindProperty("m_MyVector");m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");}// 用于重新绘制Inspector面板中的属性public override void OnInspectorGUI(){// 设置高度EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));serializedObject.ApplyModifiedProperties();}
}

在这里插入图片描述

2-4、窗口绘制

2-4-1、使用窗口绘制

好!到这里暂停一下,回想一下,还记得前文说的窗口绘制要继承什么类吗?




窗口绘制需要继承与EditorWindow类,然后在OnGUI里面进行窗口绘制。

继承EditorWindow类需要将脚本放到Editor脚本中,才能生效。

让我们在Editor文件夹中,新建Test02EditorWindow.cs脚本,编辑代码:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;public class Test02EditorWindow : EditorWindow
{[MenuItem("工具/创建窗口")]static void OpenWindow(){//泛型T 窗口类型。必须派生自 EditorWindow。//第一个参数设置为 true 可创建浮动实用程序窗口,设置为 false 可创建正常窗口。//第三个参数设置是否为窗口提供焦点(如果已存在)。Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);window.minSize = new Vector2(40, 30);window.minSize = new Vector2(80, 60);}//开窗口调用private void OnEnable(){Debug.Log("enable");}//关窗口调用private void OnDisable(){Debug.Log("disable");}//窗口开启就调用private void Update(){Debug.Log("update");}//用于绘制窗口内容private void OnGUI(){if (GUILayout.Button("测试点击")){Debug.Log("测试点击");}}//场景结构发生变化,执行回调函数private void OnHierarchyChange(){Debug.Log("hierarchy");}//项目结构发生变化,执行回调函数private void OnProjectChange(){Debug.Log("project");}//选中物体发生变化,执行回调函数private void OnSelectionChange(){//获取当前选中的物体的名称Debug.Log(Selection.activeGameObject.name);}
}

编辑器编译通过后,在编辑器的菜单栏找到工具→创建窗口
在这里插入图片描述
这个就是渲染出来的窗口了:
在这里插入图片描述
绘制就在OnGUI里面,绘制的UI也是OnGUI支持的UI就可以了。

具体OnGUI怎么用,这里就不再赘述了。

这里再分享一些,常用的小功能代码。

2-4-2、检查开启mipmap的非2的幂贴图
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;public class Test02EditorWindow : EditorWindow
{[MenuItem("工具/检查开启mipmap的非2的幂贴图")]static void OpenWindow(){Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);}//用于绘制窗口内容private void OnGUI(){if (GUILayout.Button("检查开启mipmap的非2的幂贴图")){CheckNPOT();}}private void CheckNPOT(){List<string> files = AssetDatabase.FindAssets("t:Texture").Select(AssetDatabase.GUIDToAssetPath).ToList();List<string> outputList = new List<string>();foreach (var file in files){TextureImporter textureImporter = AssetImporter.GetAtPath(file) as TextureImporter;if (textureImporter){//贴图为Sprite或设置了2的幂scaleif (textureImporter.textureType == TextureImporterType.Sprite || textureImporter.npotScale != TextureImporterNPOTScale.None){continue;}//贴图长宽均为2的幂textureImporter.GetSourceTextureWidthAndHeight(out var width, out var height);if (IsPowerOfTwo(width) && IsPowerOfTwo(height)){continue;}if (textureImporter.mipmapEnabled){outputList.Add(file);Debug.Log(file);}}}WriteLog("NPOT.log", outputList);}private void WriteLog(string fileName, List<string> outputList){if (!Directory.Exists(@"Logs")){Directory.CreateDirectory(@"Logs");}if (!File.Exists("Logs/" + fileName)){using (FileStream fs = new FileStream("Logs/" + fileName, FileMode.CreateNew)){}}File.WriteAllLines("Logs/" + fileName, outputList);}private bool IsPowerOfTwo(int value){return (value & (value - 1)) == 0;}
}
2-4-3、获取选中文件夹下的所有资源
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;public class Test02EditorWindow : EditorWindow
{[MenuItem("工具/获取选中文件夹下的所有资源")]static void OpenWindow(){Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "获取选中文件夹下的所有资源", true);}//用于绘制窗口内容private void OnGUI(){if (GUILayout.Button("获取选中文件夹下的所有资源")){List<string> pathList = new List<string>();Object[] m_objects = Selection.GetFiltered(typeof(Object), SelectionMode.Unfiltered | SelectionMode.DeepAssets);foreach (var obj in m_objects){string path = AssetDatabase.GetAssetPath(obj);if (!pathList.Contains(path)){pathList.Add(path);}}foreach (var item in pathList){Debug.Log(item);}}}
}
2-4-4、删除prefab中missing的script
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;public class Test02EditorWindow : EditorWindow
{[MenuItem("工具/删除prefab中missing的script")]static void OpenWindow(){Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "删除prefab中missing的script", true);}//用于绘制窗口内容private void OnGUI(){if (GUILayout.Button("删除prefab中missing的script")){List<string> logList = new List<string>();List<string> prefabPathList = new List<string>();foreach (var prefabPath in prefabPathList){if (EditorUtility.DisplayCancelableProgressBar("Processing", string.Format("{0} {1}/{2}",prefabPath, prefabPathList.IndexOf(prefabPath), prefabPathList.Count),prefabPathList.IndexOf(prefabPath) / (float)prefabPathList.Count)){EditorUtility.ClearProgressBar();return;}GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);if (go){int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);if (count > 0){GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go);logList.Add(string.Format("删除了{0}中的{1}个missing的script", prefabPath, count));}}}EditorUtility.ClearProgressBar();}}
}

未完待续,欢迎补充。。。

2-5、检视器Inspector绘制

将普通的类关联Editor工具,实现特殊功能。

比如一个Test01.cs类是普通类,在Editor文件夹内新建一个Test01Editor.cs编辑类,Test01Editor.cs就是一个自定义编辑器,然后在Test01Editor.cs的OnInspectorGUI函数内进行属性的修改绘制。

接下来,一个老例子来说明一下如何使用:

举个例子:

新建脚本Test01.cs编辑代码:

using UnityEngine;public class Test01 : MonoBehaviour
{public int m_MyInt = 75;public Vector3 m_MyVector = new Vector3(20, 1, 0);public GameObject m_MyGameObject;
}

默认是这样的:

在这里插入图片描述

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;//CustomEditor 属性告知 Unity 应该作为哪个组件的编辑器。
[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{SerializedProperty m_IntProp;SerializedProperty m_VectorProp;SerializedProperty m_GameObjectProp;void OnEnable(){m_IntProp = serializedObject.FindProperty("m_MyInt");m_VectorProp = serializedObject.FindProperty("m_MyVector");m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");}// 用于重新绘制Inspector面板中的属性public override void OnInspectorGUI(){// 设置高度EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));serializedObject.ApplyModifiedProperties();}
}

修改后是这样的:
在这里插入图片描述

2-6、场景Scene绘制

OnSceneGUI的运行方式很像 OnInspectorGUI,只不过在 Scene 视图中运行而已。

为了方便创建自己的编辑控件,可以使用在 Handles 类中定义的函数。

其中的所有函数都是为 3D 模式的 Scene 视图设计的。

举个例子:
新建脚本Test01.cs编辑代码:

using UnityEditor;
using UnityEngine;[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{public Vector3 lookAtPoint = Vector3.zero;public void Update(){transform.LookAt(lookAtPoint);}
}

在Project视图中,Script文件夹内,新建Editor文件夹,在这个文件夹新建Test01Editor.cs脚本,编辑代码:

using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{SerializedProperty lookAtPoint;void OnEnable(){lookAtPoint = serializedObject.FindProperty("lookAtPoint");}public void OnSceneGUI(){var t = (target as Test01);EditorGUI.BeginChangeCheck();Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);if (EditorGUI.EndChangeCheck()){Undo.RecordObject(target, "Move point");t.lookAtPoint = pos;t.Update();}}
}

在这里插入图片描述

2-7、属性Property绘制

用于从中派生自定义属性绘制器的基类。使用此基类可为您自己的 Serializable 类或者具有自定义 PropertyAttribute 的脚本变量创建自定义绘制器。

PropertyDrawer 有两种用途: 自定义 Serializable 类的每个实例的 GUI。 自定义具有自定义 PropertyAttribute 的脚本成员的 GUI。 如果您有自定义的 Serializable 类,可以使用 PropertyDrawer 来控制它在 Inspector 中的外观。

举个例子:
新建脚本Recipe.cs,编辑代码:

using System;
using UnityEngine;public enum IngredientUnit { Spoon,Cup,Bowl,Piece}[Serializable]
public class Ingredient
{public string name;public int amount = 1;public IngredientUnit unit;
}public class Recipe : MonoBehaviour
{public Ingredient potionResult;public Ingredient[] pointIngredients;
}

在Editor文件夹内新建脚本IngredientDrawerUIE.cs,编辑代码:

using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawerUIE : PropertyDrawer
{public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){EditorGUI.BeginProperty(position, label, property);// labelposition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);// 控制字段缩进 设置为不缩进var indent = EditorGUI.indentLevel;EditorGUI.indentLevel = 0;// 计算矩形范围var nameRect = new Rect(position.x, position.y, 30, position.height); var amountRect = new Rect(position.x + 35, position.y, 50, position.height);var unitRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);// 绘制字段EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);// 控制字段缩进 设置为原来的数值EditorGUI.indentLevel = indent;EditorGUI.EndProperty();}
}

将Recipe脚本添加到对象上查看效果:
在这里插入图片描述

2-8、参考链接

Unity学习笔记之编辑器开发

Unity编辑器工具开发经验总结

自定义编辑器

属性绘制器

三、后记

OK。总结一下。

Unity3D编辑器开发,就是基于Unity3D编辑器,做一些帮助开发的小工具。

在这里可以分成检视器属性界面绘制属性绘制三个大方面。

检视器属性参考2-3小节。

界面绘制可以分为窗口绘制检视器界面绘制场景绘制

窗口绘制的话就需要继承EditorWindow类,然后在OnGUI里面渲染UI。

检视器界面绘制需要继承Editor类,然后在OnInspectorGUI里面进行绘制。

场景绘制需要继承Editor类,然后在OnSceneGUI里面进行绘制。

属性绘制需要继承PropertyDrawer类,然后在OnGUI里面进行绘制。

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

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

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

相关文章

Spring MVC 中的国际化和本地化

Spring MVC 中的国际化和本地化 国际化&#xff08;Internationalization&#xff0c;简称i18n&#xff09;和本地化&#xff08;Localization&#xff0c;简称l10n&#xff09;是构建多语言应用程序的重要概念。Spring MVC提供了丰富的支持&#xff0c;使开发人员能够轻松地处…

Spark基础

一、spark基础 1、为什么使用Spark Ⅰ、MapReduce编程模型的局限性 (1) 繁杂 只有Map和Reduce两个操作&#xff0c;复杂的逻辑需要大量的样板代码 (2) 处理效率低 Map中间结果写磁盘&#xff0c;Reduce写HDFS&#xff0c;多个Map通过HDFS交换数据 任务调度与启动开销大 (…

前后端通信到底是怎样一个过程

前后端通信是怎样 前言&#xff1a;Http协议 超文本传输协议 规定&#xff1a;每一次前后端通信&#xff0c;前端需要主动向后端发出请求&#xff0c;后端接收到前端的请求后&#xff0c;可以给出响应 1、Http报文 浏览器向服务器发送请求时&#xff0c;请求本身就是信息&…

【gcc】RtpTransportControllerSend学习笔记 1

本文是woder大神 的文章的学习笔记。主要是大神文章: webrtc源码分析(8)-拥塞控制(上)-码率预估 的学习笔记。大神的webrtc源码分析(8)-拥塞控制(上)-码率预估 详尽而具体,堪称神作。因为直接看大神的文章,自己啥也没记住,所以同时跟着看代码。跟着大神走一遍,不求甚解,…

踩坑日记 uniapp 底部 tabber遮挡住购物车结算

tabbar 被购物车结算遮挡 在小程序上tabbar没有将固定栏遮挡&#xff0c;如果直接调高&#xff0c;浏览器H5页面是对了&#xff0c;但在小程序上面离底部的定位就太高了 原代码 // 底部结算样式.shop-foot {border-top: 2rpx solid #F7F7F7;background-color: #FFF;position: …

机器学习 不均衡数据采样方法:imblearn 库的使用

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

【Java接口性能优化】skywalking使用

skywalking使用 提示&#xff1a;微服务中-skywalking使用 文章目录 skywalking使用一、进入skywalking主页二、进入具体服务1.查看接口 一、进入skywalking主页 二、进入具体服务 可以点击列表或搜索后&#xff0c;点击进入具体服务 依次选择日期、小时、分钟 1.查看接口 依次…

Linux工具(三)

继Linux工具&#xff08;一&#xff09;和Linux工具&#xff08;二&#xff09;&#xff0c;下面我们就来讲解Linux最后的两个工具&#xff0c;分别是代码托管的版本控制器git和代码调试器gdb。 目录 1.git-版本控制器 从0到1的实现git代码托管 检测并安装git 新建git仓库…

Harmony跨进程通信—IPC与RPC通信开发指导

一、IPC与RPC通信概述 基本概念 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&a…

腾讯云中使用ubuntu安装属于自己的overleaf

在自己的云服务器上安装overleaf的需求是从写论文开始的&#xff0c;总担心自己的论文放在一个网站上被泄露&#xff0c;所以想要在自己的服务器上安装自己的overleaf&#xff0c;正好手边有一个云服务器&#xff0c;现在开始。 配置腾讯云 因为使用overleaf的优势就是在不同…

基于Java的新能源汽车在线租赁平台设计与实现(源码+lw+ppt+部署文档+视频讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

Cesium展示——wkt 数据绘制

文章目录 需求分析1. 第一步,数据类型转换2. 第二步,数据渲染需求 WKT 是什么:WKT 简介 在这里,我选择将 Cesium 中将wkt数据转化为geoJSON格式后渲染至地球上 分析 1. 第一步,数据类型转换 npm install terraformer-wkt-parser --savelet wkts =

《 新手》web前端(axios)后端(java-springboot)对接简解

文章目录 <font color red>1.何为前后端对接?2.对接中关于http的关键点2.1. 请求方法2.2. 请求参数设置简解&#xff1a; 3.对接中的跨域(CROS)问题**为什么后端处理跨域尽量在业务之前进行&#xff1f;**3.总结 1.何为前后端对接? “前后端对接” 是指前端和后端两个…

ffmpeg ts 关于av_seek_frame

1 ffmpeg命令行 一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现&#xff0c;比如 ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4 其中 -ss 放置较前 开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 &#xff08;此描述…

【go语言】方法

go的方法是一种作用在接收者&#xff08;某种类型的变量&#xff0c;不能是接口和指针&#xff09;上的特殊函数 方法的声明 // 类型方法接收者是值类型 func (t TypeName) MethodName (ParamList ) (Returnlist) {//method body }// 类型方法接收者是指针 func (t *TypeName…

【Vue面试题十】、Vue中组件和插件有什么区别?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;Vue中组件和插件有什么区…

大数据-玩转数据-Flink 海量数据实时去重

一、海量数据实时去重说明 借助redis的Set&#xff0c;需要频繁连接Redis&#xff0c;如果数据量过大, 对redis的内存也是一种压力&#xff1b;使用Flink的MapState&#xff0c;如果数据量过大, 状态后端最好选择 RocksDBStateBackend&#xff1b; 使用布隆过滤器&#xff0c;…

mysql八股

1、请你说说mysql索引&#xff0c;以及它们的好处和坏处 检索效率、存储资源、索引 索引就像指向表行的指针&#xff0c;是一个允许查询操作快速确定哪些行符合WHERE子句中的条件&#xff0c;并检索到这些行的其他列值的数据结构索引主要有普通索引、唯一索引、主键索引、外键…

攻防世界-T1 Training-WWW-Robots

文章目录 步骤1步骤二结束语 步骤1 看到文本——>提取有效信息——>利用有效信息 文本&#xff1a;In this little training challenge, you are going to learn about the Robots_exclusion_standard. The robots.txt file is used by web crawlers to check if they …

策略模式与模板方法结合案例

一、背景 上周在迁移项目MQ工程的时候&#xff0c;重新Review代码&#xff0c;发现有一段代码综合使用了策略模式和模板方法&#xff0c;下面讲解一下具体场景应用的思路。 二、模板方法 策略模式前段时间有一个关于库存具体案例&#xff0c;详见 库存管理与策略模式。 模板…