Unity 自定义批量打包工具

打包配置项

using UnityEngine;
using System.Collections.Generic;namespace MYTOOL.Build
{[System.Flags]public enum VersionOptions{None = 0,Major = 1,Minor = 4,Build = 8,Revision = 0x10,}/// <summary>/// 批量打包配置文件/// </summary>[CreateAssetMenu]public class BatchBuildProfile : ScriptableObject{public VersionOptions versionOptions = VersionOptions.Revision;public List<BuildTask> tasks = new List<BuildTask>(0);}
}

打包功能

using UnityEditor;
using UnityEngine;
using System;
using System.IO;
using System.Collections.Generic;namespace MYTOOL.Build
{public class LogMessage{public LogType type;public string message;public LogMessage(LogType type, string message){this.type = type;this.message = message;}}[CustomEditor(typeof(BatchBuildProfile))]public class BatchBuildProfileInspector : Editor{//配置文件private BatchBuildProfile profile;//折叠栏private Dictionary<BuildTask, bool> foldoutMap;//记录日志private List<LogMessage> logsList;private void OnEnable(){profile = target as BatchBuildProfile;foldoutMap = new Dictionary<BuildTask, bool>();logsList = new List<LogMessage>();}public override void OnInspectorGUI(){OnMenuGUI();OnListGUI();serializedObject.ApplyModifiedProperties();if (GUI.changed)EditorUtility.SetDirty(profile);}/// <summary>/// 菜单项/// </summary>private void OnMenuGUI(){EditorGUILayout.HelpBox($"已有打包工作项:{profile.tasks.Count}个", MessageType.Info);EditorGUILayout.HelpBox($"打包时会先进行排序, 优先打包当前平台【{EditorUserBuildSettings.activeBuildTarget}】", MessageType.Info);//限制20个if (profile.tasks.Count < 20){//新建工作项if (GUILayout.Button("新建工作项", GUILayout.Height(30))){Undo.RecordObject(profile, "Create");string buildPath = Path.Combine(Directory.GetParent(Application.dataPath).FullName, ".Build");if (Directory.Exists(buildPath) == false){Directory.CreateDirectory(buildPath);Debug.LogFormat("创建构建目录:{0}", buildPath);}var task = new BuildTask(PlayerSettings.productName, (MyBuildTarget)EditorUserBuildSettings.activeBuildTarget, buildPath);profile.tasks.Add(task);}}else{EditorGUILayout.HelpBox($"无法新建打包工作项", MessageType.Warning);}if (profile.tasks.Count > 0){//清空GUI.color = Color.yellow;if (GUILayout.Button("清空工作项", GUILayout.Height(30))){Undo.RecordObject(profile, "Clear");if (EditorUtility.DisplayDialog("提醒", "是否确认清理所有打包工作项?", "确定", "取消")){Debug.LogWarningFormat("清理{0}个打包工作项", profile.tasks.Count);profile.tasks.Clear();foldoutMap.Clear();}}//开始打包GUI.color = Color.cyan;if (GUILayout.Button("开始打包", GUILayout.Height(30))){if (EditorUtility.DisplayDialog("确认操作", "即将开始打包过程,这可能需要一些时间。您希望继续吗?", "继续", "取消")){logsList.Clear();OnBuild(false);}return;}//清理并打包GUI.color = Color.yellow;if (GUILayout.Button("清理并打包", GUILayout.Height(30))){if (EditorUtility.DisplayDialog("确认操作", "即将进行清理并开始打包过程,这可能需要一些时间。您希望继续吗?", "继续", "取消")){if (EditorUtility.DisplayDialog("重要提醒", "清理操作将移除当前构建平台的所有文件,请确保已备份重要数据。是否要继续?此操作不可逆。", "确定继续", "取消")){logsList.Clear();OnBuild(true);}}return;}}GUI.color = Color.white;//排序if (profile.tasks.Count > 1){if (GUILayout.Button("排序工作项", GUILayout.Height(30))){Debug.Log("排序打包工作项");profile.tasks.Sort(new BuildTaskComparer());return;}}}/// <summary>/// 任务项/// </summary>private void OnListGUI(){//新旧版本号if (profile.tasks.Count > 0){GUILayout.Space(10);//版本选项GUILayout.BeginHorizontal();GUILayout.Label("版本选项:", GUILayout.Width(70));var newVO = (VersionOptions)EditorGUILayout.EnumFlagsField(profile.versionOptions);if (profile.versionOptions != newVO){Undo.RecordObject(profile, "Version Options");profile.versionOptions = newVO;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("旧版本号:", GUILayout.Width(70));GUILayout.Label(PlayerSettings.bundleVersion);GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("新版本号:", GUILayout.Width(70));GUILayout.Label(GetNewVersion(profile.versionOptions));GUILayout.EndHorizontal();}for (int i = 0; i < profile.tasks.Count; i++){var task = profile.tasks[i];if (foldoutMap.ContainsKey(task) == false){foldoutMap.Add(task, true);}GUI.contentColor = task.enableTask ? Color.green : Color.white;GUILayout.Space(10);GUILayout.BeginHorizontal("Badge");GUILayout.Space(20);foldoutMap[task] = EditorGUILayout.Foldout(foldoutMap[task], task.ToString(), true);if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash"), "IconButton", GUILayout.Width(20))){Undo.RecordObject(profile, "Delete Task");foldoutMap.Remove(task);profile.tasks.Remove(task);break;}GUILayout.EndHorizontal();//折叠栏if (foldoutMap[task]){GUI.contentColor = Color.white;GUILayout.BeginVertical("Box");//是否激活GUILayout.BeginHorizontal();GUILayout.Label("是否激活:", GUILayout.Width(70));task.enableTask = GUILayout.Toggle(task.enableTask, "");GUILayout.EndHorizontal();//打包场景GUILayout.BeginHorizontal();GUILayout.Label("打包场景:", GUILayout.Width(70));if (GUILayout.Button("+", GUILayout.Width(20f))){task.sceneAssets.Add(null);}GUILayout.EndHorizontal();//场景列表if (task.sceneAssets.Count > 0){OnSceneAssetsList(task);}//产品名称GUILayout.BeginHorizontal();GUILayout.Label("产品名称:", GUILayout.Width(70));var newPN = GUILayout.TextField(task.productName);if (task.productName != newPN){Undo.RecordObject(profile, "Product Name");task.productName = newPN;}GUILayout.EndHorizontal();//打包平台GUILayout.BeginHorizontal();GUILayout.Label("打包平台:", GUILayout.Width(70));var newBT = (MyBuildTarget)EditorGUILayout.EnumPopup(task.buildTarget);if (task.buildTarget != newBT){Undo.RecordObject(profile, "Build Target");task.buildTarget = newBT;//这些平台只能使用IL2CPPif (task.buildTarget == MyBuildTarget.iOS || task.buildTarget == MyBuildTarget.WebGL || task.buildTarget == MyBuildTarget.WeixinMiniGame){task.scriptMode = ScriptingImplementation.IL2CPP;}//其它平台默认切换到Playerif (task.buildTarget != MyBuildTarget.StandaloneWindows64 && task.buildTarget != MyBuildTarget.StandaloneLinux64 && task.buildTarget != MyBuildTarget.NoTarget){task.buildSubtarget = StandaloneBuildSubtarget.Player;}}GUILayout.EndHorizontal();//Windows Linux添加打包子平台if (task.buildTarget == MyBuildTarget.StandaloneWindows64 || task.buildTarget == MyBuildTarget.StandaloneLinux64){GUILayout.BeginHorizontal();GUILayout.Label("打包子平台:", GUILayout.Width(70));var newBS = (StandaloneBuildSubtarget)EditorGUILayout.EnumPopup(task.buildSubtarget);if (task.buildSubtarget != newBS){Undo.RecordObject(profile, "Build Subtarget");task.buildSubtarget = newBS;}GUILayout.EndHorizontal();}//打包选项GUILayout.BeginHorizontal();GUILayout.Label("打包选项:", GUILayout.Width(70));var newBO = (BuildOptions)EditorGUILayout.EnumFlagsField(task.buildOptions);if (task.buildOptions != newBO){Undo.RecordObject(profile, "Build Options");task.buildOptions = newBO;}GUILayout.EndHorizontal();//脚本模式GUILayout.BeginHorizontal();GUILayout.Label("脚本模式:", GUILayout.Width(70));var newSM = (ScriptingImplementation)EditorGUILayout.EnumPopup(task.scriptMode);if (task.scriptMode != newSM){Undo.RecordObject(profile, "Script Mode");task.scriptMode = newSM;}GUILayout.EndHorizontal();//打包路径GUILayout.BeginHorizontal();GUILayout.Label("打包路径:", GUILayout.Width(70));GUILayout.TextField(task.buildPath);if (GUILayout.Button("浏览", GUILayout.Width(40f))){string path = EditorUtility.SaveFolderPanel("Build Path", task.buildPath, "");if (!string.IsNullOrWhiteSpace(path)){task.buildPath = path;}GUIUtility.ExitGUI();}GUILayout.EndHorizontal();//安卓平台添加其它选项if (task.buildTarget == MyBuildTarget.Android){GUILayout.BeginHorizontal();GUILayout.Label("keystore:", GUILayout.Width(70));PlayerSettings.Android.keystorePass = EditorGUILayout.PasswordField(PlayerSettings.Android.keystorePass);GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("keyalias:", GUILayout.Width(70));PlayerSettings.Android.keyaliasPass = EditorGUILayout.PasswordField(PlayerSettings.Android.keyaliasPass);GUILayout.EndHorizontal();//导出工程GUILayout.BeginHorizontal();GUILayout.Label("导出工程:", GUILayout.Width(70));EditorUserBuildSettings.exportAsGoogleAndroidProject = GUILayout.Toggle(EditorUserBuildSettings.exportAsGoogleAndroidProject, "");GUILayout.EndHorizontal();}GUILayout.EndVertical();}}}private void OnSceneAssetsList(BuildTask task){GUILayout.BeginHorizontal();GUILayout.Space(75);GUILayout.BeginVertical("Badge");for (int j = 0; j < task.sceneAssets.Count; j++){var sceneAsset = task.sceneAssets[j];GUILayout.BeginHorizontal();GUILayout.Label($"{j + 1}.", GUILayout.Width(20));task.sceneAssets[j] = EditorGUILayout.ObjectField(sceneAsset, typeof(SceneAsset), false) as SceneAsset;if (GUILayout.Button("↑", "MiniButtonLeft", GUILayout.Width(20))){if (j > 0){Undo.RecordObject(profile, "Move Up Scene Assets");var temp = task.sceneAssets[j - 1];task.sceneAssets[j - 1] = sceneAsset;task.sceneAssets[j] = temp;}}if (GUILayout.Button("↓", "MiniButtonMid", GUILayout.Width(20))){if (j < task.sceneAssets.Count - 1){Undo.RecordObject(profile, "Move Down Scene Assets");var temp = task.sceneAssets[j + 1];task.sceneAssets[j + 1] = sceneAsset;task.sceneAssets[j] = temp;}}if (GUILayout.Button("+", "MiniButtonMid", GUILayout.Width(20))){Undo.RecordObject(profile, "Add Scene Assets");task.sceneAssets.Insert(j + 1, null);break;}if (GUILayout.Button("-", "MiniButtonMid", GUILayout.Width(20))){Undo.RecordObject(profile, "Delete Scene Assets");task.sceneAssets.RemoveAt(j);break;}GUILayout.EndHorizontal();}GUILayout.EndVertical();GUILayout.EndHorizontal();}/// <summary>/// 开始打包/// </summary>/// <param name="clearBuild">清理旧的构建</param>private void OnBuild(bool clearBuild){//排序,优先当前平台的任务profile.tasks.Sort(new BuildTaskComparer());//旧版本号string oldVersion = PlayerSettings.bundleVersion;//新版本号string newVersion = GetNewVersion(profile.versionOptions);//设置新版本号PlayerSettings.bundleVersion = newVersion;try{for (int i = 0; i < profile.tasks.Count; i++){var task = profile.tasks[i];if (task.enableTask == false || task.buildTarget == MyBuildTarget.NoTarget){logsList.Add(new LogMessage(LogType.Log, $"跳过: {task}"));continue;}BuildTarget buildTarget = (BuildTarget)task.buildTarget;BuildTargetGroup targetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);BuildPlayerOptions buildPlayerOptions = SetBuildParams(targetGroup, task);EditorUtility.DisplayProgressBar("正在打包", profile.tasks[i].ToString(), (float)i + 1 / profile.tasks.Count);if (string.IsNullOrEmpty(buildPlayerOptions.locationPathName)){throw new Exception(($"无法打包 {task},产品名称可能为空"));}if (buildPlayerOptions.scenes.Length == 0){throw new Exception($"无法打包 {task},打包场景为空");}//切换平台if (buildTarget != EditorUserBuildSettings.activeBuildTarget){EditorUserBuildSettings.SwitchActiveBuildTarget(targetGroup, buildTarget);}PlayerSettings.SetScriptingBackend(targetGroup, task.scriptMode);string path = Path.GetDirectoryName(buildPlayerOptions.locationPathName);if (clearBuild && Directory.Exists(path)){Directory.Delete(path, true);}if (Directory.Exists(path) == false){Directory.CreateDirectory(path);}//开始打包var report = BuildPipeline.BuildPlayer(buildPlayerOptions);switch (report.summary.result){case UnityEditor.Build.Reporting.BuildResult.Unknown:logsList.Add(new LogMessage(LogType.Error, $"{task} 出现未知错误"));break;case UnityEditor.Build.Reporting.BuildResult.Succeeded:logsList.Add(new LogMessage(LogType.Log, $"{task} 打包耗时: {(report.summary.buildEndedAt - report.summary.buildStartedAt).TotalSeconds}秒"));break;case UnityEditor.Build.Reporting.BuildResult.Failed:string errorMsg = "\n";foreach (var file in report.GetFiles()){errorMsg += file.path + "\n";}foreach (var step in report.steps){foreach (var stepmsg in step.messages){errorMsg += "\n" + stepmsg.content;}errorMsg += "\n";}logsList.Add(new LogMessage(LogType.Error, $"{task} 打包失败: {errorMsg}"));break;case UnityEditor.Build.Reporting.BuildResult.Cancelled:logsList.Add(new LogMessage(LogType.Log, $"{task} 取消打包"));return;}//打包成功,打开目录并记录版本号if (report.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded){File.WriteAllText(string.Format("{0}/__version.txt", path), newVersion);Application.OpenURL(path);}}}catch (Exception ex){//异常情况下还原版本号PlayerSettings.bundleVersion = oldVersion;Debug.LogFormat("还原打包版本号:{0}", oldVersion);Debug.LogException(ex);}finally{EditorUtility.ClearProgressBar();Debug.LogFormat("当前打包版本号:{0}", newVersion);foreach (var log in logsList){Debug.unityLogger.Log(log.type, log.message);}logsList.Clear();}}/// <summary>/// 获取新版本号/// </summary>/// <returns></returns>private string GetNewVersion(VersionOptions options){try{Version version = new Version(PlayerSettings.bundleVersion);int major = version.Major;              //主版本int minor = version.Minor;              //次版本int build = version.Build;              //构建版本int revision = version.Revision;        //修订版本//默认不处理if (options == VersionOptions.None){//revision += 1;}else{major += options.HasFlag(VersionOptions.Major) ? 1 : 0;minor += options.HasFlag(VersionOptions.Minor) ? 1 : 0;build += options.HasFlag(VersionOptions.Build) ? 1 : 0;revision += options.HasFlag(VersionOptions.Revision) ? 1 : 0;}if (revision >= 100){build += 1;revision = 0;}if (build >= 100){minor += 1;build = 0;}if (minor >= 100){major += 1;minor = 0;}return $"{major}.{minor}.{build}.{revision}";}catch (Exception){return "1.0.0.0";}}/// <summary>/// 设置构建参数/// </summary>/// <param name="targetGroup"></param>/// <param name="task"></param>/// <returns></returns>private BuildPlayerOptions SetBuildParams(BuildTargetGroup targetGroup, BuildTask task){BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();List<string> levels = new List<string>();string[] activeLevels = EditorBuildSettingsScene.GetActiveSceneList(EditorBuildSettings.scenes);if (activeLevels.Length > 0){levels.AddRange(activeLevels);}for (int i = 0; i < task.sceneAssets.Count; i++){var scenePath = AssetDatabase.GetAssetPath(task.sceneAssets[i]);if (!string.IsNullOrEmpty(scenePath) && !levels.Contains(scenePath)){levels.Add(scenePath);}}buildPlayerOptions.scenes = levels.ToArray();buildPlayerOptions.target = (BuildTarget)task.buildTarget;buildPlayerOptions.subtarget = (int)task.buildSubtarget;buildPlayerOptions.targetGroup = targetGroup;buildPlayerOptions.options = task.buildOptions;buildPlayerOptions.locationPathName = GetBuildTargetPath(task.buildTarget, task.buildSubtarget, task.buildOptions, task.buildPath, task.productName);return buildPlayerOptions;}/// <summary>/// 获取构建路径/// </summary>/// <param name="buildTarget"></param>/// <param name="buildOptions"></param>/// <param name="buildPath"></param>/// <param name="productName"></param>/// <returns></returns>private string GetBuildTargetPath(MyBuildTarget buildTarget, StandaloneBuildSubtarget buildSubtarget, BuildOptions buildOptions, string buildPath, string productName){if (string.IsNullOrEmpty(productName)){return string.Empty;}bool isDevelopment = buildOptions.HasFlag(BuildOptions.Development);string currentDate = DateTime.Now.ToString("yyMMdd");string locationPathName = Path.Combine(buildPath, buildTarget.ToString(), buildSubtarget.ToString(), currentDate, productName);switch (buildTarget){case MyBuildTarget.StandaloneOSX:{if (isDevelopment)locationPathName += "_dev.app";elselocationPathName += ".app";}break;case MyBuildTarget.StandaloneWindows64:{if (isDevelopment)locationPathName += "_dev.exe";elselocationPathName += ".exe";}break;case MyBuildTarget.StandaloneLinux64:{if (isDevelopment)locationPathName += "_dev.x86_64";elselocationPathName += ".x86_64";}break;case MyBuildTarget.Android:{if (isDevelopment)locationPathName += $"_{currentDate}_dev";elselocationPathName += $"_{currentDate}";if (EditorUserBuildSettings.exportAsGoogleAndroidProject == false)locationPathName += ".APK";}break;case MyBuildTarget.iOS:{if (isDevelopment)locationPathName += $"_{currentDate}_dev";elselocationPathName += $"_{currentDate}";}break;}return locationPathName;}}
}

任务配置项

using System;
using System.Collections.Generic;
using UnityEditor;namespace MYTOOL.Build
{/// <summary>/// 打包的目标平台/// </summary>public enum MyBuildTarget{NoTarget = -2,//// 摘要://     Build a macOS standalone (Intel 64-bit).StandaloneOSX = 2,//// 摘要://     Build a Windows standalone.StandaloneWindows = 5,//// 摘要://     Build a Windows 64-bit standalone.StandaloneWindows64 = 19,//// 摘要://     Build a Linux 64-bit standalone.StandaloneLinux64 = 24,//// 摘要://     Build an iOS player.iOS = 9,//// 摘要://     Build an Android .apk standalone app.Android = 13,//// 摘要://     Build to WebGL platform.WebGL = 20,//// 摘要://     Build to WeixinMiniGame platform.WeixinMiniGame = 47,//// 摘要://     Build an OpenHarmony .hap standalone app.OpenHarmony = 48,}/// <summary>/// 打包工作项/// </summary>[Serializable]public class BuildTask{/// <summary>/// 是否激活/// </summary>public bool enableTask;/// <summary>/// 打包的产品名称/// </summary>public string productName;/// <summary>/// 打包的目标平台/// </summary>public MyBuildTarget buildTarget;/// <summary>/// 打包的目标子平台/// </summary>public StandaloneBuildSubtarget buildSubtarget;/// <summary>/// 打包的选项/// </summary>public BuildOptions buildOptions;/// <summary>/// 脚本模式/// </summary>public ScriptingImplementation scriptMode;/// <summary>/// 打包的保存路径/// </summary>public string buildPath;/// <summary>/// 打包的场景列表/// </summary>public List<SceneAsset> sceneAssets;/// <summary>/// 构造函数/// </summary>/// <param name="productName">产品名称</param>/// <param name="buildTarget">目标平台</param>/// <param name="buildPath">保存路径</param>public BuildTask(string productName, MyBuildTarget buildTarget, string buildPath){this.productName = productName;this.buildTarget = buildTarget;this.buildPath = buildPath;enableTask = true;buildSubtarget = StandaloneBuildSubtarget.Player;buildOptions = BuildOptions.CleanBuildCache;scriptMode = ScriptingImplementation.IL2CPP;sceneAssets = new List<SceneAsset>();}public override string ToString(){return string.Format("{0}【{1}-{2}】", productName, buildTarget, buildSubtarget);}}/// <summary>
/// BuildTask比较
/// </summary>
public class BuildTaskComparer : IComparer<BuildTask>
{public int Compare(BuildTask x, BuildTask y){if (x.enableTask && (BuildTarget)x.buildTarget == EditorUserBuildSettings.activeBuildTarget && (BuildTarget)y.buildTarget != EditorUserBuildSettings.activeBuildTarget){return -1; // x排在前}else if (y.enableTask && (BuildTarget)x.buildTarget != EditorUserBuildSettings.activeBuildTarget && (BuildTarget)y.buildTarget == EditorUserBuildSettings.activeBuildTarget){return 1; // y排在前}else if (x.enableTask && y.enableTask && x.buildTarget == y.buildTarget){return 0; //保持当前}else if (x.enableTask && x.buildTarget == y.buildTarget){return -1; // x排在前}else if (y.enableTask && x.buildTarget == y.buildTarget){return 1; // y排在前}else{return x.buildTarget.ToString().CompareTo(y.buildTarget.ToString());}}
}

效果图,可以将它锁定在这里,方便后面使用
使用也很简单,选择打包的平台,并设置一些参数。点击开始打包或清理并打包。
注意:打包场景字段是额外添加, 每次打包都会先获取Build Settings里激活的场景,并添加上打包场景中的设置
在这里插入图片描述

其它解释:
有些字段是直接使用Unity的,所以数据是共享的,比如安卓特有的选项,一个地方修改,其它相对应的位置也发生改变。
构建目录格式:打包路径+打包平台+打包子平台+日期(yyMMdd)
为什么添加打包子平台字段,因为我的项目中需要打包服务端(Dedicated Server)

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

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

相关文章

JAVA实现五子棋小游戏(附源码)

文章目录 一、设计来源捡金币闯关小游戏讲解1.1 主界面1.2 黑棋胜利界面1.3 白棋胜利界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/145161039 JA…

Flink概述

一、Flink是什么 二、Flink特点 三、Flink vs SparkStreaming 表 Flink 和 Streaming对比 Flink Streaming 计算模型 流计算 微批处理 时间语义 事件时间、处理时间 处理时间 窗口 多、灵活 少、不灵活&#xff08;窗口必须是批次的整数倍&#xff09; 状态 有 …

基于SpringBoot的企业级工位管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的企业级工位管理系统采用前后端分离架构方式&#xff0c;系统设计了管理员、员工两种角色&#xff0c;系统实现了用户登录与注册、个人中心、员工管理、部门信息管理、工位信息管理、使用情况管理、工位分配管理等功能。 技术选型 开发工具&…

Linux系统离线部署MySQL详细教程(带每步骤图文教程)

1、登录官网下载对应的安装包 MySQL :: Developer Zone 2、将压缩包上传到服务器上&#xff0c;这里直接上传到/usr/local路径上 使用sftp工具上传到/usr/local目录上 3、解压压缩包 tar -xf mysql-8.0.39-linux-glibc2.17-x86_64.tar.xz 4、将mysql-8.0.39-linux-glibc2.17…

leetcode刷题记录(六十一)——73. 矩阵置零

&#xff08;一&#xff09;问题描述 73. 矩阵置零 - 力扣&#xff08;LeetCode&#xff09;73. 矩阵置零 - 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 [http://baike.baidu.com/item/%E5%8E%9F%E5%9…

人机交互(包含推荐软件)

视觉交互、语音交互、笔式交互、触觉交互、虚拟环境交互。 主要的研究方面包括&#xff1a;人机交互界面表示模型与设计方法、可用性工程、可用性评估模型和方法、多模态智能交互技术、智能交互认知技术、语音识别交互、web界面交互设计、移动界面交互设计。 交互设计流程&am…

解锁未来情感科技:AI 机器人 Ropet 搭载的前沿智能黑科技

2025年的国际消费电子产品展览会&#xff08;CES&#xff09;上&#xff0c;一只可爱的“毛绒玩具”成了全场焦点。 当然&#xff0c;这并不是一个单纯的玩偶&#xff0c;而是和《超能陆战队》的大白一样温暖的陪伴机器人。 相信有很多人和小编一样&#xff0c;当年看完《超能…

HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载,Scroll滚动到顶部

HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载 效果展示 使用方法 import LoadingText from "../components/LoadingText" import PageToRefresh from "../components/PageToRefresh" import FooterBar from "../components/…

Flink(八):DataStream API (五) Join

1. Window Join Window join 作用在两个流中有相同 key 且处于相同窗口的元素上。这些窗口可以通过 window assigner 定义&#xff0c;并且两个流中的元素都会被用于计算窗口的结果。两个流中的元素在组合之后&#xff0c;会被传递给用户定义的 JoinFunction 或 FlatJoinFunct…

Flink CDC 在阿里云实时计算Flink版的云上实践

摘要&#xff1a;本文整理自阿里云高级开发工程师&#xff0c;Apache Flink Committer 阮航老师在 Flink Forward Asia 2024 生产实践&#xff08;三&#xff09;专场中的分享&#xff0c;主要分为以下四个方面&#xff1a; Flink CDC & 实时计算 Flink CDC YAML 核心功能…

如何使用wireshark 解密TLS-SSL报文

目录 前言 原理 操作 前言 现在网站都是https 或者 很多站点都支持 http2。这些站点为了保证数据的安全都通过TLS/SSL 加密过&#xff0c;用wireshark 并不能很好的去解析报文&#xff0c;我们就需要用wireshark去解密这些报文。我主要讲解下mac 在 chrome 怎么配置的&…

【大模型系列篇】数字人音唇同步模型——腾讯开源MuseTalk

之前有一期我们体验了阿里开源的半身数字人项目EchoMimicV2&#xff0c;感兴趣的小伙伴可跳转至《AI半身数字人开箱体验——开源项目EchoMimicV2》&#xff0c;今天带大家来体验腾讯开源的数字人音唇同步模型MuseTalk。 MuseTalk 是一个实时高品质音频驱动的唇形同步模型&#…

C++基础入门(二)

目录 前言 一、重载 1.函数重载 2.运算符重载 二、构造函数 1.什么是构造函数 2.带参数的构造函数 3.使用初始化列表 4.this关键字 5.new关键字 三、析构函数 1.什么是析构函数 四、静态成员变量 1.静态成员的定义 2.静态成员变量的作用 五、继承 1.继承基本概…

基础vue3前端登陆注册界面以及主页面设计

1.下载依赖 "element-plus/icons": "^0.0.11", "element-plus/icons-vue": "^2.3.1", "fortawesome/fontawesome-svg-core": "^6.7.2", "fortawesome/free-solid-svg-icons": "^6.7.2", &quo…

Mybatis-Plus:乐观锁与悲观锁

文章目录 一、场景二、乐观锁与悲观锁三、模拟修改冲突3.1 数据库中增加商品表3.2 添加数据3.3 添加实体3.4 添加mapper3.5 测试 四、乐观锁实现流程4.1 Mybatis-Plus实现乐观锁 一、场景 一件商品&#xff0c;成本价是80元&#xff0c;售价是100元。老板先是通知小李&#xf…

卷积神经网络——食物分类

整体框架&#xff1a; 导入库 导入了各种必需的Python库&#xff0c;用于数据处理、图像读取、模型构建和训练。 设置随机种子 seed_everything: 用于设置所有随机数生成器的种子&#xff0c;确保每次运行时的结果都是相同的。 图像预处理&#xff08;transform&#xff09; 对…

Jmeter配置服务代理器 Proxy(二)

1.创建脚本记录器 2.配置&#xff1a;Jmeter代理、端口、记录目标等 3.配置谷歌浏览器代理 浏览器配置代理的详细教程可参考&#xff1a;使用whistle代理-CSDN博客 4.启动Jmeter记录器 点击ok后弹出这个界面&#xff0c;生成了证书&#xff1a; 5.给浏览器安装Jmeter代理的证书…

灰色预测and BP神经网络 (详细上手使用)

灰色预测模型 基础知识&#xff1a; 白色系统&#xff1a;系统的信息是完全明确的。 灰色系统&#xff1a;系统的部分信息已知&#xff0c;部分信息未知。 黑色系统&#xff1a;系统的内部信息是未知的。 灰色预测是对既含有已知信息又含有不确定信息的系统进行预则&#xf…

mac 安装 node

brew versions node // 安装 node brew versions node14 // 安装指定版本 卸载node: sudo npm uninstall npm -g sudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.* sudo rm -rf /usr/local/include/node /Users/$USER/.npm su…

【Unity】unity3D 调用LoadSceneAsync 场景切换后比较暗 部门材质丢失

解决方法&#xff1a;两个场景使用同样灯光 现象 直接进入第二个场景是可以正常显示 调用LoadSceneAsync来切换后&#xff0c;第二个场景出现比较暗的情况 解决方法&#xff1a;两个场景使用同样灯光&#xff0c;在loading 的场景中加入灯光。 Light—Directional Light 如果…