C# Unity FSM 状态机
C# Unity FSM 状态机
使用状态机可以降低代码耦合性,并且可以优化代码可读性,方便团队协作等。
对于游戏开发内容来讲游戏开发的流程控制玩家动画都可以使用FSM有限状态机来实现。
1.FsmState
每个状态的基类,泛型参数表示所拥有者
public abstract class FsmState<T> where T : class
{protected internal abstract void OnInit(IFsm<T> fsm);protected internal abstract void OnEnter(IFsm<T> fsm);protected internal abstract void OnUpdate(IFsm<T> fsm);protected internal abstract void OnLeave(IFsm<T> fsm);protected internal abstract void OnDestroy(IFsm<T> fsm);protected void ChangeState<TState>(IFsm<T> fsm) where TState : FsmState<T>{Fsm<T> fsmImplement = (Fsm<T>)fsm;if(fsmImplement == null){throw new Exception("FSM is invalid.");}fsmImplement.ChangeState<TState>();}protected void ChangeState(IFsm<T> fsm, Type stateType){Fsm<T> fsmImplement = (Fsm<T>)fsm;if (fsmImplement == null){throw new Exception("FSM is invalid.");}if (stateType == null){throw new Exception("State type is invalid.");}if (!typeof(FsmState<T>).IsAssignableFrom(stateType)){throw new Exception("State type is invalid.");}fsmImplement.ChangeState(stateType);}
}
2.IFsm
有限状态机的接口
public interface IFsm<T> where T : class
{string Name{get;}string FullName{get;}T Owner{get;}int FsmStateCount{get;}bool IsRunning{get;}bool IsDestroyed{get;}FsmState<T> CurrentState{get;}float CurrentStateTime{get;}void Start<TState>() where TState : FsmState<T>;bool HasState<TState>() where TState : FsmState<T>;TState GetState<TState>() where TState : FsmState<T>;FsmState<T> GetState(Type stateType);FsmState<T>[] GetAllStates();
}
3.IFsmManager
有限状态机管理器接口
public interface IFsmManager
{int Count { get; }bool HasFsm<T>() where T : class;bool HasFsm(Type ownerType);bool HasFsm<T>(string name) where T : class;bool HasFsm(Type ownerType, string name);IFsm<T> GetFsm<T>() where T : class;IFsm<T> GetFsm<T>(string name) where T : class;IFsm<T> CreateFsm<T>(T owner, params FsmState<T>[] fsmStates) where T : class;IFsm<T> CreateFsm<T>(string name, T owner, params FsmState<T>[] states) where T : class;IFsm<T> CreateFsm<T>(T owner, List<FsmState<T>> states) where T : class;IFsm<T> CreateFsm<T>(string name, T owner, List<FsmState<T>> states) where T : class;bool DestroyFsm<T>() where T : class;bool DestroyFsm(Type ownerType);bool DestroyFsm<T>(string name) where T : class;bool DestroyFsm(Type ownerType, string name);bool DestroyFsm<T>(IFsm<T> fsm) where T : class;
}
4.FsmBase
有限状态机的基类
public abstract class FsmBase
{private string m_Name;public FsmBase(){m_Name = string.Empty;}public string Name{get{return m_Name;}protected set{m_Name = value ?? string.Empty;}}public string FullName{get{return $"{OwnerType.FullName}.{Name}";}}public abstract Type OwnerType{get;}public abstract int FsmStateCount{get;}public abstract bool IsRunning{get;}public abstract bool IsDestroyed{get;}public abstract string CurrentStateName{get;}public abstract float CurrentStateTime{get;}public abstract void Update();public abstract void Shutdown();
}
5.Fsm
状态机类
public class Fsm<T> : FsmBase, IFsm<T> where T : class
{private T m_Owner;private readonly Dictionary<Type, FsmState<T>> m_States;private FsmState<T> m_CurrentState;private float m_CurrentStateTime;private bool m_IsDestroyed;public Fsm(){m_Owner = null;m_States = new Dictionary<Type, FsmState<T>>();m_CurrentState = null;m_CurrentStateTime = 0f;m_IsDestroyed = true;}public T Owner => m_Owner;public FsmState<T> CurrentState => m_CurrentState;public override Type OwnerType => typeof(T);public override int FsmStateCount => m_States.Count;public override bool IsRunning => m_CurrentState != null;public override bool IsDestroyed => m_IsDestroyed;public override string CurrentStateName => m_CurrentState != null ? m_CurrentState.GetType().FullName : null;public override float CurrentStateTime => m_CurrentStateTime;public static Fsm<T> Create(string name,T owner,params FsmState<T>[] states){if(owner == null){throw new Exception("FSM owner is invalid.");}if(states== null|| states.Length < 1){throw new Exception("FSM states is invalid.");}Fsm<T> fsm = Pool<Fsm<T>>.Rent();fsm.Name = name;fsm.m_Owner = owner;fsm.m_IsDestroyed = false;foreach (FsmState<T> oneState in states){if(oneState == null){throw new Exception("FSM states is invalid.");}Type stateType = oneState.GetType();if (fsm.m_States.ContainsKey(stateType)){throw new Exception($"{stateType} state is already exist");}fsm.m_States.Add(stateType, oneState);oneState.OnInit(fsm);}return fsm;}public static Fsm<T> Create(string name,T owner,List<FsmState<T>> states){if (owner == null){throw new Exception("FSM owner is invalid.");}if (states == null || states.Count < 1){throw new Exception("FSM states is invalid.");}Fsm<T> fsm = Pool<Fsm<T>>.Rent();fsm.Name = name;fsm.m_Owner = owner;fsm.m_IsDestroyed = false;foreach (FsmState<T> oneState in states){if (oneState == null){throw new Exception("FSM states is invalid.");}Type stateType = oneState.GetType();if (fsm.m_States.ContainsKey(stateType)){throw new Exception($"{stateType} state is already exist");}fsm.m_States.Add(stateType, oneState);oneState.OnInit(fsm);}return fsm;}public FsmState<T>[] GetAllStates(){int index = 0;FsmState<T>[] arr = new FsmState<T>[m_States.Count];foreach (FsmState<T> fsmState in m_States.Values){arr[index++] = fsmState;}return arr;}public bool HasState<TState>() where TState : FsmState<T>{return m_States.ContainsKey(typeof(TState));}public override void Shutdown(){Pool<Fsm<T>>.Return(this, (fsm) =>{if(m_CurrentState != null){m_CurrentState.OnLeave(this);}foreach (FsmState<T> oneState in m_States.Values){oneState.OnDestroy(this);}Name = null;m_Owner = null;m_States.Clear();m_CurrentState = null;m_CurrentStateTime = 0f;m_IsDestroyed = true;});}public void Start<TState>() where TState : FsmState<T>{if (IsRunning){throw new Exception("FSM is running, can not start again.");}FsmState<T> state = GetState<TState>();if (state == null){throw new Exception("can not start state");}m_CurrentStateTime = 0f;m_CurrentState = state;m_CurrentState.OnEnter(this);}public void Start(Type stateType){if (IsRunning){throw new Exception("FSM is running, can not start again.");}if (stateType == null){throw new Exception("State type is invalid.");}if (!typeof(FsmState<T>).IsAssignableFrom(stateType)){throw new Exception("State type is invalid.");}FsmState<T> state = GetState(stateType);if (state == null){throw new Exception("FSM can not start state which is not exist.");}m_CurrentStateTime = 0f;m_CurrentState = state;m_CurrentState.OnEnter(this);}public override void Update(){m_CurrentStateTime += Time.deltaTime;m_CurrentState.OnUpdate(this);}public TState GetState<TState>() where TState : FsmState<T>{if (m_States.TryGetValue(typeof(TState), out FsmState<T> fsmState)){return (TState)fsmState;}return null;}public FsmState<T> GetState(Type stateType){if (stateType == null){throw new Exception("State type is invalid.");}if (!typeof(FsmState<T>).IsAssignableFrom(stateType)){throw new Exception("State type is invalid.");}if (m_States.TryGetValue(stateType, out FsmState<T> fsmState)){return fsmState;}return null;}public void ChangeState<TState>(){ChangeState(typeof(TState));}public void ChangeState(Type stateType){if (m_CurrentState == null){throw new Exception("Current state is invalid.");}FsmState<T> state = GetState(stateType);if (state == null){throw new Exception("FSM can not change state which is not exist.");}m_CurrentState.OnLeave(this);m_CurrentStateTime = 0f;m_CurrentState = state;m_CurrentState.OnEnter(this);}
}
6.FsmManager
状态机管理器
public class FsmManager : Singleton<FsmManager>,IFsmManager,IUpdateSingleton
{private readonly Dictionary<string, FsmBase> m_FsmDic;private readonly List<FsmBase> m_TempFsms;public FsmManager(){m_FsmDic = new Dictionary<string, FsmBase>();m_TempFsms = new List<FsmBase>();}public int Count => m_FsmDic.Count;public IFsm<T> CreateFsm<T>(T owner, params FsmState<T>[] fsmStates) where T : class{return CreateFsm(string.Empty, owner, fsmStates);}public IFsm<T> CreateFsm<T>(string name, T owner, params FsmState<T>[] states) where T : class{if (HasFsm<T>(name)){throw new Exception("Already exist FSM");}Fsm<T> fsm = Fsm<T>.Create(name, owner, states);m_FsmDic.Add(fsm.FullName, fsm);return fsm;}public IFsm<T> CreateFsm<T>(T owner, List<FsmState<T>> states) where T : class{return CreateFsm(string.Empty, owner, states);}public IFsm<T> CreateFsm<T>(string name, T owner, List<FsmState<T>> states) where T : class{if (HasFsm<T>(name)){throw new Exception("Already exist FSM");}Fsm<T> fsm = Fsm<T>.Create(name, owner, states);m_FsmDic.Add(fsm.FullName, fsm);return fsm;}public bool DestroyFsm<T>() where T : class{return InternalDestroyFsm($"{typeof(T).FullName}.{string.Empty}");}public bool DestroyFsm(Type ownerType){if (ownerType == null){throw new Exception("Owner type is invalid.");}return InternalDestroyFsm($"{ownerType.FullName}.{string.Empty}");}public bool DestroyFsm<T>(string name) where T : class{return InternalDestroyFsm($"{typeof(T).FullName}.{name}");}public bool DestroyFsm(Type ownerType, string name){if (ownerType == null){throw new Exception("Owner type is invalid.");}return InternalDestroyFsm($"{ownerType.FullName}.{name}");}public bool DestroyFsm<T>(IFsm<T> fsm) where T : class{if (fsm == null){throw new Exception("FSM is invalid.");}return InternalDestroyFsm(fsm.FullName);}public IFsm<T> GetFsm<T>() where T : class{return (IFsm<T>)InternalGetFsm($"{typeof(T).FullName}.{string.Empty}");}public IFsm<T> GetFsm<T>(string name) where T : class{return (IFsm<T>)InternalGetFsm($"{typeof(T).FullName}.{name}");}public bool HasFsm<T>() where T : class{return InternalHasFsm($"{typeof(T).FullName}.{string.Empty}");}public bool HasFsm(Type ownerType){if(ownerType == null){throw new Exception("Owner type is invalid.");}return InternalHasFsm($"{ownerType.FullName}.{string.Empty}");}public bool HasFsm<T>(string name) where T : class{return InternalHasFsm($"{typeof(T).FullName}.{name}");}public bool HasFsm(Type ownerType, string name){if (ownerType == null){throw new Exception("Owner type is invalid.");}return InternalHasFsm($"{ownerType.FullName}.{name}");}private bool InternalDestroyFsm(string name){if(m_FsmDic.TryGetValue(name,out FsmBase fsmBase)){fsmBase.Shutdown();return m_FsmDic.Remove(name);}return false;}private FsmBase InternalGetFsm(string name){FsmBase fsm = null;if (m_FsmDic.TryGetValue(name, out fsm)){return fsm;}return null;}private bool InternalHasFsm(string name){return m_FsmDic.ContainsKey(name);}public void Update(){m_TempFsms.Clear();if (m_FsmDic.Count <= 0)return;foreach (FsmBase fsmBase in m_FsmDic.Values){m_TempFsms.Add(fsmBase);}foreach (FsmBase fsmBase in m_TempFsms){if (fsmBase.IsDestroyed)continue;fsmBase.Update();}}protected override void Load(int assemblyName){}protected override void UnLoad(int assemblyName){}
}
7.测试
(一)IdleState
public class IdleState : FsmState<FsmTest>
{protected internal override void OnDestroy(IFsm<FsmTest> fsm){Debug.Log("销毁 IdleState");}protected internal override void OnEnter(IFsm<FsmTest> fsm){Debug.Log("进入 IdleState");}protected internal override void OnInit(IFsm<FsmTest> fsm){}protected internal override void OnLeave(IFsm<FsmTest> fsm){Debug.Log("离开 IdleState");}protected internal override void OnUpdate(IFsm<FsmTest> fsm){if (Input.GetKeyDown(KeyCode.A)){ChangeState<WalkState>(fsm);}}
}
(二)WalkState
public class WalkState : FsmState<FsmTest>
{protected internal override void OnDestroy(IFsm<FsmTest> fsm){Debug.Log("销毁 WalkState");}protected internal override void OnEnter(IFsm<FsmTest> fsm){Debug.Log("进入 WalkState");}protected internal override void OnInit(IFsm<FsmTest> fsm){}protected internal override void OnLeave(IFsm<FsmTest> fsm){Debug.Log("离开 WalkState");}protected internal override void OnUpdate(IFsm<FsmTest> fsm){if (Input.GetKeyDown(KeyCode.B)){ChangeState<RunState>(fsm);}}
}
(三)RunState
public class RunState : FsmState<FsmTest>
{protected internal override void OnDestroy(IFsm<FsmTest> fsm){Debug.Log("销毁 RunState");}protected internal override void OnEnter(IFsm<FsmTest> fsm){Debug.Log("进入 RunState");}protected internal override void OnInit(IFsm<FsmTest> fsm){}protected internal override void OnLeave(IFsm<FsmTest> fsm){Debug.Log("离开 RunState");}protected internal override void OnUpdate(IFsm<FsmTest> fsm){if (Input.GetKeyDown(KeyCode.C)){ChangeState<IdleState>(fsm);}}
}
mono测试
public class FsmTest : MonoBehaviour
{private IFsm<FsmTest> m_TestFsm;void Start(){SingletonSystem.Initialize();AssemblyManager.Load(1, GetType().Assembly);m_TestFsm = FsmManager.Instance.CreateFsm<FsmTest>("MyTestFsm",this, new IdleState(),new WalkState(),new RunState());m_TestFsm.Start<IdleState>();}void Update(){SingletonSystem.Update();if (Input.GetKeyDown(KeyCode.P)){FsmManager.Instance.DestroyFsm(m_TestFsm);}}
}
相关文章:

C# Unity FSM 状态机
C# Unity FSM 状态机 使用状态机可以降低代码耦合性,并且可以优化代码可读性,方便团队协作等。 对于游戏开发内容来讲游戏开发的流程控制玩家动画都可以使用FSM有限状态机来实现。 1.FsmState 每个状态的基类,泛型参数表示所拥有者 publi…...

pytorch搭建squeezenet网络的整套工程,及其转tensorrt进行cuda加速
本来,前辈们用caffe搭建了一个squeezenet的工程,用起来也还行,但考虑到caffe的停更后续转trt应用在工程上时可能会有版本的问题所以搭建了一个pytorch版本的。 以下的环境搭建不再细说,主要就是pyorch,其余的需要什么p…...

【精读Uboot】SPL阶段的board_init_r详细分析
对于i.MX平台上的SPL来说,其不会直接跳转到Uboot,而是在SPL阶段借助BOOTROM跳转到ATF,然后再通过ATF跳转到Uboot。 board_init_f会初始化设备相关的硬件,最后进入board_init_r为镜像跳转做准备。下面是board_init_r调用的核心函数…...

canvas绘制渐变色三角形金字塔
项目需求:需要绘制渐变色三角形金字塔,并用折线添加标识 (其实所有直接用图片放上去也行,但是ui没切图,我也懒得找她要,正好也没啥事,直接自己用代码绘制算了,总结一句就是闲的) 最终效果如下图: (以上没用任何图片,都是代码绘制的) 在网上找了,有用canvas绘…...

企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力,为外部供…...

Debain JDK8 安装
Debain JDK8 安装 首先请安装依赖: sudo apt-get update && sudo apt-get install -y wget apt-transport-https然后信任 GPG 公钥: wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo tee /etc/apt/keyrings/…...

Python序列操作指南:列表、字符串和元组的基本用法和操作
文章目录 序列列表创建列表访问元素修改元素添加和删除元素 range()字符串创建字符串访问字符字符串切片修改字符串 元组创建元组访问元素获取元素数量元组的特点: 可变对象改变对象的值改变变量的指向比较运算符总结 python精品专栏推荐python基础知识(…...

【已更新代码图表】2023数学建模国赛E题python代码--黄河水沙监测数据分析
E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响,以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位于小浪底水…...

【前端】CSS-Grid网格布局
目录 一、grid布局是什么二、grid布局的属性三、容器属性1、display①、语句②、属性值 2、grid-template-columns属性、grid-template-rows属性①、定义②、属性值1)、固定的列宽和行高2)、repeat()函数3)、auto-fill关键字4)、f…...

计算机竞赛 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别
文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…...

2023-9-8 求组合数(二)
题目链接:求组合数 II #include <iostream> #include <algorithm>using namespace std;typedef long long LL; const int mod 1e9 7; const int N 100010;// 阶乘,阶乘的逆 int fact[N], infact[N];LL qmi(int a, int k, int p) {int res…...

k8s service的一些特性
文章目录 Service分发负载的策略同一端口通过不同协议暴露Headless Service的负载分发策略 Service分发负载的策略 大家都知道,一个service可以对应多个pod,那么一定要有一些方法来把service接收到的请求(负载)转发到pod上。 一般…...

C++中std::enable_if和SFINAE介绍
作为一个标准的C++模板类,我们先看下enable_if的定义: // STRUCT TEMPLATE enable_if template <bool _Test, class _Ty = void> struct enable_if {}; // no member "type" when !_Testtemplate <class _Ty> struct enable_if<true, _Ty> { //…...
华为OD机考算法题:数字加减游戏
目录 题目部分 解读与分析 代码实现 题目部分 题目数字加减游戏难度难题目说明小明在玩一个数字加减游戏,只使用加法或者减法,将一个数字 s 变成数字 t 。 每个回合,小明可以用当前的数字加上或减去一个数字。 现在有两种数字可以用来加减…...

WPF命令
在设计良好的Windows应用程序中,应用程序逻辑不应位于事件处理程序中,而应在更高层的方法中编写代码。其中的每个方法都代表单独的应用程序任务。每个任务可能依赖其他库。 使用这种设计最明显的方式是在需要的地方添加事件处理程序,并使用各…...

Unity中Shader的屏幕抓取 GrabPass
文章目录 前言一、抓取1、抓取指令2、在使用抓取的屏幕前,需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的 前言 Unity中Shader的屏幕抓取 GrabPass 一、抓取 1、抓取指令 屏幕的抓取需要使用一个Pass GrabPass{} GrabPass{“NAME”} 2、在使用…...

手撕 队列
队列的基本概念 只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头 队列用链表实现 队列的实现 队列的定义 队列…...

【autodl/linux配环境心得:conda/本地配cuda,cudnn及pytorch心得】-未完成
linux配环境心得:conda/本地配cuda,cudnn及pytorch心得 我们服务器遇到的大多数找不到包的问题一,服务器安装cuda和cudnn使用conda在线安装cuda和cudnn使用conda进行本地安装检查conda安装的cuda和cudnn本地直接安装cuda和cudnn方法一&#x…...

macOS Ventura 13.5.2(22G91)发布,附黑/白苹果镜像下载地址
系统介绍(下载请百度搜索:黑果魏叔) 黑果魏叔 9 月 8 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.5.2 更新(内部版本号:22G91),本次更新距离上次发布隔了 21 天。 本次更新查…...

vue 子组件向父组件传递参数 子传父
子组件中写: this.$emit(RowCount,res.data.RowCount); 父组件中写: getMFGLRowCount(val){ //父组件中的方法: 接收子组件传过来的参数值赋值给父组件的变量 //this.totalCount val; alert("这…...

自然语言处理学习笔记(八)———— 准确率
目录 1.准确率定义 2.混淆矩阵与TP/FN/FP/TN 3. 精确率 4.召回率 5.F1值 6.中文分词的P、R、F1计算 7.实现 1.准确率定义 准确率是用来衡量一个系统的准确程度的值,可以理解为一系列评测指标。当预测与答案的数量相等时,准确率指的是系统做出正确判…...

Matlab 如何选择窗函数和 FFT 的长度
Matlab 如何选择窗函数和 FFT 的长度 1、常用的四种窗函数 对于实际信号序列,如何选取窗函数呢?一般来说,选择第一旁瓣衰减大,旁瓣峰值衰减快的窗函数有利于緩解截断过程中产生的頻泄漏问题。但具有这两个特性的窗函数࿰…...

node.js下载安装环境配置以及快速使用
目录 一、下载 二、安装 三、测试安装是否成功 四、配置环境 五、测试配置环境是否成功 六、安装淘宝镜像 七、快速上手 1、建立一个自己的工作目录 2、下载工作代码 八、各种配置文件匹配问题入坑 九、总结 一、下载 Node.js 中文网 想选择其他版本或者其他系统使用…...

使用栈检查括号的合法性 C 实现
使用栈检查括号的合法性 思路讲解:首先从数组数组0下标开始,如果是左括号直接无脑压入栈,直到出现右括号开始判断合法与否。遇到右括号分两种情况,第一种是空栈的情况,也就是说我们第一个字符就是右括号,那…...

小白备战大厂算法笔试(四)——哈希表
文章目录 哈希表常用操作简单实现冲突与扩容链式地址开放寻址线性探测多次哈希 哈希表 哈希表,又称散列表,其通过建立键 key 与值 value 之间的映射,实现高效的元素查询。具体而言,我们向哈希表输入一个键 key ,则可以…...

云原生Kubernetes:pod基础
目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略(image PullPolicy) 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是ku…...

Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析
本文是 3 篇系列文章的一部分,该系列文章将讨论智能手机镜头模组设计的挑战,从概念、设计到制造和结构变形的分析。本文是三部分系列的第三部分。它涵盖了使用 Ansys Zemax OpticStudio Enterprise 版本提供的 STAR 技术对智能手机镜头进行自动的结构、热…...

CSP-J初赛复习大题整理笔记
本篇全是整理,为比赛准备. 在这里插入代码片 #include<cstdio> using namespace std; int n, m; int a[100], b[100];int main() {scanf_s("%d%d", &n, &m);for (int i 1; i < n; i)a[i] b[i] 0;//将两个数组清0,这…...

面试题 ⑤
1、TCP与UDP的区别 UDPTCP是否连接无连接,即刻传输面向连接,三次握手是否可靠不可靠传输,网络波动拥堵也不会减缓传输可靠传输,使用流量控制和拥塞控制连接对象个数支持一对一,一对多,多对一和多对多交互通…...

硅谷课堂1
文章目录 P1 项目概述P2—P12 MybatisPlus知识回顾P8 MybatisPlus实现逻辑删除P9 QueryWrapper使用P14 项目后端模块介绍P15 项目后端环境搭建P50—P53 整合腾讯云对象存储1、整合腾讯2、腾讯云示例3、讲师头像上传-后端代码P54—P60 课堂分类管理1、课堂分类查询2、课程分类导…...