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

行为树详解(6)——黑板模式

【动作节点数据共享】

行为树中需要的参数可以来自游戏中的各个模块,如果仅需从多个模块获取少量参数,那么可以直接在代码中调用其他模块的单例继而层层调用获取数据。

如果获取的参数量很大,从架构上看,我们需要通过加一个中间者去管理各个模块的参数获取调用,行为树从中间者获取数据即可。

换一种说法就是要有共享数据的地方,通常会采用黑板模式。

综合来说,存在以下情况:

  1. 多个不同的动作节点或条件节点需要获取或设置来自不同模块的属性
  2. 多个不同的动作节点或条件节点会获取或设置相同模块的同一属性
  3. 不同动作节点之间有通信,A动作节点生成的临时数据是B动作节点所需的数据
  4. 不同动作节点存在大量重复计算,例如距离计算
  5. 多个动作节点会共用临时存在的多个数据

针对这些情况,我们可以通过键值对的形式实现黑板模式

需要注意的是,这些黑板不属于节点,考虑到不同行为树也会共享数据,因此也不一定属于黑板。需要有一个黑板的管理者来做数据管理。

黑板模式的数据管理本质还是通过键值对的方式,为处理不同的情况,我们需要对每种情况提供不同的Key。这和MVC中的数据管理并无本质区别。

和节点参数配置不同的是,这里是要程序做控制的,而且不确定性更大,无法做明确的规定。

在这种情况下,我们需要对每个数据做单独得ID定义,每个数据有各自的获取设置方法,通过ID映射。

根据行为树ID,节点ID,数据ID,方法ID可以实现不同的数据获取,程序只需实现方法ID即可。

在黑板中,我们需要根据这些参数生成唯一的Key,这里自然而然的就会需要对参数做封装,用泛型,用对象池。

同样的,我们需要有这些参数对应的结果,考虑数据类型差异,结果有效性等,自然也需要做封装。

【动作节点的实现位置】

在整个游戏中,与角色相关的模块如下:

  • 角色动画,基于状态机提供动作切换,提供最基础的接口
  • 角色运动,包括基础移动(走、跑等),地形移动(蹲下、跳跃、攀爬等),寻路。会调用角色动画提供的接口
  • 角色交互:
    • 与物体的交互(拾取、推开、握住、抓住、攀绕、踢开等等)。会调用角色动画或角色运动提供的接口,前者为主
    • 与角色的交互(主要是打击、少量握手、拥抱等)。会调用角色动画或角色运动提供的接口,前者为主
  • 角色技能:技能、Buff、伤害计算、效果表现。会调用角色动画或角色运动或角色交互提供的接口,前者为主
  • 角色属性:记录角色的各类状态
  • 角色行为:这里就是角色AI,会调用角色动画或角色运动或角色交互或角色技能或角色属性提供的接口

因此,在实现动作节点时,属于其他模块的直接调用其他模块的接口或在其他模块内实现,属于角色行为的在动作节点内实现。

例如,就技能而言,对角色技能来说不同角色的技能各有差异,要做不同的实现;但对角色AI而言,只有普攻、1技能、2技能、大招等

【代码实现】

数据配置

    [Serializable]public class DataConfig{public int dataId;public int setMethodId;public int getMethodId;public DataLife dataLife;public bool multi;//同一类型参数,参数值不同,结果不同public bool praseType;//如果解析类型,做自动化生成public bool cache;//是否做数据缓存}public enum DataLife{Persistent,//永久性数据Conditional,//条件性数据,满足某条件出现,不满足消失FixedTime,//固定时间内有效的数据FixedFrame,//固定帧数内有效的数据}[CreateAssetMenu(fileName = "BlackBoard_Data_Config", menuName = "BT/BlackBoardDataConfig")]public class BlackBoardDataConfig:ScriptableObject{public List<DataConfig> dataConfigs = new List<DataConfig>();}

数据请求

    public class BBDataRequest{public int btId;public int nodeId;public int dataId;public virtual void Release() { }}public class BBDataRequest<Parmas> : BBDataRequest{private static ObjectPool<BBDataRequest<Parmas>> Pool = new ObjectPool<BBDataRequest<Parmas>>(GenBBDataRequest);private static BBDataRequest<Parmas> GenBBDataRequest(){return new BBDataRequest<Parmas>();}public static BBDataRequest<Parmas> GetBBDataRequest(){return Pool.Get();}public static void Release(BBDataRequest<Parmas> data){data.Reset();Pool.Release(data);}public virtual int TryAddParams(Parmas data){return -1;}public virtual int TryAddObject(object data){return -1;}public virtual Parmas GetParmas(int index){return default(Parmas);}public virtual object GetObject(int index){return null;}public override void Release(){Release(this);}public virtual void Reset(){}}public class BBDataRequestSingle<Parmas> : BBDataRequest<Parmas>{public Parmas reqParamsNoBoxing;public object reqParams;public override int TryAddParams(Parmas data){reqParamsNoBoxing = data;return -1;}public override int TryAddObject(object data){reqParams = data;return -1;}public override Parmas GetParmas(int index){return reqParamsNoBoxing;}public override object GetObject(int index){return reqParams;}public override void Reset(){reqParamsNoBoxing = default(Parmas);reqParams = default(object);}}public class BBDataRequestMulti<Parmas>:BBDataRequest<Parmas>{public Dictionary<Parmas,int> paramsNoBoxingIndex = new Dictionary<Parmas,int>();public Dictionary<object,int> paramsIndex = new Dictionary<object,int>();private Dictionary<int, Parmas> index2ParamsNoBoxing = new Dictionary<int, Parmas>();private Dictionary<int,object> index2Params = new Dictionary<int,object>();public override int TryAddParams(Parmas data){if(!paramsNoBoxingIndex.TryGetValue(data,out int res)){res = paramsNoBoxingIndex.Count;paramsNoBoxingIndex[data] = res;}index2ParamsNoBoxing[res] = data;return res;}public override int TryAddObject(object data){if(!paramsIndex.TryGetValue(data,out int res)){res = paramsIndex.Count;paramsIndex[data] = res;}index2Params[res] = data;return res;}public override object GetObject(int index){return index2Params[index];}public override Parmas GetParmas(int index){return index2ParamsNoBoxing[index];}public override void Reset(){paramsIndex.Clear();index2Params.Clear();index2ParamsNoBoxing.Clear();paramsNoBoxingIndex.Clear();}}

数据结果

    public class BBDataResult<Result> : IBBDataResult{public int dataId { get; set; }public DataConfig config { get; set; }public BBDataRequest request { get; set; }  public float lifeTime;public float curTime;public int curFrame;public bool Valid(){switch(config.dataLife){case DataLife.FixedTime:case DataLife.FixedFrame:return curTime > lifeTime;case DataLife.Conditional:case DataLife.Persistent: return true;}return true;  }public virtual bool Getted(int frameCount, int index){return false;}public virtual Result GetCurResult(int index){return default(Result);}public virtual void SetGetResult(Result value, int index){}public virtual void SetCurResult(Result value,int index){}public virtual void Tick(float deltaTime){curFrame = Time.frameCount;if (config.dataLife == DataLife.FixedTime){curTime += deltaTime;}if(config.dataLife == DataLife.FixedFrame){curTime += 1;}}public virtual void Reset(){curTime = 0;curFrame = 0;}private static ObjectPool<BBDataResult<Result>> Pool = new ObjectPool<BBDataResult<Result>>(GenBBDataResult);private static BBDataResult<Result> GenBBDataResult(){return new BBDataResult<Result>();}public static BBDataResult<Result> GetBBDataResult(){return Pool.Get();}public static void Release(BBDataResult<Result> bbDataResult){bbDataResult.Reset();Pool.Release(bbDataResult);}public void Release(){Release(this);}}public class BBDataResultSingle<Result>: BBDataResult<Result>{public Result result;public bool getted;public override void SetGetResult(Result value, int index){result = value;getted = true;}public override void SetCurResult(Result value,int index){result = value;getted = false;}public override bool Getted(int frameCount, int index){return getted && frameCount == curFrame;}public override void Tick(float deltaTime){base.Tick(deltaTime);getted = false;}public override void Reset(){getted = false;result = default(Result);}public override Result GetCurResult(int index){return result;}}public class BBDataResultMulti<Result>: BBDataResult<Result>{public Dictionary<int,Result> resultIndex = new Dictionary<int,Result>();public Dictionary<int, bool> getted = new Dictionary<int, bool>();public override void SetGetResult(Result value,int index){resultIndex[index] = value;getted[index] = true;}public override void SetCurResult(Result value, int index){resultIndex[index] = value;getted[index] = false;}public override bool Getted(int frameCount,int index){return getted[index] && frameCount == curFrame;}public override void Tick(float deltaTime){base.Tick(deltaTime);foreach (var item in getted.Keys){getted[item] = false;}}public override void Reset(){getted.Clear();resultIndex.Clear();}public override Result GetCurResult(int index){return resultIndex[index];}}

黑板类及其管理者

    public class BlackBoardManager{private static BlackBoardManager instance;private BlackBoardManager() { }public static BlackBoardManager Instance{get{if (instance == null){instance = new BlackBoardManager();}return instance;}}public Dictionary<int, BlackBoard> id2BB = new Dictionary<int, BlackBoard>();private Dictionary<int,DataConfig> dataConfig = new Dictionary<int, DataConfig>();public void Init(){BlackBoard bb = new BlackBoard();bb.bbId = 1;id2BB[1] = bb;//load配置数据           }public void Tick(float deltaTime){foreach (var bb in id2BB.Values){bb.Tick(deltaTime);}}public BlackBoard CreateBlackBoard(bool common){if (common){return id2BB[1];}else{BlackBoard bb = new BlackBoard();bb.bbId = id2BB.Count + 1;id2BB[bb.bbId] = bb;return bb;}}public BlackBoard GetBlackBoard(int bbId){id2BB.TryGetValue(bbId, out var bb);return bb;}public DataConfig GetDataConfig(int id){return dataConfig[id];}public void RemoveBlackBoard(BlackBoard bb){id2BB.Remove(bb.bbId);}public void Clear(){foreach(var bb in id2BB.Values){bb.Clear();}id2BB.Clear();dataConfig.Clear();}}public class BlackBoard{public int bbId;public Dictionary<int, IBBDataResult> id2Result = new Dictionary<int, IBBDataResult>();public Dictionary<int,BBDataRequest> id2Request;//这里简单根据Id做划分,可以做更复杂的分类,以便于收集数据做数据分析或Debugprivate List<int> waitRemoveList = new List<int>();private List<BBDataRequest> reqHistory = new List<BBDataRequest>();//可以收集数据做分析public void Tick(float deltaTime)//Tick检查去掉无效数据{waitRemoveList.Clear();foreach (var item in id2Result){item.Value.Tick(deltaTime);if(!item.Value.Valid()){waitRemoveList.Add(item.Key);}}foreach (var item in waitRemoveList){RemoveData(item);}}public Result GetData<Params,Result>(int btId,int nodeId,int dataId, Params reqparams,out bool valid){var config = BlackBoardManager.Instance.GetDataConfig(dataId);//根据数据Id获取数据配置var request = GetBBDataRequest<Params>(btId,nodeId,dataId,reqparams,config.multi && config.cache, out int index);//根据参数获取请求,分为Single请求和Multi请求var result = GetBBDataResult<Result>(dataId, config, request);//获取请求对应的结果//一个数据Id只有一个对应的请求和结果valid = result.Valid();if(valid){if(config.praseType){BBDataMethod.DispatchMethoId<Params,Result>(result.config.getMethodId, bbId, dataId, index, true);//自动解析传入的参数和结果的类型,自动化生成代码,适用于简单的值类型}else{BBDataMethod.DispatchMethoId(result.config.getMethodId, bbId, dataId, index);//自定义处理数据类型}return ((BBDataResult<Result>)result).GetCurResult(index);//同一个数据Id,在获取时会传入不同的参数,在请求中,给参数生成Index,根据Index获取其对应的结果}    return default(Result);}public Result GetData<Result>(int btId, int nodeId, int dataId, object reqparams, out bool valid){var config = BlackBoardManager.Instance.GetDataConfig(dataId);var request = GetBBDataRequest(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);var result = GetBBDataResult<Result>(dataId, config, request);valid = result.Valid();if (valid){if (config.praseType){BBDataMethod.DispatchMethoId<object, Result>(result.config.getMethodId, bbId, dataId, index, true);}else{BBDataMethod.DispatchMethoId(result.config.getMethodId, bbId, dataId, index);}return ((BBDataResult<Result>)result).GetCurResult(index);}return default(Result);}public void SetData<Params, Value>(int btId, int nodeId, int dataId,Value value, Params reqparams = default){var config = BlackBoardManager.Instance.GetDataConfig(dataId);//根据数据Id获取数据配置var request = GetBBDataRequest<Params>(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);//根据参数获取请求,分为Single请求和Multi请求var result = GetBBDataResult<Value>(dataId, config, request);//获取请求对应的结果if (!((BBDataResult<Value>)result).GetCurResult(index).Equals(value))//判断设置的值是否和当前的结果值相当,如果相等就不用再设置了{if (config.praseType){BBDataMethod.DispatchMethoId<Params, Result>(result.config.setMethodId, bbId, dataId, index, false);}else{BBDataMethod.DispatchMethoId(result.config.setMethodId, bbId, dataId, index);}}         }public void SetData<Value>(int btId, int nodeId, int dataId, Value value,object reqparams = null){var config = BlackBoardManager.Instance.GetDataConfig(dataId);var request = GetBBDataRequest(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);var result = GetBBDataResult(dataId, config, request);if (!((BBDataResult<Value>)result).GetCurResult(index).Equals(value)){if (config.praseType){BBDataMethod.DispatchMethoId<object, Result>(result.config.setMethodId, bbId, dataId, index, false);}else{BBDataMethod.DispatchMethoId(result.config.setMethodId, bbId, dataId, index);}}}public bool RemoveData(int dataId){int count = 0;if(id2Result.TryGetValue(dataId,out var result)){result.Release();id2Result.Remove(dataId);count++;}if(id2Request.TryGetValue(dataId,out var request)){request.Release();id2Request.Remove(dataId);count++;}return count == 2;}public BBDataRequest GetDataRequest(int dataId){id2Request.TryGetValue(dataId, out var result);return result;}public IBBDataResult GetDataResult(int dataId){id2Result.TryGetValue(dataId, out var result);return result;}public void Clear(){id2Request.Clear();id2Result.Clear();waitRemoveList.Clear();//SaveHistoryreqHistory.Clear();}private BBDataRequest GetBBDataRequest<T>(int btId, int nodeId, int dataId,T data,bool multi,out int index){if(!id2Request.TryGetValue(dataId,out var request)){request = multi ? BBDataRequestMulti<T>.GetBBDataRequest() : BBDataRequestSingle<T>.GetBBDataRequest();request.btId = btId;request.nodeId = nodeId;request.dataId = dataId;//reqHistory.Add(request);}var res = request as BBDataRequest<T>;index = res.TryAddParams(data);//将获取数据传入的参数封装在 BBDataRequest中return res;}private BBDataRequest GetBBDataRequest(int btId, int nodeId, int dataId, object data,bool multi,out int index){if (!id2Request.TryGetValue(dataId, out var request)){request = multi ? BBDataRequestMulti<object>.GetBBDataRequest() : BBDataRequestSingle<object>.GetBBDataRequest();request.btId = btId;request.nodeId = nodeId;request.dataId = dataId;//reqHistory.Add(request);}var res = request as BBDataRequest<object>;index = res.TryAddObject(data);return res;}private IBBDataResult GetBBDataResult<T>(int dataId,DataConfig config,BBDataRequest request){if (!id2Result.TryGetValue(dataId, out var result)){BBDataResult<T> res = (config.multi && config.cache) ? BBDataResultMulti<T>.GetBBDataResult() : BBDataResultSingle<T>.GetBBDataResult();if (!config.cache) res.SetCurResult(default(T), 0);result = res;result.dataId = dataId;result.config = config;}result.request = request;return result;}private IBBDataResult GetBBDataResult(int dataId, DataConfig config, BBDataRequest request){if (!id2Result.TryGetValue(dataId, out var result)){BBDataResult<object> res = (config.multi && config.cache) ? BBDataResultMulti<object>.GetBBDataResult() : BBDataResultSingle<object>.GetBBDataResult();if (!config.cache) res.SetCurResult(null, 0);result = res;result.dataId = dataId;result.config = config;}result.request = request;return result;}}

数据的GetSet方法实

    public static class BBDataDefinition{//这里通过配置自动生成public const int Def_获取血量 = 11223344;public const int Def_设置血量 = 11223345;public const int Def_获取资源数量 = 121212123;public const int Def_设置资源数量 = 121212124;}public partial class BBDataMethod{//这里通过配置自动生成private static Dictionary<(Type, Type), Action<BBDataRequest,IBBDataResult,int,int,bool>> TypeToPraseAction = new Dictionary<(Type, Type), Action<BBDataRequest, IBBDataResult, int, int,bool>>(){[(typeof(void),typeof(int))] = PraseVoidAndInt,[(typeof(int), typeof(int))] = PraseIntAndInt,[(typeof(int), typeof(void))] = PraseIntAndVoid,};private static Dictionary<int, Func<int>> GetIntValue = new Dictionary<int, Func<int>>(){[BBDataDefinition.Def_获取资源数量] = GetResCount,};private static Dictionary<int, Action<int>> SetIntValue = new Dictionary<int, Action<int>>(){[BBDataDefinition.Def_设置资源数量] = SetResCount,};private static Dictionary<int, Func<int,int>> GetIntValueByInt = new Dictionary<int, Func<int,int>>(){};private static Dictionary<int, Action<int, int>> SetIntValueByInt = new Dictionary<int, Action<int, int>>(){};public static void DispatchMethoId<Params,Result>(int methodId,int bbId,int dataId,int index,bool get){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);var res = bb.GetDataResult(dataId);if (get && res != null && res.Getted(Time.frameCount, index)){return;}var req = bb.GetDataRequest(dataId);if (req != null && res != null){var typeReq = typeof(Params);var typeRes = typeof(Result);TypeToPraseAction.TryGetValue((typeReq, typeRes), out var action);if (action != null){action(req, res, methodId, index, get);}}}public static void DispatchMethoId(int methodId, int bbId, int dataId, int index){switch (methodId){case BBDataDefinition.Def_获取血量: GetRoleHp(bbId, dataId, index); break;case BBDataDefinition.Def_设置血量: SetRoleHp(bbId, dataId, index); break;}}private static void PraseVoidAndInt(BBDataRequest req, IBBDataResult res, int methodId, int index,bool get){if(get){int intValue = GetIntValue[methodId].Invoke();var intResult = res as BBDataResult<int>;intResult.SetGetResult(intValue, index);}}private static void PraseIntAndVoid(BBDataRequest req, IBBDataResult res, int methodId, int index, bool get){if (!get){var intResult = res as BBDataResult<int>;int intValue = intResult.GetCurResult(index);SetIntValue[methodId].Invoke(intValue);}}private static void PraseIntAndInt(BBDataRequest req, IBBDataResult res, int methodId, int index, bool get){if(get){var intReq = req as BBDataRequest<int>;int intParams = intReq.GetParmas(index);int intValue = GetIntValueByInt[methodId].Invoke(intParams);var intResult = res as BBDataResult<int>;intResult.SetGetResult(intValue, index);}else{var intReq = req as BBDataRequest<int>;int intParams = intReq.GetParmas(index);var intResult = res as BBDataResult<int>;int intValue = intResult.GetCurResult(index);SetIntValueByInt[methodId].Invoke(intParams, intValue);}}}public partial class BBDataMethod{public static void GetRoleHp(int bbId, int dataId,int index){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);//获取数据所在的BBvar res = bb.GetDataResult(dataId);//获取数据对应的结果if(res != null && res.Getted(Time.frameCount,index))//判断当前帧该数据是否已经获取过{return;}var req = bb.GetDataRequest(dataId);//获取数据对应的请求           if(req != null ){               var intReq = req as BBDataRequest<int>;int roleId = intReq.GetParmas(index);//获取请求的参数int hp = 100;//通过角色Id获取角色属性,属性系统固定时,这些类似的获取值的代码都可以通过自动化配置生成            var intResult = res as BBDataResult<int>;intResult.SetGetResult(hp,index);//设置获取的结果}}public static void SetRoleHp(int bbId, int dataId,int index){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);var res = bb.GetDataResult(dataId);var req = bb.GetDataRequest(dataId);if (req != null && res != null){var intReq = req as BBDataRequest<int>;int roleId = intReq.GetParmas(index);var intResult = res as BBDataResult<int>;int hp = intResult.GetCurResult(index);             //调用接口设置角色血量}}public static int GetResCount() { return 100; }public static void SetResCount(int value) { }}

相关文章:

行为树详解(6)——黑板模式

【动作节点数据共享】 行为树中需要的参数可以来自游戏中的各个模块&#xff0c;如果仅需从多个模块获取少量参数&#xff0c;那么可以直接在代码中调用其他模块的单例继而层层调用获取数据。 如果获取的参数量很大&#xff0c;从架构上看&#xff0c;我们需要通过加一个中间…...

【prometheus】Pushgateway安装和使用

目录 一、Pushgateway概述 1.1 Pushgateway简介 1.2 Pushgateway优点 1.3 pushgateway缺点 二、测试环境 三、安装测试 3.1 pushgateway安装 3.2 prometheus添加pushgateway 3.3 推送指定的数据格式到pushgateway 1.添加单条数据 2.添加复杂数据 3.SDk-prometheus-…...

耗时一天,我用AI开发了AI小程序

小码哥从事前后端开发近十年&#xff0c;但是随着技术的更新迭代&#xff0c;有时候没有时间和精力去优化UI、实现一些前后端功能&#xff0c;以及解决一些bug。特别是我想开发小码哥AI的移动端&#xff0c;但觉得自己没有那么多时间去研究移动端了&#xff0c;准备放弃了&…...

Java 日期时间格式化标准

文章目录 Java日期时间格式化符号ISO 8601中的日期时间ISO 8601标准的定义ISO 8601日期时间格式 周数年份ISO 8601中的周数年份Java中的周数年份 Java跨年日期格式化BUG注意事项 Java日期时间格式化符号 JDK官网截图&#xff1a; 格式化符号梳理&#xff1a; 符号描述符号用…...

undolog,redolog,binlog分别是做什么的?

在数据库系统中&#xff08;尤其是 MySQL&#xff09;&#xff0c;Undo log、Redo log 和 Binlog 是用于实现数据持久性和一致性的重要日志机制。 1. Undo Log&#xff08;回滚日志&#xff09; 功能&#xff1a; 用于事务回滚&#xff1a;记录事务开始前的状态&#xff0c;以…...

NRF24L01模块STM32-接收端

前言 在调试接收端时,建议先看下下篇文章NRF24L01调试心得 环境&#xff1a; 芯片&#xff1a;STM32F103C8T6 Keil&#xff1a;V5.24.2.0 一、接收端初始化 void NRF24l01_rx_mode(void) {NRF24L01_CE(0);NRF24l01_write_buf(NRF_WRITE_REG TX_ADDR, (uint8_t *)TX_ADDRE…...

核磁机器学习 | 机器学习和深度学习算法在fMRI中的应用

摘要 功能磁共振成像(fMRI)是目前应用最广泛的脑图像动态分析技术之一&#xff0c;通常结合多种算法来分析复杂的动态数据。近年来&#xff0c;机器学习和深度学习算法在分析fMRI数据方面的应用呈指数级增长。然而&#xff0c;由于文献中存在大量算法&#xff0c;选择合适的机器…...

【数据结构-堆】力扣3296. 移山所需的最少秒数

给你一个整数 mountainHeight 表示山的高度。 同时给你一个整数数组 workerTimes&#xff0c;表示工人们的工作时间&#xff08;单位&#xff1a;秒&#xff09;。 工人们需要 同时 进行工作以 降低 山的高度。对于工人 i : 山的高度降低 x&#xff0c;需要花费 workerTimes…...

web前端-html

HTML部分 HTML&#xff1a;超文本标记语言。是万维网web编程的基础&#xff0c;web是建立在超文本基础上的。HTML 是万维网的基石 打开www.baidu.com的页面源代码可见 超文本标记超的含义 1.最重要的标签&#xff0c;超链接标签&#xff0c;可跳转页面&#xff0c;关联所有页…...

单片机-定时器中断

1、相关知识 振荡周期1/12us; //振荡周期又称 S周期或时钟周期&#xff08;晶振周期或外加振荡周期&#xff09;。 状态周期1/6us; 机器周期1us; 指令周期1~4us; ①51单片机有两组定时器/计数器&#xff0c;因为既可以定时&#xff0c;又可以计数&#xff0c;故称之为定时器…...

计算机网络 (31)运输层协议概念

一、概述 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。运输层的一个核心功能是提供从源端主机到目的端主机的可靠的、与实际使用的网络无关的信息传输。它向高层用…...

【学Rust开发CAD】2 创建第一个工作空间、项目及库

文章目录 一、 创建工作空间二、新建项目&#xff08;可执行文件&#xff09;三、 新建库&#xff08;库文件&#xff09;四、更新项目依赖五、编写代码七、总结 在 Rust 中&#xff0c;工作空间&#xff08;workspace&#xff09;允许你管理多个相关的包&#xff08;crate&…...

使用GitLab+Jenkins搭建CICD执行环境

使用GitLabJenkins搭建CI\CD执行环境 前言什么是DevOps&#xff1f;什么是CI/CD&#xff1f;使用GitLabJenkins搭建CI\CD执行环境GitLab安装1. 安装和配置所需的依赖2. 下载并安装极狐GitLab3. 登录极狐GitLab 实例4.常用gitlab指令5.修改密码 Jenkins安装1.Jenkins 的主要特点…...

微信小程序——创建滑动颜色条

在微信小程序中&#xff0c;你可以使用 slider 组件来创建一个颜色滑动条。以下是一个简单的示例&#xff0c;展示了如何实现一个颜色滑动条&#xff0c;该滑动条会根据滑动位置改变背景颜色。 步骤一&#xff1a;创建小程序项目 首先&#xff0c;使用微信开发者工具创建一个…...

Mac中配置vscode(第一期:python开发)

1、终端中安装 xcode-select --install #mac的终端中安装该开发工具 xcode-select -p #显示当前 Xcode 命令行工具的安装路径注意&#xff1a;xcode-select --install是在 macOS 上安装命令行开发工具(Command Line Tools)的关键命令。安装的主要组件包括&#xff1a;C/C 编…...

Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式

业务场景 在目前常见的中后台管理系统中&#xff0c;比较常见的是固定的布局方式包裹页面&#xff0c;但一些特殊页面&#xff0c;比如&#xff1a;登录页面、注册页面、忘记密码页面这些页面是不需要布局包裹的。 但在 Next.js AppRouter 中&#xff0c;必须包含一个根布局文…...

Linux环境下确认并操作 Git 仓库

在软件开发和版本控制中&#xff0c;Git 已成为不可或缺的工具。有时&#xff0c;我们需要确认某个目录是否是一个 Git 仓库&#xff0c;并在该目录中运行脚本。本文将详细介绍如何确认 /usr/local/src/zcxt/backend/policy-system-backend 目录是否是一个 Git 仓库&#xff0c…...

海陵HLK-TX510人脸识别模块 stm32使用

一.主函数 #include "stm32f10x.h" // Device header #include "delay.h" #include "lcd.h" #include "dht11.h" #include "IOput.h" #include "usart.h" //#include "adc.h" …...

MATLAB语言的正则表达式

MATLAB 中的正则表达式使用指南 引言 在数据处理和文本分析中&#xff0c;正则表达式是一种强大而灵活的工具。MATLAB 作为一种广泛应用于科学计算和数据分析的编程语言&#xff0c;提供了对正则表达式的支持&#xff0c;使得用户可以方便地进行字符串匹配与处理。本文将深入…...

【CVPR 2024】【遥感目标检测】Poly Kernel Inception Network for Remote Sensing Detection

0.论文摘要 摘要 遥感图像&#xff08;RSIs&#xff09;中的目标检测经常面临几个日益增加的挑战&#xff0c;包括目标尺度的巨大变化和不同范围的背景。现有方法试图通过大核卷积或扩张卷积来扩展主干的空间感受野来解决这些挑战。然而&#xff0c;前者通常会引入相当大的背…...

笔记-使用ffmpeg产生rtsp视频流,然后用进行VLC播放

笔记-使用ffmpeg产生rtsp视频流&#xff0c;然后用进行VLC播放 1.软件配置1.1下载安装好**ffmpeg**1.2使用EasyDarwin创建RTSP服务器 2.FFmpeg找本地摄像头名字3.FFmpeg推流命令3.1使用VLC实现拉流 1.软件配置 1.1下载安装好ffmpeg ffmpeg官网 本地下载 1.2使用EasyDarwin创…...

Ubuntu20.04中EasyConnect启动报错

安装路径 /usr/share/sangfor/EasyConnect 方法 通过 ./EasyConnect获得错误代码&#xff0c;其中‘Failed to load module "canberra-gtk-module"’可以忽略&#xff0c;主要是‘Harfbuzz version too old (1.3.1)’这个问题&#xff0c;后边的版本号可能因系统不…...

计算机毕业设计Python中华古诗词知识图谱可视化 古诗词智能问答系统 古诗词数据分析 古诗词情感分析模型 自然语言处理NLP 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

Excel | 空格分隔的行怎么导入excel?

准备工作&#xff1a;windows&#xff0c;一个记事本程序和微软的Excel软件。 打开记事本&#xff0c;选中所有内容&#xff0c;按CtrlA全选&#xff0c;然后复制(CtrlC)。 在Excel中&#xff0c;定位到你想粘贴的单元格&#xff0c;按CtrlV进行粘贴。粘贴后&#xff0c;你会在…...

【形式篇】年终总结怎么写:PPT如何将内容更好地表现出来

——细节满满&#xff0c;看完立马写出一篇合格的PPT 总述 形式服务于内容&#xff0c;同时合理的形式可以更好地表达和彰显内容 年终总结作为汇报型PPT&#xff0c;内容一定是第一位的&#xff0c;在内容篇(可点击查看)已经很详细地给出了提纲思路&#xff0c;那如何落实到…...

Python生日祝福烟花

1. 实现效果 2. 素材加载 2个图片和3个音频 shoot_image pygame.image.load(shoot(已去底).jpg) # 加载拼接的发射图像 flower_image pygame.image.load(flower.jpg) # 加载拼接的烟花图 烟花不好去底 # 调整图像的像素为原图的1/2 因为图像相对于界面来说有些大 shoo…...

(六)CAN总线通讯

文章目录 CAN总线回环测试第一种基于板载CAN测试第一步确认板载是否支持第二步关闭 CAN 接口将 CAN 接口置于非活动状态第三步 配置 CAN 接口第一步 设置 CAN 接口比特率第二步 设置 CAN 启用回环模式第三步 启用 CAN 接口 第四步 测试CAN总线回环捕获 CAN 消息发送 CAN 消息 第…...

python.exe无法找到程序入口 无法定位程序输入点(Anaconda Prompt报错)

参考 总结&#xff1a; &#xff08;1&#xff09;pythoncom36.dll是pywin32的一个文件&#xff0c;位于路径Anaconda3\envs\pytorch(你的虚拟环境)\Lib\site-packages\pywin32_system32中&#xff1b; &#xff08;2&#xff09;但在Anaconda3\envs\pytorch\Library\bin中也…...

C++网络程序设计

在C网络编程中&#xff0c;使用Berkeley Sockets API是一种常见的方法来实现跨平台的TCP通信。Berkeley Sockets API最初是在UNIX系统上开发的&#xff0c;但它已经被广泛移植到其他操作系统&#xff0c;包括Windows。 示例代码 client.cpp #include <iostream> #incl…...

【快速实践】深度学习 -- 数据曲线平滑化

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; 在观察数据结果时&#xff0c;我们通常希望获得整体趋…...

【强化学习】演员评论家Actor-Critic算法(万字长文、附代码)

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…...

Kubernetes Gateway API-5-后端协议和网关基础设置标签

1 后端协议 自 v1.2.0 开始支持 并非所有网关API实现都支持自动协议选择。在某些情况下&#xff0c;协议在没有明确选择加入的情况下被禁用。 当 Route 的后端引用Kubernetes Service 时&#xff0c;应用程序开发人员可以使用 ServicePort appProtocol 字段指定协议。 例如…...

鸿蒙 ArkUI实现地图找房效果

常用的地图找房功能&#xff0c;是在地图上添加区域、商圈、房源等一些自定义 marker&#xff0c;然后配上自己应用的一些筛选逻辑构成&#xff0c;在这里使用鸿蒙 ArkUI 简单实现下怎么添加区域/商圈、房源等 Marker. 1、开启地图服务 在华为开发者官网&#xff0c;注册应用&…...

前后端分离架构设计与实现:构建现代Web应用的基石

前后端分离架构设计与实现&#xff1a;构建现代Web应用的基石 引言 随着互联网技术的发展&#xff0c;Web应用变得越来越复杂和多样化。传统的单体式架构难以满足快速迭代、团队协作以及性能优化的需求。前后端分离架构应运而生&#xff0c;它不仅提高了开发效率&#xff0c;…...

【51单片机】02LED流水灯实验

点亮你的LED 一、点亮第一个LED1.GPIO介绍2.P1、P2、P3端口 二、LED实验2.尝试点亮LED3.LED流水灯4.利用库函数实现流水灯的移动 一、点亮第一个LED 1.GPIO介绍 这块内容这里可以做简单的了解&#xff0c;与数电知识强相关。后续可以再回过头来学习 GPIO (general purpose in…...

【简博士统计学习方法】第1章:4. 模型的评估与选择

4. 模型的评估与选择 4.1 训练误差与测试误差 假如存在样本容量为 N N N的训练集&#xff0c;将训练集送入学习系统可以训练学习得到一个模型&#xff0c;我们将这么模型用决策函数的形式表达&#xff0c;也就是 y f ^ ( x ) y\hat{f}(x) yf^​(x)&#xff0c;关于模型的拟合…...

GitLab创建用户,设置访问SSH Key

继上一篇 Linux Red Hat 7.9 Server安装GitLab-CSDN博客 安装好gitlab&#xff0c;启用管理员root账号后&#xff0c;开始创建用户账户 1、创建用户账户 进入管理后台页面 点击 New User 输入用户名、邮箱等必填信息和登录密码 密码最小的8位&#xff0c;不然会不通过 拉到…...

flutter 专题二十四 Flutter性能优化在携程酒店的实践

Flutter性能优化在携程酒店的实践 一 、前言 携程酒店业务使用Flutter技术开发的时间快接近两年&#xff0c;这期间有列表页、详情页、相册页等页面使用了Flutter技术栈进行了跨平台整合&#xff0c;大大提高了研发效率。在开发过程中&#xff0c;也遇到了一些性能相关问题和…...

RAFT:随机退火森林

RAFT:随机退火森林 RAFT(Randomized Annealed Forests)是一种机器学习算法,主要用于分类和回归任务。以下是对它的介绍及原理举例说明: 一、RAFT简介 RAFT是一种基于随机森林的集成学习方法,它结合了随机森林的优点和退火算法的思想。随机森林通过构建多个决策树并综…...

上下游服务间解耦的技术与管理

一、解耦为何至关重要 在当今软件研发的复杂生态中&#xff0c;耦合问题如影随形&#xff0c;困扰着众多开发者与企业。当多个模块、系统或团队紧密交织&#xff0c;相互依赖程度不断攀升&#xff0c;仿佛一张错综复杂的网&#xff0c;牵一发而动全身。就拿电商系统来说&#…...

[桌面运维]windows自动设置浅深色主题

设置自动浅色/深色主题 我看很多up主的教程过于繁琐&#xff0c;需要添加四个功能&#xff0c;并且有些还不能生效&#xff01; 大多数都是教程&#xff1a; 自动任务栏浅色 add HKCUSOFTWAREMicrosoftWindowsCurrentVersionThemesPersonalize/v SystemUsesLightTheme /t …...

【Spring】Spring DI(依赖注入)详解——注入参数的细节处理-内部Bean的注入

引言 在现代Java开发中&#xff0c;Spring框架已经成为了构建企业级应用的标准工具之一。Spring的核心特性之一就是依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;&#xff0c;它通过将对象的依赖关系从代码中解耦出来&#xff0c;提升了代码的可维护性…...

Docker: 教程07 - ( 如何对 Docker 进行降级和升级)

如果我们使用 docker 来管理容器&#xff0c;那么保持 docker 引擎的更新将会是十分重要的&#xff0c;这一篇文章我们将会讨论如何对Docker 进行降级和升级。 准备工作 - docker 环境 我们需要拥有一个安装好 docker 的运行环境。 如果你需要了解如何安装 docker 可以通过如…...

CV-LLM经典论文解读|VTimeLLM: Empower LLM to Grasp Video MomentsVTimeLLM:赋能大语言模型理解视频片段

论文标题 VTimeLLM: Empower LLM to Grasp Video Moments VTimeLLM&#xff1a;赋能大语言模型理解视频片段 论文链接&#xff1a; VTimeLLM: Empower LLM to Grasp Video Moments论文下载 论文作者 Bin Huang, Xin Wang, Hong Chen, Zihan Song, Wenwu Zhu (Tsinghua Un…...

新手学习yolov8目标检测小记2--对比实验中经典模型库MMDetection使用方法(使用自己的数据集训练,并转换为yolo格式评价指标)

一、按照步骤环境配置 pip install timm1.0.7 thop efficientnet_pytorch0.7.1 einops grad-cam1.4.8 dill0.3.6 albumentations1.4.11 pytorch_wavelets1.3.0 tidecv PyWavelets -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -U openmim -i https://pypi.tuna.t…...

Kubernetes开发环境minikube | 开发部署apache tomcat web单节点应用

minikube是一个主要用于开发与测试Kubernetes应用的运行环境 本文主要描述在minikube运行环境中部署J2EE tomcat web应用 minikube start --force minikube status 如上所示&#xff0c;在Linux中启动minikube运行环境 service docker start docker version service docker …...

浙江安吉成新的分布式光伏发电项目应用

摘 要&#xff1a;分布式光伏发电站是指将光伏发电组件安装在用户的建筑物屋顶、空地或其他适合的场地上&#xff0c;利用太阳能进行发电的一种可再生能源利用方式&#xff0c;与传统的大型集中式光伏电站相比&#xff0c;分布式光伏发电具有更灵活的布局、更低的建设成本和更高…...

Git - 记录一次由于少输入了一个命令导致的更改丢失

Git - 记录一次由于少输入了一个参数导致的更改丢失 前言 某晚我激情开发了几个小时&#xff0c;中途没有进行commit存档。准备睡觉时&#xff0c;我想创建一个新的分支并将今晚所有更改提交到新分支上&#xff08;似乎应该开发时候就创建&#xff1f;&#xff09;。 然后因…...

【Nginx】设置https和http同时使用同一个端口访问

以下是一个同时使用 HTTP 和 HTTPS 并通过 8070 端口的配置示例&#xff1a; server {listen 8070;server_name your_domain.com;location / {root /var/www/html;index index.html;} }server {listen 8070 ssl;server_name your_domain.com;# SSL 证书和私钥的路径ssl_certif…...

Vue 组件开发:构建高效可复用的 UI 构建块

在现代前端开发中&#xff0c;Vue.js 凭借其简洁的 API、渐进式框架设计和强大的生态系统&#xff0c;已经成为众多开发者的首选。Vue 组件化开发是其核心特性之一&#xff0c;它允许我们将复杂的 UI 拆分成多个独立、可复用的组件&#xff0c;从而提高代码的可维护性和可扩展性…...