Unity-编辑器扩展
之前我们关于Unity的讨论都是针对于Unity底层的内容或者是代码层面的东西,这一次我们来专门研究Unity可视化的编辑器,在已有的基础上做一些扩展。
基本功能
首先我们来认识三个文件夹:
Editor,Gizmos,Editor Default Resources
这三个文件夹在Unity中比较特殊,当你符合命名要求以及文件路径要求之后它就会被Unity编辑器自动识别为这些特殊文件夹。
比如:
然后我们来了解一下操作编辑器的内容:
[MenuItem]
我们在Editor文件夹中新建一个脚本后放入以下函数:
[MenuItem("MyTool/DeleteAllObj", true)]private static bool DeleteValidate(){if (Selection.objects.Length > 0)return true;elsereturn false;}[MenuItem("MyTool/DeleteAllObj", false)]private static void MyToolDelete(){//Selection.objects foreach (Object item in Selection.objects){Undo.DestroyObjectImmediate(item);}}
MenuItem会帮助我们在Unity编辑器窗口新建一个可选项,代码中新建的可选项名为MyTool,其中点击MyTool我们定义了一个DeleteAllObj按钮,点击之后执行下列的DeleteValidate函数。后续跟到的一个bool变量代表的是一个验证函数和一个执行函数:true下面跟的是验证函数而false下面跟的是执行函数。验证函数会动态的检查我们菜单项是否可用,可用就高亮显示而不可用就会变灰;如果验证函数返回true,菜单项可用,我们才能去执行执行函数。
[MenuItem(“CONTEXT/组件名/按钮名”)]
[MenuItem(“CONTEXT/组件名/按钮名”)] 是 Unity 编辑器扩展中用于 在特定组件的上下文菜单中添加自定义功能按钮 的关键语法。因为是在已有的组件上添加自定义按钮,所以组件名称必须是已有的组件名称。
比如你加入这段代码在Editor文件的脚本中:
[MenuItem("CONTEXT/Rigidbody/Init")]private static void RigidbodyInit(){Debug.Log("12345");}
然后你去场景中任一物体的刚体组件上:
点击按钮后就会执行你的语句。
MenuCommand
用于获取当前操作的组件,在代码里使用,比如以下代码:
[MenuItem("CONTEXT/PlayerHealth/Init")]static void Init(MenuCommand cmd){PlayerHealth health = cmd.context as PlayerHealth;}
可以看到我们的代码中直接用MenuCommand cmd定义了当前组件的实例。
ContextMenu、ContextMenuItem
对于ContextMenu来说,通过在脚本方法上标注 [ContextMenu("菜单项名称")]
,可在该组件的 Inspector 右键菜单 中添加一个按钮,点击后触发对应方法。
比如以下代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ResetHealth : MonoBehaviour
{[ContextMenu("Reset Health")]void Resethealth(){// 重置血量逻辑}
}
那么在Inspector中:
ContextMenuItem 的作用则是通过在字段上标注 [ContextMenuItem("菜单项名称", "方法名")]
,可在 Inspector 中该字段的 右键菜单 中添加操作按钮,直接修改字段值或触发逻辑。
比如以下代码:
[ContextMenuItem("随机生成", "RandomizeName")]public string characterName;void RandomizeName() {characterName = "Player_" + Random.Range(1, 100);}
效果如下:
Selection
用于获取选择的游戏物体
- Selection.activeGameObject 返回第一个选择的场景中的对象
- Selection.gameObjects 返回场景中选择的多个对象,包含预制体等
- Selection.objects 返回选择的多个对象
用法如下:
using UnityEditor;
using UnityEngine;public static class DeleteAll // 改为静态类
{[MenuItem("MyTool/Delete Selected Objects")]static void DeleteSelected(){// 加入安全校验if (Selection.objects.Length == 0){Debug.LogWarning("未选择任何对象");return;}foreach (Object obj in Selection.objects){if (obj != null){Undo.DestroyObjectImmediate(obj);}}}
}
我们选中这个黄色的cube,之后打开MyTool中的Delete Selected Objects:
就会删除了。
自定义Inspector面板
学会了操作Unity编辑器的基本操作后,我们来尝试自己写出一个Inspector界面:
这是我们正常的Inspector界面。
我们先写这样一个脚本:
using UnityEngine;
public enum Course
{Chinese,Mathematics,English
}public class InspectorExample : MonoBehaviour
{public int intValue;public float floatValue;public string stringValue;public bool boolValue;public Vector3 vector3Value;public Course enumValue = Course.Chinese;public Color colorValue = Color.white;public Texture textureValue;
}
这个脚本定义了一个枚举和一些变量,其Inspector如图:
然后这个时候我们再根据这个InspectorExample类来写下面的这个脚本:
using UnityEngine;
using UnityEditor;[CustomEditor(typeof(InspectorExample))]
public class InspectorExampleEditor : Editor
{//target指该编辑器类绘制的目标类,需要将它强转为目标类private InspectorExample _target { get { return target as InspectorExample; } }//GUI重新绘制public override void OnInspectorGUI(){//EditorGUILayout.LabelField("IntValue",_target.intValue.ToString(),EditorStyles.boldLabel);//_target.intValue = EditorGUILayout.IntSlider(new GUIContent("Slider"),_target.intValue, 0, 10);//_target.floatValue = EditorGUILayout.Slider(new GUIContent("FloatValue"), _target.floatValue, 0, 10);_target.intValue = EditorGUILayout.IntField("IntValue", _target.intValue);_target.floatValue = EditorGUILayout.FloatField("FloatValue", _target.floatValue);_target.stringValue = EditorGUILayout.TextField("StringValue", _target.stringValue);_target.boolValue = EditorGUILayout.Toggle("BoolValue", _target.boolValue);_target.vector3Value = EditorGUILayout.Vector3Field("Vector3Value", _target.vector3Value);_target.enumValue = (Course)EditorGUILayout.EnumPopup("EnumValue", (Course)_target.enumValue);_target.colorValue = EditorGUILayout.ColorField(new GUIContent("ColorValue"), _target.colorValue);_target.textureValue = (Texture)EditorGUILayout.ObjectField("TextureValue", _target.textureValue, typeof(Texture), true);}
}
效果如图:
不难看出这段函数中最重要的内容就是EditorGUILayout这个函数:
这是一个自定义Inspector的方法,还有一种是:
using UnityEditor;[CustomEditor(typeof(InspectorExample))]
public class InspectorExampleEditor : Editor
{//定义序列化属性private SerializedProperty intValue;private SerializedProperty floatValue;private SerializedProperty stringValue;private SerializedProperty boolValue;private SerializedProperty vector3Value;private SerializedProperty enumValue;private SerializedProperty colorValue;private SerializedProperty textureValue;private void OnEnable(){//通过名字查找被序列化属性。intValue = serializedObject.FindProperty("intValue");floatValue = serializedObject.FindProperty("floatValue");stringValue = serializedObject.FindProperty("stringValue");boolValue = serializedObject.FindProperty("boolValue");vector3Value = serializedObject.FindProperty("vector3Value");enumValue = serializedObject.FindProperty("enumValue");colorValue = serializedObject.FindProperty("colorValue");textureValue = serializedObject.FindProperty("textureValue");}public override void OnInspectorGUI(){//表示更新序列化物体serializedObject.Update();EditorGUILayout.PropertyField(intValue);EditorGUILayout.PropertyField(floatValue);EditorGUILayout.PropertyField(stringValue);EditorGUILayout.PropertyField(boolValue);EditorGUILayout.PropertyField(vector3Value);EditorGUILayout.PropertyField(enumValue);EditorGUILayout.PropertyField(colorValue);EditorGUILayout.PropertyField(textureValue);//应用修改的属性值,不加的话,Inspector面板的值修改不了serializedObject.ApplyModifiedProperties();}
}
这两种自定义Inspector的方法的区别在于:
可以这样理解两种自定义Inspector的方法的差异:第一种是我们统一将Inspector里的内容转换为属性(Property),然后统一对属性进行操作;第二种则是我们通过Unity里提供的接口直接对Inspector的内容进行修改。
效果如图:
看起来似乎没有什么区别,那只是因为我们没有在重载的OnInspectorGUI()方法中加入新东西而已。
我们往里面加一个:
// 大标题(加粗+蓝色字体)var titleStyle = new GUIStyle(EditorStyles.boldLabel) {fontSize = 16,normal = { textColor = new Color(0.2f, 0.6f, 1f) }};EditorGUILayout.LabelField("★ 老公今晚不在家 ★", titleStyle);
效果如图:
这样就实现了我们的自定义Inspector了,总结来说:首先我们需要一个InspectorExample这样的脚本作为基础,然后在Editor中通过序列化或者直接字段操作来修改InspectorExample在Inspector上显示的内容即可。
创建编辑器窗体
学会自定义Inspector之后我们来学习如何在编辑器中创建窗(window),这方面Unity也已经帮助我们写好了API,我们直接去调用并设置即可。
Unity有三种窗类:ScriptableWizard,EditorWindow和PopupWindowContent。
可以看到,第一种窗口最大的特色就是自动生成输入控件,所以第一种窗口往往都是作为需求输入来使用;第二种窗口则是有着更完整的生命周期,且其功能更全面;第三种窗口则是一个临时性的弹窗,点一点按钮就会关闭的那种,更适合作为提示窗口。
我们分别来使用代码生成这三种窗口试试:
using UnityEngine;
using UnityEditor;public class WindowExample1 : ScriptableWizard
{public string msg = ""; //显示窗体[MenuItem("MyWindow/First Window")]private static void ShowWindow(){ScriptableWizard.DisplayWizard<WindowExample1>("WindowExample1", "确定", "取消");}//显示时调用private void OnEnable(){Debug.Log("OnEnable");}//更新时调用private void OnWizardUpdate(){Debug.Log("OnWizardUpdate");if (string.IsNullOrEmpty(msg)){errorString = "请输入信息内容";//错误提示helpString = "";//帮助提示}else{errorString = "";helpString = "请点击确认按钮";}}//点击确定按钮时调用private void OnWizardCreate(){Debug.Log("OnWizardCreate");}//点击第二个按钮时调用private void OnWizardOtherButton(){Debug.Log("OnWizardOtherButton");}//当ScriptableWizard需要更新其GUI时,将调用此函数以绘制内容//为GUI绘制提供自定义行为,默认行为是按垂直方向排列绘制所有公共属性字段//一般不重写该方法,按照默认绘制方法即可protected override bool DrawWizardGUI(){return base.DrawWizardGUI();}//隐藏时调用private void OnDisable(){Debug.Log("OnDisable");}//销毁时调用private void OnDestroy(){Debug.Log("OnDestroy");}
}
放入Editor中后在“MyWindow”中点击“First Window”,效果如图:
然后是:
using UnityEngine;
using UnityEditor;public class WindowExample2 : EditorWindow
{private static WindowExample2 window;//窗体实例//显示窗体[MenuItem("MyWindow/Second Window")]private static void ShowWindow(){window = EditorWindow.GetWindow<WindowExample2>("Window Example");window.Show();}//显示时调用private void OnEnable(){Debug.Log("OnEnable");}//绘制窗体内容private void OnGUI(){EditorGUILayout.LabelField("Your Second Window", EditorStyles.boldLabel);}//固定帧数调用private void Update(){Debug.Log("Update");}//隐藏时调用private void OnDisable(){Debug.Log("OnDisable");}//销毁时调用private void OnDestroy(){Debug.Log("OnDestroy");}
}
效果如下:
可以看到打开这个窗口后也会有类似于MonoBehaviour一样的生命周期函数存在并提供使用,所以说非常全能。
最后是:
using UnityEngine;
using UnityEditor;public class WindowExample3 : EditorWindow
{private static WindowExample3 window;private PopWindowExample popWindow = new PopWindowExample();private Rect buttonRect;//显示窗体[MenuItem("MyWindow/Third Window")]private static void ShowWindow(){window = EditorWindow.GetWindow<WindowExample3>("Window Example 3");window.Show();}//绘制窗体内容private void OnGUI(){GUILayout.Label("Popup example", EditorStyles.boldLabel);if (GUILayout.Button("Popup Options", GUILayout.Width(200))){PopupWindow.Show(buttonRect, popWindow);}//获取GUILayout最后用于控件的矩形if (Event.current.type == EventType.Repaint)buttonRect = GUILayoutUtility.GetLastRect();}
}public class PopWindowExample : PopupWindowContent
{bool toggle = true;//开启弹窗时调用public override void OnOpen(){Debug.Log("OnOpen");}//绘制弹窗内容public override void OnGUI(Rect rect){EditorGUILayout.LabelField("PopWindow");toggle = EditorGUILayout.Toggle("Toggle", toggle);}//关闭弹窗时调用public override void OnClose(){Debug.Log("OnClose");}public override Vector2 GetWindowSize(){//设置弹窗的尺寸return new Vector2(200, 100);}
}
效果如图:
针对不同的情境我们使用不同的窗体。
Gizmos辅助调试工具
Gizmos是Scene场景的可视化调试或辅助工具,相信在实际的游戏开发中大家都不会陌生,我们经常需要Gizmos绘制一些诸如射线检测,碰撞检测之类的实际检测范围来看交互逻辑有没有写错。
最常见的相关函数是OnDrawGizmo或者OnDrawGizmosSelected方法:OnDrawGizmos在每一帧都会被调用,其渲染的Gizmos是一直可见的,而OnDrawGizmosSelected是当物体被选中的时候才会显示。
public class GizmosExample : MonoBehaviour
{//绘制效果一直显示private void OnDrawGizmos(){var color = Gizmos.color;Gizmos.color = Color.white;Gizmos.DrawCube(transform.position, Vector3.one);Gizmos.color = color;}//绘制效果在选中对象时显示private void OnDrawGizmosSelected(){var color = Gizmos.color;Gizmos.color = Color.white;Gizmos.DrawWireCube(transform.position, Vector3.one);Gizmos.color = color;}
}
现在我们来试试如何绘制:
我们先新建一个类TargetExample。
using UnityEngine;// 目标脚本需挂载到场景中的游戏对象
public class TargetExample : MonoBehaviour
{}
然后我们在Editor文件夹中写这样一个脚本:
using UnityEngine;
using UnityEditor;public class GizmosTest
{//表示物体显示并且被选择的时候,绘制Gizmos[DrawGizmo(GizmoType.Active | GizmoType.Selected)]//第一个参数需要指定目标类,目标类需要挂载在场景对象中private static void MyCustomOnDrawGizmos(TargetExample target, GizmoType gizmoType){var color = Gizmos.color;Gizmos.color = Color.white;//target为挂载该组件的对象Gizmos.DrawCube(target.transform.position, Vector3.one);Gizmos.color = color;}
}
然后随便新建一个empty:
点击后就能看到:
我们再来一个比较常见的关于Gizmos的应用:
private Camera mainCamera;private void OnDrawGizmos(){if (mainCamera == null)mainCamera = Camera.main;Gizmos.color = Color.green;//设置gizmos的矩阵 Gizmos.matrix = Matrix4x4.TRS(mainCamera.transform.position, mainCamera.transform.rotation, Vector3.one);Gizmos.DrawFrustum(Vector3.zero, mainCamera.fieldOfView, mainCamera.farClipPlane, mainCamera.nearClipPlane, mainCamera.aspect);}
效果如图:
扩展Scene视图
用大白话来说,扩展Scene视图就是给Unity的场景编辑窗口“加外挂”,让开发者能更方便地调试和编辑游戏场景。具体能干三件事:加辅助显示,加自定义工具,改操作方式。
怎么实现呢?
在这里我们就不能不提一个很好用的小工具——Handles了。
Unity 的 Handles 类是专为 Scene 视图设计的交互式 3D 控件工具,简单来说就是给开发者提供了一套“可视化操作杆”,可以直接在场景中用鼠标拖拽、旋转、缩放物体或参数,而无需反复修改代码或 Inspector 面板中的数值。以下是它的核心功能和应用场景:
我们来看下面这个代码:
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(SceneExt))]
public class SceneExtEditor : Editor
{//获取SceneExt脚本对象private SceneExt _target { get { return target as SceneExt; } }private void OnSceneGUI(){//操作句柄Handles.Label(_target.transform.position,_target.transform.name + " : " + _target.transform.position);//绘制GUI的内容必须要在BeginGUI、EndGUI的方法对中Handles.BeginGUI();//设置GUI绘制的区域GUILayout.BeginArea(new Rect(50, 50, 200, 200));GUILayout.Label("Scene 扩展练习");GUILayout.EndArea();Handles.EndGUI();}
}
效果如图:
需要注意的是,我们关于Handles绘制GUI的内容必须在BeginGUI和EndGUI之间。
关于Handles类还有很多可用的内容,比如我们这样写一个脚本挂载在目标物体上:
using UnityEngine;public class SceneExt : MonoBehaviour
{public bool showLabel = true;public bool showLine = true;public bool showSlider = true;public bool showRadius = true;public bool showCircleHandleCap = true;public bool showSphereHandleCap = true; public bool showGUI = true;public Vector3 sliderPos = Vector3.forward;public float areaRadius = 1;public float circleSize = 1;
}
然后我们在Editor文件夹中这样写一个脚本:
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(SceneExt))]
public class SceneExtEditor : Editor
{//获取SceneExt脚本对象private SceneExt _target { get { return target as SceneExt; } }private void OnSceneGUI(){if (_target.showLabel){//操作句柄,显示文本Handles.Label(_target.transform.position + Vector3.up * 0.5f, _target.transform.name + " : " + _target.transform.position);}if (_target.showLine){//修改句柄的颜色Handles.color = Color.yellow;//绘制一条线Handles.DrawLine(_target.transform.position, Vector3.up * 5);}if (_target.showSlider){Handles.color = Color.red;//绘制一个可以沿着某个轴向的3D滑动条_target.sliderPos = Handles.Slider(_target.sliderPos, _target.transform.forward);}if (_target.showRadius){Handles.color = Color.blue;//绘制一个半径控制手柄_target.areaRadius = Handles.RadiusHandle(Quaternion.identity, _target.transform.position, _target.areaRadius);}if (_target.showCircleHandleCap){//获取Y轴的颜色Handles.color = Handles.yAxisColor;//绘制一个圆环Handles.CircleHandleCap(0, _target.transform.position + Vector3.up * 2, Quaternion.Euler(90, 0, 0), _target.circleSize, EventType.Repaint);}if (_target.showSphereHandleCap){Handles.color = Color.green;//绘制一个球形Handles.SphereHandleCap(1, _target.transform.position, Quaternion.identity, HandleUtility.GetHandleSize(_target.transform.position), EventType.Repaint);}if (_target.showGUI){//绘制GUI的内容必须要在BeginGUI、EndGUI的方法对中Handles.BeginGUI();//设置GUI绘制的区域GUILayout.BeginArea(new Rect(50, 50, 200, 200));GUILayout.Label("Scene 扩展练习");GUILayout.EndArea();Handles.EndGUI();}}
}
效果如图:
EditorPrefs、ScriptableObject、Undo
Unity编辑器为开发者提供了类似PlayerPrefs的数据保存方式EditorPrefs。EditorPrefs是适用于编辑器模式,而PlayerPrefs适用于游戏运行时。
EditorPrefs提供了四种数据的保存:int,float,string,bool
通过Set方法保存下数据,下次则通过Get方法来获取数据,HasKey方法可以判断是否存在该数据的保存,删除数据调用DeleteKey方法即可。
比如下列这段代码:
using UnityEngine;
using UnityEditor;public class WindowExample2 : EditorWindow
{private static WindowExample2 window;//窗体实例private string tempMsg;//显示窗体[MenuItem("MyWindow/Second Window")] private static void ShowWindow(){window = EditorWindow.GetWindow<WindowExample2>("Window Example");window.Show();}private void OnEnable(){if (EditorPrefs.HasKey("TempMsg")){tempMsg = EditorPrefs.GetString("TempMsg");}}private void OnGUI(){tempMsg = EditorGUILayout.TextField("Temp Msg", tempMsg);if (GUILayout.Button("Save")){EditorPrefs.SetString("TempMsg", tempMsg);}}
}
我们调用EditorPrefs.HasKey()方法来检测是否有"TempMsg"键,有的话用Get方法获取,需要的话用Set方法设置键和值进行保存。
效果如图:
点击save之后就会存储“123”到EditorPrefs中。
ScriptableObject则是一个我们都不陌生的内容,早在很久之前我们就用SO来实现内容的存储过。
ScriptableObject是Unity引擎中一种特殊的可编程数据容器,它允许开发者将数据以资源文件(.asset)的形式存储在项目中,既能在编辑器模式下持久化数据,又能高效管理游戏运行时配置。
Undo则是专门用于撤销编辑器模式下的操作,如:
Undo.RegisterCreatedObjectUndo : 记录新建的对象状态,可以撤销新建的对象
Undo.RecordObject:记录对象的状态,需要在修改之前调用
Undo.AddComponent:可以撤销新挂载的组件
Undo.DestroyObjectImmediate:可以撤销删除对象的操作
Undo.SetTransformParent:可以撤销修改父对象的操作
我们用一个实例来学习其用法:
using UnityEditor;
using UnityEngine;public class UndoTest
{[MenuItem("Tools/Create Obj")]private static void CreateObj(){GameObject newObj = new GameObject("Undo");Undo.RegisterCreatedObjectUndo(newObj, "CreateObj");}[MenuItem("Tools/Move Obj")]private static void MoveObj() {//获取选中的场景对象Transform trans = Selection.activeGameObject.transform;if (trans){Undo.RecordObject(trans, "MoveObj");trans.position += Vector3.up;}}[MenuItem("Tools/AddComponent Obj")]private static void AddComponentObj() {//获取选中的场景对象GameObject selectedObj = Selection.activeGameObject;if (selectedObj){Undo.AddComponent(selectedObj,typeof(Rigidbody));}}[MenuItem("Tools/Destroy Obj")]private static void DestroyObj(){//获取选中的场景对象GameObject selectedObj = Selection.activeGameObject;if (selectedObj){Undo.DestroyObjectImmediate(selectedObj);}}[MenuItem("Tools/SetParent Obj")]private static void SetParentObj(){//获取选中的场景对象Transform trans = Selection.activeGameObject.transform;Transform root = Camera.main.transform;if (trans){Undo.SetTransformParent(trans, root, trans.name);}}
}
效果如下:
AssetPostprocessor
AssetPostprocessor是Unity引擎中用于自动化资源导入流程的核心工具,它允许开发者在资源(纹理、模型、音频等)导入前后插入自定义逻辑,实现资源处理的标准化、优化和批量化。
一言以蔽之,AssetPostprocessor就是一个在导入资源时在导入前后对资源进行处理的一个工具。
AssetPostprocessor是基于周期函数来实现功能的,也就是Preprocessh和Postprocess。
这是一些常见的方法:
OnPreprocessTexture:在导入纹理贴图之前调用
OnPreprocessModel:在导入模型之前调用
OnPreprocessAudio:在导入音频之前调用
OnPostprocessTexture:在导入纹理贴图之后调用
OnPostprocessModel:在导入模型之后调用
OnPostprocessAudio:在导入音频之后调用
OnPostprocessAllAssets:所有资源的导入,删除,移动操作都会调用该方法
比如以下代码:
using UnityEngine;
using UnityEditor;public class AssetsImport : AssetPostprocessor
{private void OnPreprocessTexture(){Debug.Log("OnPreprocessTexture:" + this.assetPath);TextureImporter importer = this.assetImporter as TextureImporter;importer.textureType = TextureImporterType.Sprite;importer.maxTextureSize = 512;importer.mipmapEnabled = false;}public void OnPostprocessTexture(Texture2D tex){Debug.Log("OnPostprocessTexture:" + this.assetPath);}
}
我们随便拖入一张图:
后台会输出:
且这张图的属性会被修正为:
相关文章:
Unity-编辑器扩展
之前我们关于Unity的讨论都是针对于Unity底层的内容或者是代码层面的东西,这一次我们来专门研究Unity可视化的编辑器,在已有的基础上做一些扩展。 基本功能 首先我们来认识三个文件夹: Editor,Gizmos,Editor Defaul…...
系分论文《论遗产系统演化》
系统分析师论文范文系列 摘要 2022年6月,某金融机构启动核心业务系统的技术升级项目,旨在对其运行超过十年的遗留系统进行演化改造。该系统承担着账户管理、支付结算等关键业务功能,但其技术架构陈旧、扩展性不足,难以适应数字化转型与业务快速增长的需求。作为系统分析师,…...
Django基础(二)Django 项目基础操作
一、实验目标 熟悉 Django 基本命令 理解 Django 项目和应用的目录结构 掌握项目初始化、应用创建与注册、项目启动、视图函数编写、路由配置、数据库配置等基础操作 二、Django 项目初始化 进入虚拟环境 source venv/bin/activate创建 Django 项目 django-admin startproje…...
【图像大模型】Stable Video Diffusion:基于时空扩散模型的视频生成技术深度解析
Stable Video Diffusion:基于时空扩散模型的视频生成技术深度解析 一、架构设计与技术演进1.1 核心模型架构1.2 技术创新点1.2.1 运动预测网络1.2.2 层级式训练策略 二、系统架构解析2.1 完整生成流程2.2 性能指标对比 三、实战部署指南3.1 环境配置3.2 基础推理代码…...
【免杀】C2免杀技术(七)远程线程注入
远程线程注入(Remote Thread Injection)是一种常见的进程注入技术,经常用于红队渗透、恶意软件加载、持久化控制等场景中,尤其在免杀(AV/EDR bypass)应用领域中,是一种历史悠久但依然有效的手段…...
二、【环境搭建篇】:Django 和 Vue3 开发环境准备
【环境搭建篇】:Django 和 Vue3 开发环境准备 前言为什么我们需要特定的开发环境?准备工作第一步:搭建后端开发环境 (Python, Django, DRF)1. 安装 Python2. 创建和激活 Python 虚拟环境3. 在虚拟环境中安装 Django 和 DRF 第二步:…...
【神经网络与深度学习】激活函数的可微可导
引言: 在深度学习领域,激活函数扮演着至关重要的角色。它不仅影响神经网络的非线性建模能力,还直接关系到梯度计算的稳定性。在优化过程中,我们通常要求激活函数具有良好的数学性质,其中可微性是一个关键条件。相比简单…...
【Tauri2】046—— tauri_plugin_clipboard_manager(一)
目录 前言 正文 安装 Rust中的使用 对文字的操作 看看write_text的函数签名 看看read_text的函数签名 对图像的操作 对html的操作 总结 前言 这篇就来看看clipboard这个插件。 参考如下 Clipboard | Taurihttps://tauri.app/plugin/clipboard/ 正文 安装 执行下…...
高效选课系统:一键管理你的课程表
选课流程 数据模型 我的课程表Controller Api(value "我的课程表接口", tags "我的课程表接口") Slf4j RestController public class MyCourseTablesController {Autowiredprivate MyCourseTablesService myCourseTablesService;ApiOperation("添加…...
Pytorch分布式训练,数据并行,单机多卡,多机多卡
分布式训练 所有代码可以见我github 仓库:https://github.com/xiejialong/ddp_learning.git 数据并行(Data Parallelism,DP) 跨多个gpu训练模型的最简单方法是使用 torch.nn.DataParallel. 在这种方法中,模型被复制…...
Secarmy Village: Grayhat Conference靶场
Secarmy Village: Grayhat Conference 来自 <Secarmy Village: Grayhat Conference ~ VulnHub> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182,靶…...
centos 9 Kickstart + Ansible自动化部署 —— 筑梦之路
目标 利用 Kickstart 完成 centos 9 系统的全自动安装(裸金属/虚拟机)。 安装完成后自动接入 Ansible 进行软件包、服务、用户、配置等系统初始化操作。 实现一套通用、可重复、可维护的自动化交付流程。 KS文件 # ks.cfg 示例 install lang zh_CN.…...
HarmonyOS应用开发入门宝典——项目驱动学习法实践
学习一项新技能,最好也是最快的方法就是动手实战。学习鸿蒙也一样,给自己定一个小目标,直接找项目练,这样进步是最快的。记住,最好的学习时机永远是现在,最好的老师永远是你正在开发的项目。 一、为什么选择…...
Python类的力量:第六篇:设计模式——Python面向对象编程的“架构蓝图”
文章目录 前言:从“代码堆砌”到“模式复用”的思维跃迁 一、创建型模式:对象创建的“智能工厂”1. 单例模式(Singleton):全局唯一的“资源管家”2. 工厂模式(Factory):对象创建的“…...
第50天-使用Python+Qt+DeepSeek开发AI运势测算
1. 环境准备 bash 复制 下载 pip install pyside6 requests python-dotenv 2. 获取DeepSeek API密钥 访问DeepSeek官网注册账号 进入控制台创建API密钥 在项目根目录创建.env文件: env 复制 下载 DEEPSEEK_API_KEY=your_api_key_here 3. 创建主应用框架 python 复制…...
CentOS系统上挂载磁盘
在CentOS系统上挂载磁盘,主要包括查看磁盘设备、分区(若需要)、格式化、创建挂载点和挂载等步骤,以下是详细操作: 1. 查看磁盘设备 使用fdisk -l或lsblk命令查看系统识别到的磁盘设备。 fdisk -l:列出所…...
(一) 本地hadoop虚拟机系统设置
1.配置固定IP地址(每一台都配置) 开启node1,修改主机名为node1,并修改固定IP为:192.168.88.131 # 修改主机名 hostnamectl set-hostname node1# 修改IP vim /etc/sysconfig/network-scripts/ifcfg-ens33 IPADDR"…...
亿级核心表如何优雅扩展字段
1 导语 亿级数据的核心表新增一个字段,远不止一句简单的“ALTER TABLE”,锁表风险、页分裂、索引性能衰减……每一个问题都可能引发线上事故。如何在不影响业务的前提下,只需简单的配置,即可实现字段的动态扩展?本文将…...
单端传输通道也会有奇偶模现象喔
奇模(Odd mode)与偶模(Even mode)对差动对是很关键的要素,其会影响奇/偶模阻抗与相位速度,设计不良甚会让共模噪声引入整个差动对使讯号质量下降。 然而对单端信号系统而言呢? 如果说一对side b…...
VUE3 中的 ResizeObserver 警告彻底解决方案
问题背景 今天在使用 Vue 3 Ant Design Vue 开发后台管理系统时,在页面频繁触发 元素尺寸变化(如表格滚动、窗口缩放) 的时候,控制台频繁出现如下警告: ResizeObserver loop completed with undelivered notificati…...
IDEA2025版本使用Big Data Tools连接Linux上Hadoop的HDFS
目录 Windows的准备 1. 将与Linux上版本相同的hadoop压缩包解压到本地 编辑2.设置$HADOOP HOME环境变量指向:E:\hadoop-3.3.4 3.下载hadoop.dll和winutils.exe文件 4.将hadoop.dll和winutils.exe放入$HADOOP HOME/bin中 IDEA中操作 1.下载Big Data Tools插件 2.添加并连…...
Gas优化利器:Merkle 树如何助力链上数据效率革命
目录 前言原理Merkle树示意图实战演示:构建 Merkle 树并在合约中验证离线构建 Merkle 树(手动计算Merkle树、生成mermaid示意图)编写Merkle.js脚本执行Merkle.js脚本执行结果展示mermaid流程图展示离线构建 Merkle 树(merkletreejs计算Merkle树、验证哈希路径)编写Merkle.…...
R语言空间分析实战:地理加权回归联合主成份与判别分析破解空间异质性难题
在自然和社会科学领域有大量与地理或空间有关的数据,这一类数据一般具有严重的空间异质性,而通常的统计学方法并不能处理空间异质性,因而对此类型的数据无能为力。以地理加权回归为基础的一系列方法:经典地理加权回归,…...
kafka入门(二)
Java客户端访问Kafka 引入maven依赖 <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka‐clients</artifactId> <version>2.4.1</version> </dependency> 消息发送端代码 package com.tuling.kafka.ka…...
学习日记-day11-5.20
完成目标: comment.java package com.zcr.pojo; import org.hibernate.annotations.GenericGenerator;import javax.persistence.*; //JPA操作表中数据,可以将对应的实体类映射到一张表上Entity(name "t_comment")//表示当前的实体类与哪张表…...
手淘不易被清洗销量的4个成交入口
在淘宝运营中,销量是店铺权重的重要指标之一,但平台对虚假交易的打击力度越来越大,许多商家因销量被清洗而损失惨重。那么,通过什么样的手淘成交入口稳定不易清洗呢?经过实测,我们总结了以下手淘4个不易被清…...
【Linux】Linux 多线程
目录 1. Linux线程概念2. 重谈进程地址空间---页表2.1 如何由虚拟地址转化为物理地址的 3. pthread库调用接口3.1 线程的创建---pthread_create3.2 线程等待---pthread_join3.3 线程的退出3.4 分离线程 4. 线程库5. 线程ID6. Linux线程互斥6.1 锁6.2 锁的接口6.2.1 互斥量的初始…...
DAY31
知识点回顾 规范的文件命名规范的文件夹管理机器学习项目的拆分编码格式和类型注解 作业:尝试针对之前的心脏病项目,准备拆分的项目文件,思考下哪些部分可以未来复用。 浙大疏锦行...
大模型应用开发“扫盲”——基于市场某款智能问数产品的技术架构进行解析与学习
本文将从一款问数产品相关技术架构,针对大模型应用开发中的基础知识进行“扫盲”式科普,文章比较适合新手小白,属于是我的学习笔记整理,大佬可以划走啦~产品关键信息已经进行模糊处理,如有侵权请联系删除。 文章目录 前…...
List优雅分组
一、前言 最近小永哥发现,在开发过程中,经常会遇到需要对list进行分组,就是假如有一个RecordTest对象集合,RecordTest对象都有一个type的属性,需要将这个集合按type属性进行分组,转换为一个以type为key&…...
打破建筑与制造数据壁垒:Revit 到 STP 格式转换全攻略(含插件应用 + 迪威模型实战)
引言 在建筑信息模型(BIM)与计算机辅助设计(CAD)领域,数据在不同软件和系统间的高效流转至关重要。Revit 作为 BIM 技术应用的主流软件,常用于建筑设计、施工和运维管理;而 STP(STE…...
RISC-V 开发板 MUSE Pi Pro USB 测试(3.0 U盘,2.0 UVC摄像头)
视频讲解: RISC-V 开发板 MUSE Pi Pro USB 测试(3.0 U盘,2.0 UVC摄像头) 总共开发板有4个USB的A口,1个USB的TypeC口,我们插上两个USB3.0的U盘和一个USB2.0的UVC摄像头来进行测试 lsusb -tv 可以看到有3个US…...
驱动相关基础
一、驱动分类与区别 字符设备驱动 一个字节一个字节进行读写操作的设备,以字符流的形式进行数据传输(如鼠标、键盘、串口)。 块设备驱动 以块为单位进行读写操作的设备,块的大小通常为 512 字节、1024 字节。 块设备驱动主…...
【node.js】核心进阶
个人主页:Guiat 归属专栏:node.js 文章目录 1. Node.js高级异步编程1.1 Promise深入理解1.1.1 创建和使用Promise1.1.2 Promise组合模式 1.2 Async/Await高级模式1.2.1 基本使用1.2.2 并行执行1.2.3 顺序执行与错误处理 1.3 事件循环高级概念1.3.1 事件循…...
高频Java面试题深度拆解:String/StringBuilder/StringBuffer三剑客对决(万字长文预警)
文章目录 一、这道题的隐藏考点你Get到了吗?二、内存模型里的暗战(图解警告)2.1 String的不可变性之谜2.2 可变双雄的内存游戏 三、线程安全背后的修罗场3.1 StringBuffer的同步真相3.2 StringBuilder的裸奔哲学 四、性能对决:用数…...
量子计算的曙光:从理论奇点到 IT 世界的颠覆力量
在信息技术(IT)的飞速发展中,一项前沿技术正以耀眼的光芒照亮未来——量子计算(Quantum Computing)。2025 年,随着量子硬件的突破、算法的优化以及企业对超算能力的渴求,量子计算从科幻梦想逐步…...
c++使用protocol buffers
在 C 里使用 Protocol Buffer,要先定义消息结构,接着生成 C 代码,最后在程序里使用这些生成的代码。 定义消息结构 首先要创建一个.proto文件,在其中定义消息类型和字段。 // person.proto syntax "proto3"; // 指…...
AI驱动发展——高能受邀参加华为2025广东新质生产力创新峰会
当AI浪潮席卷全球产业版图,一场以"智变"驱动"质变"的变革正在发生。5月15日,华为中国行2025广东新质生产力创新峰会璀璨启幕,作为华为生态战略合作伙伴,高能计算机与行业领军者同台论道,共同解码A…...
怎样解决photoshop闪退问题
检查系统资源:在启动 Photoshop 之前,打开任务管理器检查 CPU 和内存的使用情况。如果发现资源占用过高,尝试关闭不必要的程序或重启计算机以释放资源。更新 Photoshop 版本:确保 Photoshop 是最新版本。Adobe 经常发布更新以修复…...
AWS CodePipeline+ Elastic Beanstalk(AWS中国云CI/CD)
问题 最近需要利用AWS云上面的CI/CD部署Spring应用。 一图胜千言 步骤 打开CodePipeline网页,开始管道创建,如下图: 管道设置,如下图: 这里主要设置管道名称,至于服务角色,直接让codepipel…...
人工智能核心知识:AI Agent 的四种关键设计模式
人工智能核心知识:AI Agent 的四种关键设计模式 一、引言 在人工智能领域,AI Agent(人工智能代理)是实现智能行为和决策的核心实体。它能够感知环境、做出决策并采取行动以完成特定任务。为了设计高效、灵活且适应性强的 AI Age…...
Electron+vite+vue3 从0到1搭建项目,开发Win、Mac客户端
随着前端技术的发展,出现了所谓的大前端。 大前端则是指基于前端技术延伸出来的各种终端平台及应用场景,包括APP、桌面端、手表终端、服务端等。 本篇文章主要是和大家一起学习一下使用Electron 如何打包出 Windows 和 Mac 所使用的客户端APPÿ…...
GitLab部署
学git Git最新最新详细教程、安装(从入门到精通!!!!企业级实战!!!工作必备!!!结合IDEA、Github、Gitee实战!!!…...
基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析技术
在自然和社会科学领域,存在大量与地理或空间相关的数据,这些数据通常具有显著的空间异质性。传统的统计学方法在处理这类数据时往往力不从心。基于R语言的一系列空间异质性数据分析方法,如地理加权回归(GWR)、地理加权…...
指针深入理解(二)
volatile关键字 防止优化指向内存地址, typedef 指针可以指向C语言所有资源 typedef 就是起一个外号。 指针运算符加减标签操作 指针加的是地址,并且增加的是该指针类型的一个单位,指针变量的步长可以用sizeof(p[0]) 这两个的p1是不一样…...
django回忆录(Python的一些基本概念, pycharm和Anaconda的配置, 以及配合MySQL实现基础功能, 适合初学者了解)
django 说实在的, 如果是有些Python基础或者编程基础, 使用django开发本地网站的速度还是很快的, 特别是配合ai进行使用. 本人使用该框架作业的一个主要原因就是因为要做数据库大作业, 哥们想速通, 结果由于我一开始没有接触过这些方面的知识, 其实也不算快, 而且现在我也没有…...
leetcode hot100刷题日记——5.无重复字符的最长字串
解答:滑动窗口思想(见官方题解) //方法1 class Solution { public:int lengthOfLongestSubstring(string s) {//哈希表记录是否有重复字符unordered_set<char>c;int maxlength0;int ns.size();//右指针初始化为-1,可以假设…...
一文讲清python、anaconda的安装以及pycharm创建工程
软件下载 Pycharm下载地址: https://download-cdn.jetbrains.com.cn/python/pycharm-community-2024.1.1.exe?_gl1*1xfh3l8*_gcl_au*MTg1NjU2NjA0OC4xNzQ3MTg3Mzg1*FPAU*MTg1NjU2NjA0OC4xNzQ3MTg3Mzg1*_ga*MTA2NzE5ODc1NS4xNzI1MzM0Mjc2*_ga_9J976DJZ68*czE3NDczMD…...
[每日一题] 3355. 零数组变换 i
文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 3355. 零数组变换 I - 力扣(LeetCode) 2. 题目描述 给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] [li, ri]。…...
【笔记】与PyCharm官方沟通解决开发环境问题
#工作记录 2025年5月20日 星期二 背景 在此前的笔记中,我们提到了向PyCharm官方反馈了几个关于Conda环境自动激活、远程解释器在社区版中的同步问题以及Shell脚本执行时遇到的问题。这些问题对日常开发流程产生了一定影响,因此决定联系官方支持寻求解…...