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

Unity C#脚本的热更新

以下内容是根据Unity 2020.1.0f1版本进行编写的

目前游戏开发厂商主流还是使用lua框架来进行热更,如xlua,tolua等,也有的小游戏是直接整包更新,这种小游戏的包体很小,代码是用C#写的;还有的游戏就是通过热更C#代码来实现热更新的。本篇就来学习一下。

1、热更C#代码的方法

AI时代,遇事不决,先问AI,下面就是百度问AI的答案:
在这里插入图片描述
在这里插入图片描述
可以看到,AI给出的答案大部分都是将C#代码编译成dll,然后在需要时动态加载对应的dll来实现代码的热更新的。下面就来尝试一下。

2、使用ILRuntime框架

ILRuntime官方文档:https://ourpalm.github.io/ILRuntime/public/v1/guide/tutorial.html
在这里插入图片描述
“scopedRegistries”: [
{
“name”: “ILRuntime”,
“url”: “https://registry.npmjs.org”,
“scopes”: [
“com.ourpalm”
]
}
],
在这里插入图片描述
如上图一,新建一个unity项目,然后在工程目录Packages下的manifest文件中增加图一框住的代码,保存。然后关闭Unity项目再重新打开。点击菜单栏Window->Package Manager打开PackageManager窗口,切换Packages切换到My Registries,可以看到刚刚加上去的ILRuntime包(如图二)。

选中后点击右下角箭头所指的install按钮就可以导入包体了(这里因为我已经导入过了所以显示的按钮是Remove)。这个包体还有示例Demo,有需要也可以一并导入到工程中。
在这里插入图片描述
导入的Demo用的是unsafe代码,导入后可能会有很多报错,点击菜单栏Edit->Project Settings打开ProjectSettings窗口,设置player页签中的OtherSettings,使工程允许使用unsafe代码。
在这里插入图片描述
接着尝试运行Demo,直接运行会报错,需要生成dll。
先用VisualStudio打开一次项目工程的sln文件,再打开下载的Demo包内工程的sln文件(如上图)。
在这里插入图片描述
在这里插入图片描述
在打开的HotFix_Project中,点击菜单栏生成->生成解决方案按钮,等待VS左下角出现生成成功提示。
在这里插入图片描述
此时回到Unity随便运行一个Examples场景,都不会有报错了。

在这里插入图片描述
接下来简单尝试一下,先做一个简单的界面(如上图),功能是点击下方左右两个按钮,点击哪边的按钮,就在中间的文本显示点击了哪边的按钮。

using UnityEngine;
using System.Collections;
using System.IO;
using System;public class AppCommon : MonoBehaviour
{static AppCommon instance;System.IO.MemoryStream fs;System.IO.MemoryStream p;public bool isLoaded = false;public static AppCommon Instance{get { return instance; }}//AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个//大家在正式项目中请全局只创建一个AppDomainpublic ILRuntime.Runtime.Enviorment.AppDomain appdomain;//在awake方法中先加载好appdomainvoid Awake(){instance = this;StartCoroutine(LoadHotFixAssembly());}IEnumerator LoadHotFixAssembly(){//首先实例化ILRuntime的AppDomain,AppDomain是一个应用程序域,每个AppDomain都是一个独立的沙盒appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();//正常项目中应该是自行从其他地方下载dll,或者打包在AssetBundle中读取,平时开发以及为了演示方便直接从StreammingAssets中读取,//正式发布的时候需要大家自行从其他地方读取dll//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
#if UNITY_ANDROIDWWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll");
#elseWWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endifwhile (!www.isDone)yield return null;if (!string.IsNullOrEmpty(www.error))UnityEngine.Debug.LogError(www.error);byte[] dll = www.bytes;www.Dispose();//PDB文件是调试数据库,如需要在日志中显示报错的行号,则必须提供PDB文件,不过由于会额外耗用内存,正式发布时请将PDB去掉,下面LoadAssembly的时候pdb传null即可
#if UNITY_ANDROIDwww = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb");
#elsewww = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
#endifwhile (!www.isDone)yield return null;if (!string.IsNullOrEmpty(www.error))UnityEngine.Debug.LogError(www.error);byte[] pdb = www.bytes;fs = new MemoryStream(dll);p = new MemoryStream(pdb);try{appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());}catch{Debug.LogError("加载热更DLL失败,请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");}InitializeILRuntime();OnHotFixLoaded();}private void OnDestroy(){fs.Close();p.Close();}unsafe void InitializeILRuntime(){
#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)//由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profilerappdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif//这里做一些ILRuntime的注册appdomain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());appdomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>{return new UnityEngine.Events.UnityAction(() =>{((Action)act)();});});ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain);}unsafe void OnHotFixLoaded(){isLoaded = true;Debug.Log("AppDomain Loaded");}
}

首先在Unity中新建一个名叫AppCommon的脚本,用于定义一些项目内通用的单例类。这里主要是定义一个叫appdomain的类,在Demo中是建议全局只创建一个的。
每个Demo都会有加载这个appdpmain的方法,将代码复制到AppCommon类中,在其InitializeILRuntime方法中注册好全部需要用到的事件等。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;public class MyView1 : MonoBehaviour
{static MyView1 instance;public static MyView1 Instance{get { return instance; }}void Start(){instance = this;StartCoroutine(LoadHotFixAssembly());}IEnumerator LoadHotFixAssembly(){while(!AppCommon.Instance.isLoaded){yield return 0;}OnHotFixLoaded();}void OnHotFixLoaded(){SetupCLRRedirection();SetupCLRRedirection2();AppCommon.Instance.appdomain.Invoke("HotFix_Project.TestMyView1", "ShowView", null, gameObject);}unsafe void SetupCLRRedirection(){//这里面的通常应该写在InitializeILRuntime,这里为了演示写这里var arr = typeof(GameObject).GetMethods();foreach (var i in arr){if (i.Name == "AddComponent" && i.GetGenericArguments().Length == 1){AppCommon.Instance.appdomain.RegisterCLRMethodRedirection(i, AddComponent);}}}unsafe void SetupCLRRedirection2(){//这里面的通常应该写在InitializeILRuntime,这里为了演示写这里var arr = typeof(GameObject).GetMethods();foreach (var i in arr){if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1){AppCommon.Instance.appdomain.RegisterCLRMethodRedirection(i, GetComponent);}}}unsafe static StackObject* AddComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj){//CLR重定向的说明请看相关文档和教程,这里不多做解释ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;var ptr = __esp - 1;//成员方法的第一个参数为thisGameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;if (instance == null)throw new System.NullReferenceException();__intp.Free(ptr);var genericArgument = __method.GenericArguments;//AddComponent应该有且只有1个泛型参数if (genericArgument != null && genericArgument.Length == 1){var type = genericArgument[0];object res;if (type is CLRType){//Unity主工程的类不需要任何特殊处理,直接调用Unity接口res = instance.AddComponent(type.TypeForCLR);}else{//热更DLL内的类型比较麻烦。首先我们得自己手动创建实例var ilInstance = new ILTypeInstance(type as ILType, false);//手动创建实例是因为默认方式会new MonoBehaviour,这在Unity里不允许//接下来创建Adapter实例var clrInstance = instance.AddComponent<MonoBehaviourAdapter.Adaptor>();//unity创建的实例并没有热更DLL里面的实例,所以需要手动赋值clrInstance.ILInstance = ilInstance;clrInstance.AppDomain = __domain;//这个实例默认创建的CLRInstance不是通过AddComponent出来的有效实例,所以得手动替换ilInstance.CLRInstance = clrInstance;res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstanceclrInstance.Awake();//因为Unity调用这个方法时还没准备好所以这里补调一次}return ILIntepreter.PushObject(ptr, __mStack, res);}return __esp;}unsafe static StackObject* GetComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj){//CLR重定向的说明请看相关文档和教程,这里不多做解释ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;var ptr = __esp - 1;//成员方法的第一个参数为thisGameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;if (instance == null)throw new System.NullReferenceException();__intp.Free(ptr);var genericArgument = __method.GenericArguments;//AddComponent应该有且只有1个泛型参数if (genericArgument != null && genericArgument.Length == 1){var type = genericArgument[0];object res = null;if (type is CLRType){//Unity主工程的类不需要任何特殊处理,直接调用Unity接口res = instance.GetComponent(type.TypeForCLR);}else{//因为所有DLL里面的MonoBehaviour实际都是这个Component,所以我们只能全取出来遍历查找var clrInstances = instance.GetComponents<MonoBehaviourAdapter.Adaptor>();for (int i = 0; i < clrInstances.Length; i++){var clrInstance = clrInstances[i];if (clrInstance.ILInstance != null)//ILInstance为null, 表示是无效的MonoBehaviour,要略过{if (clrInstance.ILInstance.Type == type){res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstancebreak;}}}}return ILIntepreter.PushObject(ptr, __mStack, res);}return __esp;}
}

然后还是在Unity中新建一个名叫MyView1的脚本,这个脚本的功能其实只是等待上述的appdomain类加载完之后,然后调用加载的dll内部的类以及方法即可。实际逻辑是写到HotFix_Project里的,也只有HotFix_Project里的代码能热更。
这里写的比较简单,大部分代码是抄MonoBehaviourDemo的,实际上就是需要使appdomain注册自定义实现AddComponent方法和GetComponent方法。这样,在热更的代码中就可以通过AddComponent方法来把C#代码以组件的形式挂载到对应的GameObject中。
在这里插入图片描述
在这里插入图片描述
最后就是热更代码部分。
因为要用到Unity UI部分的方法,所以需要将UnityEngine.UI的dll复制过来并加入到HotFix_Project的引用中。(先把dll复制到UnityDlls目录下,然后再在VS上右键添加引用,在打开的窗口中点击右下角浏览,然后选择复制的dll文件即可)

using UnityEngine;
using UnityEngine.UI;namespace HotFix_Project
{class TestMyView1 : MonoBehaviour{private Button btn1;private Button btn2;private Text text;void Start(){btn1 = gameObject.transform.Find("btn1").GetComponent<Button>();btn2 = gameObject.transform.Find("btn2").GetComponent<Button>();btn1.onClick.AddListener(OnClickBtn1);btn2.onClick.AddListener(OnClickBtn2);text = gameObject.transform.Find("Text").GetComponent<Text>();}void OnClickBtn1(){text.text = "点击了左边的按钮";}void OnClickBtn2(){text.text = "点击了右边的按钮";}public static void ShowView(GameObject go){go.AddComponent<TestMyView1>();}}
}

接着在HotFix_Project新建一个名叫TestMyView1的C#脚本,实现上面描述的功能就可以了。这一部分代码就是可热更的。
写好代码后,点击HotFix_Project菜单栏的生成->重新生成解决方案按钮,即可运行Unity。
效果如下:
在这里插入图片描述
最后,如果需要实现热更,就是在AppCommon加载appdomain的协程中,修改一下加载的文件位置(如上图框住的部分,这里我没试)。
所以实际上,C#代码热更就是将代码编译成dll,然后在加载后以反射调用或者委托等方式来调用写在dll内部的类和方法。因此每次热更只需要重新编译生成dll就可以了。
在这里插入图片描述
在这里插入图片描述
此外,该框架还能生成一些CLR绑定的代码,用于减少反射调用的消耗。(实际上这里的做法有点类似于tolua框架)

代码仓库地址:https://gitee.com/chj–project/CSharpHotUpdate

相关文章:

Unity C#脚本的热更新

以下内容是根据Unity 2020.1.0f1版本进行编写的   目前游戏开发厂商主流还是使用lua框架来进行热更&#xff0c;如xlua&#xff0c;tolua等&#xff0c;也有的小游戏是直接整包更新&#xff0c;这种小游戏的包体很小&#xff0c;代码是用C#写的&#xff1b;还有的游戏就是通过…...

监督学习之逻辑回归

逻辑回归&#xff08;Logistic Regression&#xff09; 逻辑回归是一种用于二分类&#xff08;binary classification&#xff09;问题的统计模型。尽管其名称中有“回归”二字&#xff0c;但逻辑回归实际上用于分类任务。它的核心思想是通过将线性回归的输出映射到一个概率值…...

深度优先算法(DFS)洛谷P1683-入门

虽然洛谷是有题解的,但是你如果直接看得懂题解,你也不会来看这篇文章.. 这些代码均是我记录自身成长的记录,有写的不好的地方请谅解&#xff01; 先上代码&#xff1a; #include <iostream> #include <vector> #include<iomanip> #include<cstdio&…...

Python数据分析基础

本文介绍了Python在数据分析中的应用&#xff0c;包括数据读取、清洗、处理和分析的基本操作。通过使用Pandas和Numpy库&#xff0c;我们可以高效地处理大量数据&#xff0c;并利用Matplotlib和Seaborn库进行数据可视化。 1. 引言 Python因其简洁的语法和强大的库支持&#x…...

《企业自设2-软件测试》线下课day3: 006扩展虚拟机

1.win11 修改hosts无权限 分别再cmd终端输入以下两行代码&#xff1a; C:\Windows\System32\drivers\etcnotepad hosts 2.先保存快照&#xff01;&#xff01;&#xff01; 3.关闭虚拟机&#xff0c;将内存&#xff0c;CPU进行修改 就是再这个位置修改&#xff1a; 4.运…...

配置和排查 Lombok 在 IDEA 中使用的详细步骤

在日常开发中&#xff0c;Java 代码常常需要大量的样板代码&#xff0c;比如 getter、setter、toString 等方法。Lombok 是一个 Java 库&#xff0c;可以通过注解的方式&#xff0c;自动生成这些常见的代码&#xff0c;从而让代码更加简洁、清晰。比如&#xff0c;我们可以通过…...

JavaWeb合集18-接口管理Swager

十八、接口管理 1、Swager 使用Swagger你只需要按照它的规范去定义接口及接口相关的信息&#xff0c;就可以做到生成接口文档&#xff0c;以及在线接口调试页面。 官网: https://swagger.io/ Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。 <dependency&g…...

背包九讲——二维费用背包问题

目录 二维费用背包问题 问题描述&#xff1a; 解决方法&#xff1a; 方法一&#xff1a; 代码实现&#xff1a; 方法二&#xff1a; 代码实现&#xff1a; 背包问题第五讲——二维费用背包问题 背包问题是一类经典的组合优化问题&#xff0c;通常涉及在限定容量的背包中…...

【mysql进阶】4-7. 通用表空间

通⽤表空间 - General Tablespace 1 通⽤表空间的作⽤和特性&#xff1f; ✅ 解答问题 通⽤表空间是使⽤ CREATE tablespace 语法创建的共享InnoDB表空间 通⽤表空间能够存储多个表的数据&#xff0c;与系统表空间类似也是共享表空间&#xff1b; 服务器运⾏时会把表空间元数…...

2024 年互联网大厂 1300 多道 JAVA 面试题汇总,包含了程序员的所有技术点

作为一个 Java 程序员&#xff0c;你平时总是陷在业务开发里&#xff0c;每天噼里啪啦忙敲着代码&#xff0c;上到系统开发&#xff0c;下到 Bug 修改&#xff0c;你感觉自己无所不能。然而偶尔的一次聚会&#xff0c;你听说和自己一起出道的同学早已经年薪 50 万&#xff0c;而…...

【开源免费】基于SpringBoot+Vue.JS在线文档管理系统(JAVA毕业设计)

本文项目编号 T 038 &#xff0c;文末自助获取源码 \color{red}{T038&#xff0c;文末自助获取源码} T038&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…...

Linux资源与网络请求

参数说明&#xff1a; d : 改变显示的更新速度&#xff0c;或是在交谈式指令列( interactive command)按 sq : 没有任何延迟的显示速度&#xff0c;如果使用者是有 superuser 的权限&#xff0c;则 top 将会以最高的优先序执行c : 切换显示模式&#xff0c;共有两种模式&#…...

RPA技术重塑企业自动化的未来

1. RPA定义与原理 1.1 机器人流程自动化(RPA)概念 机器人流程自动化&#xff08;Robotic Process Automation&#xff0c;简称RPA&#xff09;是一种软件技术&#xff0c;通过模拟人类用户在计算机界面上的操作来执行重复性的业务流程任务。RPA软件机器人能够自动执行基于规则…...

使用RabbitMQ实现延迟消息的完整指南

在分布式系统中&#xff0c;消息队列通常用于解耦服务&#xff0c;RabbitMQ是一个广泛使用的消息队列服务。延迟消息&#xff08;也称为延时队列或TTL消息&#xff09;是一种常见的场景应用&#xff0c;特别适合处理某些任务在一段时间后执行的需求&#xff0c;如订单超时处理、…...

阿里员工:阿里工作7年至少得P7吧,快的都P8了,年薪100W是正常的,80才算及格...

上一篇&#xff1a;一线体面男的收入 年薪64W的阿里蚂蚁员工爆料&#xff1a;在阿里&#xff0c;工作7年至少得P7&#xff0c;快的都P8了&#xff0c;年薪100W才正常&#xff0c;80分才算及格。 其实&#xff0c;在大厂工作&#xff0c;听起来风光无限&#xff0c;但个中滋味&a…...

Django进一步掌握(10月22日)

一、请求响应对象 请求对象request 响应对象HttpResponse 二、HttpResponse常用属性 status设置HTTP响应状态码 status_code查询HTTP响应状态码 content_type设置响应的类型 write()写入响应内容 三、重定向 1、实现URl访问的重定向 &#xff08;1&#xff09;使用Ht…...

C++从入门到起飞之——红黑树封装map和set 全方位剖析!

目录 1、map和set的整体框架 2、map和set迭代器的实现 3、map支持[] 4、完整源码 set.h map.h RBTree.h 1、map和set的整体框架 因为map和set的底层都是红黑树&#xff0c;所以我们考虑用一个红黑树的类模版去实例化map和set对象&#xff01;不过&#xff0c;map节点中存…...

【javax maven项目缺少_Maven的依赖管理 引入依赖】

javax maven项目缺少_Maven的依赖管理 引入依赖 Maven的依赖管理 - 引入依赖依赖管理(引入依赖)导入依赖 https://blog.csdn.net/weixin_28932089/article/details/112381468 Maven的依赖管理 - 引入依赖 依赖管理(引入依赖) 能够掌握依赖引入的配置方式 导入依赖 导入依赖练…...

手搓一个定时器

目录 1.什么是定时器 2.计时器的使用 3.手搓定时器 3.1定义一个TimerTask类 3.2定义一个Timer类 3.3实现schedule方法 3.4实现Timer的构造方法 3.4.1随时随地查看优先级队列中是否有任务要执行 3.4.2获取队首任务&#xff0c;并判断是否到执行时间 3.4.3到达执行时间…...

AI提示词工程优化Prompt-GPT使用手册(科普一键收藏史上最强攻略)

Prompt(提示)&#xff0c;最初是 NLP 研究者为下游任务设计出来的一种任务专属的输入形式或模板。在 ChatGPT 引发大语言模型新时代之后&#xff0c;Prompt 指与大模型交互输入的代称。 随着大模型的进展&#xff0c;Prompt Engineering是一个持久的探索过程。 目录 什么是提示…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

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;用于…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...