当前位置: 首页 > news >正文

Unity编辑器拓展-Odin

1.相比于原生Unity的优势

  • Unity不支持泛型类型序列化,例如字典原生Unity不支持序列化,而Odin可以继承序列化的Mono实现
  • 功能强大且使用简单,原生Unity想实现一些常见的功能需要额外自己编写Unity扩展的编码,实现功能只需要加一个特性即可Odin帮忙写好了内部管理和实现
  • 编辑器的窗口实现简单且美观

2.常用功能代码总结

通过Tools-Odin Inspector-Attribute Overview即可打开一个预览各个特性的效果的窗口,可供参考

OdinValueDrawer

类继承自OdinValueDrawer,其中T为自定义数据类型,之后重写DrawPropertyLayout方法,实现自定义绘制,实现了对于我们自定义数据类型的自定义绘制,重写Initialize来做对其下序列化部分的初始化,例如Selector的生成,选择变化的事件监听,初始值的定义。同时在DrawPropertyLayout中定义对应的按钮,在按下的时候用selector.ShowInPopup();打开对应的selector

#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos
{using UnityEngine;using System;#if UNITY_EDITORusing Sirenix.OdinInspector.Editor;using UnityEditor;using Sirenix.Utilities;#endif// 演示如何为自定义类型生成自定义drawer的示例。[TypeInfoBox("此示例演示如何为自定义结构或类实现自定义drawer")]public class CustomDrawerExample : MonoBehaviour{public MyStruct MyStruct;[ShowInInspector]public static float labelWidth = 10;}// 自定义数据结构,用于演示。[Serializable]public struct MyStruct{public float X;public float Y;}#if UNITY_EDITORpublic class CustomStructDrawer : OdinValueDrawer<MyStruct>{protected override void DrawPropertyLayout(GUIContent label){//获取我们绘制类的值MyStruct value = this.ValueEntry.SmartValue;//获取要绘制的区域(rect)var rect = EditorGUILayout.GetControlRect();//在Odin中,标签是可选项,可以为空,所以我们必须考虑到这一点。if (label != null){rect = EditorGUI.PrefixLabel(rect, label);}//保存原始labelWidth的宽度,此label为struct中对应的X,Yvar prev = EditorGUIUtility.labelWidth;//设定新的label宽度EditorGUIUtility.labelWidth = CustomDrawerExample.labelWidth;//根据slider对应的值进行赋值value.X = EditorGUI.Slider(rect.AlignLeft(rect.width * 0.5f), "X", value.X, 0, 1);value.Y = EditorGUI.Slider(rect.AlignRight(rect.width * 0.5f), "Y", value.Y, 0, 1);//恢复设定原始label宽度EditorGUIUtility.labelWidth = prev;//将新的Struct赋值给我们定义的MyStructthis.ValueEntry.SmartValue = value;}}
#endif
}
#endif

OdinSelector

一个选择框,可以选择提供的列表,同时选择后触发对应的事件

BuildSelectionTree

BuildSelectionTree的作用是在OdinSelector中构建一个选择树,也就是显示在弹出窗口中的树形结构的列表。你可以通过重写这个方法,来决定选择树中包含哪些值,以及它们的层级关系和排序方式。

protected override void BuildSelectionTree(OdinMenuTree tree)  
{  
//搜索框tree.Config.AutoFocusSearchBar = true;  tree.Config.DrawSearchToolbar = true;  tree.Selection.SupportsMultiSelect = false;  tree.MenuItems.Clear();  foreach (结合excel枚举对应的pb)  {      tree.Add(path, instance);}}

在外部进行的初始化

例如在上述的Drawer的Init中进行初始化,需要new出这个类,并且增加监听事件,且设置初始值。同时监听外部按钮,打开这个selector

m_WeaponIDSelector = new WeaponIDSelector();  
m_WeaponIDSelector.SelectionTree.UpdateMenuTree();  
m_WeaponIDSelector.SetSelection(WeaponData.weaponKey.thingID); //初始值 
m_WeaponIDSelector.SelectionChanged += OnWeaponThingIDChanged;//改变时候监听
// 重写Initialize方法
protected override void Initialize()
{
// 调用基类的Initialize方法
base.Initialize();// 实例化OdinMenuTree对象
tree = new OdinMenuTree();// 添加一些菜单项到选择树中,例如一些GameObject或者其他自定义对象
tree.Add("Cube", GameObject.CreatePrimitive(PrimitiveType.Cube));
tree.Add("Sphere", GameObject.CreatePrimitive(PrimitiveType.Sphere));
tree.Add("MyObject", new MyObject());// 设置选择树的配置,例如是否支持多选,是否显示搜索栏等
tree.Config.DrawSearchToolbar = true;
tree.Config.MultiSelect = true;// 设置选择树的事件,例如当菜单项被选中或者双击时执行一些操作
tree.Selection.SelectionChanged += OnSelectionChanged;
tree.MenuItems.OnDoubleClick += OnDoubleClick;// 在Initialize的时候调用UpdateMenuTree方法,以便初始化选择树中的菜单项,以及处理搜索和排序等功能
tree.UpdateMenuTree();
}

在外部按钮按下进行的显示这个selector

//FocusType是一个枚举类型,用于表示按钮是否可以通过键盘选择。FocusType有三个可能的值:Passive、Keyboard和Native.Passive表示按钮不会接收键盘焦点,只能通过鼠标点击选择;Keyboard表示按钮可以通过Tab键或方向键在其他控件之间切换焦点;Native表示按钮使用平台本地的焦点行为,例如在Mac上使用Option+Tab键切换焦点.在你的代码中,FocusType.Passive表示你的下拉按钮不需要通过键盘选择,只能通过鼠标点击打开下拉菜单。
if (EditorGUILayout.DropdownButton(new GUIContent("武器选择: " + m_WeaponIDSelector.CurSelectName), FocusType.Passive))  
{  m_WeaponIDSelector.RebuildSelectionTree();  m_WeaponIDSelector.EnsureSingleClickToSelect();  m_WeaponIDSelector.ShowInPopup();  
}
public void RebuildSelectionTree()  
{  List<uint> curSelections = GetCurrentSelection().ToList();  BuildSelectionTree(SelectionTree);  EnableIDChangeEvent = false;  SetSelection(curSelections);  EnableIDChangeEvent = true;  SelectionTree.Config.AutoFocusSearchBar = true;  
}
public void EnsureSingleClickToSelect()  
{  
//设置选择树的Config.SelectionConfirmNavigationKey属性为KeyCode.None,这样就可以取消双击确认选择的功能EnableSingleClickToSelect();  //设置了SelectionTree.Config.ConfirmSelectionOnDoubleClick属性为false,这个属性也是用于控制是否需要双击确认选择的功能虽然这个属性和Config.SelectionConfirmNavigationKey属性有重复的作用,但是为了保险起见,最好都设置一下。SelectionTree.Config.ConfirmSelectionOnDoubleClick = false;  //设置了SelectionTree.Config.AutoFocusSearchBar属性为true,这个属性可以让选择树在打开时自动聚焦搜索栏,方便用户输入搜索关键词SelectionTree.Config.AutoFocusSearchBar = true;  
}

处理Selector的改变

这里需要注意的是要用var first = col.FirstOrDefault(); 来取,并且需要做判空,一般是取完keyID之后再去读表刷上其他的数据,或者是根据

selector.SelectionChanged += col => //添加SelectionChanged事件的处理方法
{
var first = col.FirstOrDefault(); //获取第一个选择的对象
if (first != null) //如果不为空
{
selectedName = first.name; //更新selectedName为对象的名称
}

缩进级别

  • GUIHelper.PushIndentLevel(1);
  • GUIHelper.PopIndentLevel();
    结合使用,增加一个缩进级别,可以让GUI的元素更加有层次感和结构化。例如,你可以用这个方法来创建一个树形的菜单或列表。

结构化API

  • EditorGUILayout.BeginHorizontal();这两个老朋友了就不解释了
  • EditorGUILayout.BeginVertical();
  • GUIHelper.PushGUIEnabled(bool);//表示接下来的一个范围内我要检查一下这个bool值来觉得这些UI是否可以被选
  • GUIHelper.PopGUIEnabled();
//GUIHelper.PushGUIEnabled和GUIHelper.PopGUIEnabled是两个用于控制GUI元素是否可用的方法。它们的作用是在一段代码中临时改变GUI元素的可用状态,而不影响其他代码中的GUI元素。具体来说,GUIHelper.PushGUIEnabled方法可以接受一个布尔值作为参数,表示要设置的GUI元素的可用状态。这个方法会将当前的GUI元素的可用状态压入一个栈中,然后设置新的可用状态。GUIHelper.PopGUIEnabled方法则会从栈中弹出之前保存的GUI元素的可用状态,并恢复它。这样就可以在一段代码中临时禁用或启用一些GUI元素,而不影响其他代码中的GUI元素。
GUIHelper.PushGUIEnabled(toggle); //根据复选框的状态设置按钮的可用状态
if (GUILayout.Button("Click Me")) //绘制按钮会受到是否可选的影响
{
Debug.Log("You clicked the button!"); //打印日志
}
GUIHelper.PopGUIEnabled(); //恢复之前的可用状态

补充

可以通过UnityToolbarExtender来打开目标窗口,使用起来也非常简单

static ToolbarGUIUtility()  
{  uxToolPrefabOpen = EditorPrefs.GetBool("UXToolPrefabOpen", false);  uxToolPowerOpen = EditorPrefs.GetBool("UXToolPowerOpen", false);  ToolbarExtender.LeftToolbarGUI.Add(OnLeftToolbarGUI);  ToolbarExtender.RightToolbarGUI.Add(OnRightToolbarGUI);  
}
private static GUIStyle ToolbarButtonLeftStyle  
{  get  {  if (toolbarButtonLeftStyle == null) toolbarButtonLeftStyle = new GUIStyle("toolbarbuttonLeft");  return toolbarButtonLeftStyle;  }}
static void OnLeftToolbarGUI()  
{  if (TestCommon.TestHelper.ColorButton(new GUIContent("XXX配置", "XXXToolTip"), new Color(0.55f, 0.37f, 1f), GUILayoutOptions.Width(90), ToolbarButtonLeftStyle))  {        TestWindow.OpenWindow();  }

快速创建Button的管理者类

#if UNITY_EDITOR  
using System;  
using System.Collections.Generic;  
using System.Reflection;  
using Sirenix.Utilities;  
using Sirenix.Utilities.Editor;  
using UnityEditor;  
using UnityEngine;  namespace TestCommon
{  public static class TestHelper{  private static GUIStyle ColoredButtonStyle = new GUIStyle(GUI.skin.button);  public static bool ColorButton(string text, Color color, int width, int height, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  Rect buttonRect = GUILayoutUtility.GetRect(width, height);  bool res = guiStyle != null ? GUI.Button(buttonRect, "", guiStyle) : GUI.Button(buttonRect, "");  GUIHelper.PopColor();  GUI.Label(buttonRect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(string text, Color color, GUILayoutOption[] guiLayoutOptions, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  GUIHelper.PushContentColor(Color.clear);  bool res = guiStyle != null ? GUILayout.Button(text, guiStyle, guiLayoutOptions) : GUILayout.Button(text, guiLayoutOptions);  GUIHelper.PopContentColor();  GUIHelper.PopColor();  Rect buttonRect = GUILayoutUtility.GetLastRect();  GUI.Label(buttonRect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(GUIContent guiContent, Color color, GUILayoutOption[] guiLayoutOptions, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  GUIHelper.PushContentColor(Color.clear);  bool res = guiStyle != null ? GUILayout.Button(guiContent, guiStyle, guiLayoutOptions) : GUILayout.Button(guiContent, guiLayoutOptions);  GUIHelper.PopContentColor();  GUIHelper.PopColor();  Rect buttonRect = GUILayoutUtility.GetLastRect();  GUI.Label(buttonRect, guiContent.text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(Rect rect, string text, Color color, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            GUIHelper.PushColor(color);  bool res = guiStyle != null ? GUI.Button(rect, "", guiStyle) : GUI.Button(rect, "");  GUIHelper.PopColor();  GUI.Label(rect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(Rect rect, Texture2D texture2D, Color color, GUIStyle guiStyle = null)  {            GUIHelper.PushColor(color);  bool res = guiStyle != null ? GUI.Button(rect, texture2D, guiStyle) : GUI.Button(rect, texture2D);  GUIHelper.PopColor();  return res;  }  public static void RichLabel(Rect rect, string text, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null)  {            string richText = text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  if (guiStyle != null)  GUI.Label(rect, richText, guiStyle);  else  GUI.Label(rect, richText);  }  public static void RichLabel(Rect rect, GUIContent guiContent, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null)  {            string richText = guiContent.text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  guiContent.text = richText;  if (guiStyle != null)  GUI.Label(rect, guiContent, guiStyle);  else  GUI.Label(rect, guiContent);  }  public static void RichLabel(string text, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null, GUILayoutOption[] guiLayoutOptions = null)  {            string richText = text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  if (guiStyle != null)  {                if (guiLayoutOptions != null)  GUILayout.Label(richText, guiStyle, guiLayoutOptions);  else  GUILayout.Label(richText, guiStyle);  }            else  {  if (guiLayoutOptions != null)  GUILayout.Label(richText, guiLayoutOptions);  else  GUILayout.Label(richText);  }        }  public static void RichLabel(GUIContent guiContent, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null, GUILayoutOption[] guiLayoutOptions = null)  {            string richText = guiContent.text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  guiContent.text = richText;  if (guiStyle != null)  {                if (guiLayoutOptions != null)  GUILayout.Label(guiContent, guiStyle, guiLayoutOptions);  else  GUILayout.Label(guiContent, guiStyle);  }            else  {  if (guiLayoutOptions != null)  GUILayout.Label(guiContent, guiLayoutOptions);  else  GUILayout.Label(guiContent);  }        }  public enum Alignment  {  UpperLeft,  UpperCenter,  UpperRight,  MiddleLeft,  MiddleCenter,  MiddleRight,  BottomLeft,  BottomCenter,  BottomRight,  }  public static bool IsUpper(this Alignment alignment)  {            return alignment == Alignment.UpperRight || alignment == Alignment.MiddleRight || alignment == Alignment.BottomRight;  }  public static bool IsMiddle(this Alignment alignment)  {            return alignment == Alignment.MiddleLeft || alignment == Alignment.MiddleCenter || alignment == Alignment.MiddleRight;  }  public static bool IsBottom(this Alignment alignment)  {            return alignment == Alignment.BottomLeft || alignment == Alignment.BottomCenter || alignment == Alignment.BottomRight;  }  public static bool IsLeft(this Alignment alignment)  {            return alignment == Alignment.UpperLeft || alignment == Alignment.MiddleLeft || alignment == Alignment.BottomLeft;  }  public static bool IsRight(this Alignment alignment)  {            return alignment == Alignment.UpperRight || alignment == Alignment.MiddleRight || alignment == Alignment.BottomRight;  }  public static bool IsCenter(this Alignment alignment)  {            return alignment == Alignment.UpperCenter || alignment == Alignment.MiddleCenter || alignment == Alignment.BottomCenter;  }  public static void DrawResponsiveLayout(int rowWidth, List<int> elementWidths, Action<int> drawActions, Alignment alignment)  {            int accumulatedWidth = 0;  GUILayout.BeginVertical(GUILayoutOptions.ExpandWidth(false).ExpandHeight(false));  {                if (alignment.IsMiddle() || alignment.IsBottom()) GUILayout.FlexibleSpace();  GUILayout.BeginHorizontal();  if (alignment.IsRight() || alignment.IsCenter()) GUILayout.FlexibleSpace();  for (int index = 0; index < elementWidths.Count; index++)  {                    int elementWidth = elementWidths[index] + 4;  if (accumulatedWidth + elementWidth > rowWidth)  {                        accumulatedWidth = 0;  if (alignment.IsLeft() || alignment.IsCenter()) GUILayout.FlexibleSpace();  GUILayout.EndHorizontal();  GUILayout.BeginHorizontal();  if (alignment.IsRight() || alignment.IsCenter()) GUILayout.FlexibleSpace();  }  accumulatedWidth += elementWidth;                    drawActions.Invoke(index);  }  if (alignment.IsLeft() || alignment.IsCenter()) GUILayout.FlexibleSpace();  GUILayout.EndHorizontal();  if (alignment.IsMiddle() || alignment.IsUpper()) GUILayout.FlexibleSpace();  }            GUILayout.EndVertical();  }  [InitializeOnLoad]  public class ScrollableTextArea  {  private delegate string ScrollableTextAreaInternalDelegate(  Rect position,  string text,  ref Vector2 scrollPosition,  GUIStyle style);  private static readonly ScrollableTextAreaInternalDelegate EditorGUI_ScrollableTextAreaInternal;  static ScrollableTextArea()  {                MethodInfo method = typeof(EditorGUI).GetMethod("ScrollableTextAreaInternal", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);  if (method == null)  return;  EditorGUI_ScrollableTextAreaInternal = (ScrollableTextAreaInternalDelegate)Delegate.CreateDelegate(typeof(ScrollableTextAreaInternalDelegate), method);  }  public static string EditorGUITextArea(Rect rect, string text, ref Vector2 scrollPos, int minLines = 0, int maxLines = 10)  {                return EditorGUI_ScrollableTextAreaInternal(rect, text, ref scrollPos, EditorStyles.textArea);  }  public static string EditorGUILayoutTextArea(string text, ref Vector2 scrollPos, int minLines = 0, int maxLines = 10)  {                float height = 32f + (float)((Mathf.Clamp(Mathf.CeilToInt(EditorStyles.textArea.CalcHeight(GUIHelper.TempContent(text), GUIHelper.ContextWidth) / 13f), minLines, maxLines) - 1) * 13);  Rect controlRect = EditorGUILayout.GetControlRect(false, height);  return EditorGUI_ScrollableTextAreaInternal(controlRect, text, ref scrollPos, EditorStyles.textArea);  }        }  public static Rect BeginColorBox(string label, Color color, params GUILayoutOption[] options)  {            Rect rect = SirenixEditorGUI.BeginBox(options);  SirenixEditorGUI.DrawSolidRect(rect, color);  SirenixEditorGUI.DrawBorders(rect, 1, new Color(0.24f, 0.24f, 0.24f));  SirenixEditorGUI.BeginBoxHeader();  float fieldWidth = EditorGUIUtility.fieldWidth;  EditorGUIUtility.fieldWidth = 10f;  Rect controlRect = EditorGUILayout.GetControlRect(false);  EditorGUIUtility.fieldWidth = fieldWidth;  GUI.Label(controlRect, label);  SirenixEditorGUI.EndBoxHeader();  return rect;  }  public static void EndColorBox()  {            SirenixEditorGUI.EndBox();  }  public static Color Grey(float value)  {            return new Color(value, value, value);  }  public static Color WithGrey(this Color color, float value)  {            return new Color(color.r * value, color.g * value, color.b * value);  }  public static Color WithAlpha(this Color color, float alpha)  {            color.a = alpha;  return color;  }    }}  
#endif

参考

十分钟入门Unity革命性编辑器拓展插件——Odin_哔哩哔哩_bilibili
Odin常用功能整理 | 登峰造极者,殊途亦同归。 (lfzxb.top)
marijnz/unity-toolbar-extender at dbd76ed996483d219c39d240f785b1790aa039ed (github.com)

相关文章:

Unity编辑器拓展-Odin

1.相比于原生Unity的优势 Unity不支持泛型类型序列化&#xff0c;例如字典原生Unity不支持序列化&#xff0c;而Odin可以继承序列化的Mono实现功能强大且使用简单&#xff0c;原生Unity想实现一些常见的功能需要额外自己编写Unity扩展的编码&#xff0c;实现功能只需要加一个特…...

小红书婴童产业探索,解析消费者需求!

在消费升级、市场引导的背景下&#xff0c;众多产业都在悄然发生着变化&#xff0c;其中“婴童产业”就是非常有代表性的一个。今天就来深入分析小红书婴童产业探索&#xff0c;解析消费者需求&#xff01; 一、何为婴童产业 事实上&#xff0c;婴童产业&#xff0c;并不仅仅局…...

离线安装mysql客户端

下载路径 oracle网站总是在不断更新&#xff0c;所以下载位置随时可能变动但万变不离其宗&#xff0c;学习也要学会一通百通。 首先直接搜索&#xff0c;就能找找到mysql官网 打开网站&#xff0c;并点击 DOWNLOADS 往下滚动&#xff0c;找到社区版下载按钮。…...

Docker 数据管理

管理 Docker 容器中数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09; 数据卷容器&#xff08;DataVolumes Containers&#xff09;。 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xf…...

数据统计--图形报表--ApacheEcharts技术 --苍穹外卖day10

Apache Echarts 营业额统计 重点:已完成订单金额要排除其他状态的金额 根据时间选择区间 设计vo用于后端向前端传输数据,dto用于后端接收前端发送的数据 GetMapping("/turnoverStatistics")ApiOperation("营业额统计")public Result<TurnoverReportVO…...

【kubernetes的三种网络】

kubernetes的三种网络 一、三种网络service网络&#xff08;service是虚拟IP地址&#xff09;pod网络&#xff08;pod的IP地址 docker容器的IP&#xff09;节点网络&#xff08;网络服务器上的物理网卡IP&#xff09; 二、其他网络flannel一、vxlan(隧道方案)1.定义2.优势3.工作…...

Java中树形菜单的实现方式(超全详解!)

前言 这篇文中&#xff0c;我一共会用两种方式来实现目录树的数据结构&#xff0c;两种写法逻辑是一样的&#xff0c;只是一种适合新手理解&#xff0c;一种看着简单明了但是对于小白不是很好理解。在这里我会很详细的讲解每一步代码&#xff0c;主要是方便新人看懂&#xff0…...

基于Uniswap V3的去中心化前端现货交易平台Oku正式登陆Moonbeam

波卡上的Uniswap v3合约由Moonbeam智能合约、Oku前端&#xff0c;以及Wormhole远程路由技术共同实现。 跨链互连应用的最佳去中心化开发平台Moonbeam宣布Uniswap现已正式登陆。此次是Uniswap产品作为一个主流的DEX首次涉足Polkadot生态。用户可以通过新的、易于使用的Oku界面与…...

leetcode 每日一题复盘(10.9~10.15)

leetcode 101 对称二叉树 这道题一开始想是用层序遍历,看每一层是否都对称,遇到一个问题就是空指针(子树为空)无法记录下来,同时会导致操作空指针的问题,因此需要修改入队条件,并用一个标志去表示空指针 vector<int>numv;for(int i0;i<size;i){TreeNode*frontque.fro…...

【云计算网络安全】DDoS 缓解解析:DDoS 攻击缓解策略、选择最佳提供商和关键考虑因素

文章目录 一、前言二、什么是 DDoS 缓解三、DDoS 缓解阶段四、如何选择 DDoS 缓解提供商4.1 网络容量4.2 处理能力4.3 可扩展性4.4 灵活性4.5 可靠性4.6 其他考虑因素4.6.1 定价4.6.2 所专注的方向 文末送书《数据要素安全流通》本书编撰背景本书亮点本书主要内容 一、前言 云…...

如何巧用AI智能技术,让文物不再“无人问津”?

文物是文化与传统的象征&#xff0c;而博物馆则是展现文物的载体。传统的博物馆监控体系只是利用摄像头进行监控&#xff0c;无法将人工智能融入其中&#xff0c;使其更加智能化、信息化。那么&#xff0c;如何将AI技术与传统视频监控相融合呢&#xff1f;TSINGSEE青犀智能分析…...

一天一八股——SSL/TLS协议

早期设计的http协议存在诸多的问题&#xff0c;SSL/TLS在http的基础上保证了数据的保密&#xff0c;验证和身份验证 https的保密性通过混合加密的方式保证&#xff0c;解决窃听问题https数据的完整性通过摘要算法保证&#xff0c;通过数字证书CA的方式进行数据来源和数据可靠性…...

SpringCloud学习笔记-Eureka服务的搭建

目录 1.首先引入依赖2.main中配置注解3.src/main/resources/application.yml配置文件 本文的主要工作是介绍如何搭建一个Eureka服务 1.首先引入依赖 pom文件中加入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring…...

css如何实现页面布局与五种实现方式

CSS布局实现的主要方式有以下几种&#xff1a; 一、盒模型布局&#xff1a;CSS中&#xff0c;每个元素都是一个盒子&#xff0c;包括内容、内边距、边框和外边距。通过设置盒子的属性&#xff08;如宽度、高度、内边距、边框、定位等&#xff09;&#xff0c;可以实现不同的布…...

cv2.split函数与cv2.merge函数

split函数用于图像BGR通道的分离 merge函数用于可将分开的图像通道合并到一起 1.split函数的使用 这是原图&#xff0c;我们使用split函数对其三个通道进行分离。 注意&#xff1a;split函数分离通道的顺序是B、G、R。 以下方法是将三个通道的值都设置为与某一个通道相同。…...

Vue--1.7watch侦听器(监视器)

作用&#xff1a;监视数据变化&#xff0c;执行一些业务逻辑或异步操作。 语法&#xff1a; 1.简单写法->简单类型数据&#xff0c;直接监视 const app new Vue({el: #app,data: {words:},watch:{words(newValue,oldValue){}}}) const app new Vue({el: #app,data: {obj…...

序列:全序关系

一个序列满足全序关系必须满足以下条件&#xff1a; 反对称性&#xff1a;若 a ≤ b a\le b a≤b&#xff0c;则 b ≥ a b\ge a b≥a传递性&#xff1a;若 a ≤ b a\le b a≤b 且 b ≤ c b\le c b≤c&#xff0c;则 a ≤ c a\le c a≤c完全性&#xff1a; a ≤ b a\le b …...

100M服务器能同时容纳多少人访问?

100M的服务器带宽能够同时容纳的用户访问量需要考虑以下几个关键因素: &#x1f449;1.单个用户的平均访问流量大小 这取决于网站内容,是否有多媒体等。一般文本类网站每用户每次访问在50-100KB。 &#x1f449;2.每个用户的平均访问页面 通常每次访问会打开多个页面 &…...

Javascript 笔记:函数调用与函数上下文

在 JavaScript 中&#xff0c;函数上下文通常指的是函数在执行时的当前对象的引用&#xff0c;这通常用 this 关键字表示。this 关键字在不同的执行上下文中可能引用到不同的对象。 1 全局上下文 当 this 关键字用在全局上下文&#xff08;不在任何函数内部&#xff09;&#…...

【WebService】C#搭建的标准WebService接口,在使ESB模版作为参数无法获取参数数据

一、问题说明 1.1 问题描述 使用C# 搭建WebService接口&#xff0c;并按照ESB平台人员的要求&#xff0c;将命名空间改为"http://esb.webservice",使用PostmanESB平台人员提供的入参示例进行测试时&#xff0c;callBussiness接口参数message始终为null。 以下是ES…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...