行為樹詳解(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) { }}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65302.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65302.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65302.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

阿里云 人工智能與機器學習

阿里云的 人工智能&#xff08;AI&#xff09;與機器學習&#xff08;ML&#xff09; 服務為企業提供了全面的AI解決方案&#xff0c;幫助用戶在多個行業實現數據智能化&#xff0c;提升決策效率&#xff0c;推動業務創新。阿里云通過先進的技術和豐富的工具&#xff0c;支持用…

如果Adobe 退出中國后怎么辦

最近聽說Adobe要退出中國了?那咱們的設計師們可得好好想想怎么搞到正版軟件了。別急&#xff0c;今天教大家一個超酷的福利——Edu郵箱&#xff01; Edu郵箱是什么&#xff1f;有什么好處&#xff1f; Edu郵箱就是學校給學生和老師們發的郵箱&#xff0c;一般結尾是.edu。有了…

Structured-Streaming集成Kafka

一、上下文 《Structured-Streaming初識》博客中已經初步認識了Structured-Streaming&#xff0c;Kafka作為目前最流行的一個分布式的實時流消息系統&#xff0c;是眾多實時流處理框架的最優數據源之一。下面我們就跟著官方例子來看看Structured-Streaming是如何集成Kafka的&a…

Spring Boot 項目中集成 Kafka-03

在 Spring Boot 項目中集成 Kafka 有多種方式&#xff0c;適應不同的應用場景和需求。以下將詳細介紹幾種常用的集成方法&#xff0c;包括&#xff1a; 使用 Spring Kafka (KafkaTemplate 和 KafkaListener)使用 Spring Cloud Stream 與 Kafka Binder使用 Spring for Apache K…

生物醫學信號處理--緒論

前言 參考書籍&#xff1a;劉海龍&#xff0c;生物醫學信號處理&#xff0c;化學工業出版社 生物醫學信號分類 1、由生理過程自發或者誘發產生的電生理信號和非電生理信號 ? 電生理信號&#xff1a;ECG/心電、EEG/腦電、EMG/肌電、 EGG/胃電、 EOG/眼電 ? 非電生理信號&am…

unity 播放 序列幀圖片 動畫

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、方法一&#xff1a;代碼控制播放序列幀1、設置圖片屬性2、創建Image組件3、簡單的代碼控制4、掛載代碼并賦值 二、方法二&#xff1a;直接使用1.Image上添加…

QT c++ 自定義按鈕類 加載圖片 美化按鈕

如果你有需要利用圖片美化按鈕的情況&#xff0c;本文能幫助你。 鼠標左鍵按下按鈕和松開&#xff0c;按鈕顯示不同的圖片。 1.按鈕類 //因為此類比較簡單&#xff0c;1個頭文件搞定&#xff0c;沒有cpp文件 #ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include <Q…

web漏洞之文件包含漏洞

一、文件包含漏洞 1、把DVWA頁面改為low級別&#xff0c;然后點擊File Inclusion頁面 文件包含漏洞有四種include()/require()/include_once()/require_once() 常見的文件包含漏洞代碼如下 <?php$file$_GET[filename]; filename隨意定義include($file); ?> -----…

小程序與物聯網(IoT)融合:開啟智能生活新篇章

一、引言 隨著移動互聯網技術的飛速發展&#xff0c;小程序作為一種輕量級的應用形式&#xff0c;憑借其無需下載安裝、即用即走的特點&#xff0c;迅速滲透到人們生活的各個領域。與此同時&#xff0c;物聯網&#xff08;IoT&#xff09;技術也在不斷進步&#xff0c;將各種物…

Ubuntu無法創建python venv環境

排查步驟如下 1. python3 -m venv venv he virtual environment was not created successfully because ensurepip is not available. On Debian/Ubuntu systems, you need to install the python3-venv package using the following command.apt install python3.8-venvYou…

如何很快將文件轉換成另外一種編碼格式?編碼?按指定編碼格式編譯?如何檢測文件編碼格式?Java .class文件編碼和JVM運行期內存編碼?

如何很快將文件轉換成另外一種編碼格式? 利用VS Code右下角的"選擇編碼"功能&#xff0c;選擇"通過編碼保存"可以很方便將文件轉換成另外一種編碼格式。尤其&#xff0c;在測試w/ BOM或w/o BOM, 或者ANSI編碼和UTF編碼轉換&#xff0c;特別方便。VS文件另…

PCL點云庫入門——PCL庫點云特征之PFH點特征直方圖(Point Feature Histograms -PHF)

1、算法原理 PFH點&#xff08;Point Feature Histogram&#xff09;特征直方圖的原理涉及利用參數化查詢點與鄰域點之間的空間差異&#xff0c;并構建一個多維直方圖以捕捉點的k鄰域幾何屬性。這個高維超空間為特征表示提供了一個可度量的信息空間&#xff0c;對于點云對應曲面…

5. CSS引入方式

5.1 CSS的三種樣式 按照 CSS 樣式書寫的位置(或者引入的方式)&#xff0c;CSS樣式表可以分為三大類&#xff1a; 1.行內樣式表&#xff08;行內式&#xff09; 2.內部樣式表&#xff08;嵌入式&#xff09; 3. 外部樣式表&#xff08;鏈接式&#xff09; 5.2 內部樣式表 …

為什么ip屬地一會河南一會江蘇

在使用互聯網的過程中&#xff0c;許多用戶可能會遇到這樣一個問題&#xff1a;自己的IP屬地一會兒顯示為河南&#xff0c;一會兒又變成了江蘇。這種現象可能會讓人感到困惑&#xff0c;甚至產生疑慮&#xff0c;擔心自己的網絡活動是否受到了某種影響。為了解答這一疑問&#…

unity3d-搞個場景漫游如何實現Alpha

要處理兩個問題&#xff1a; 如何設置地面人不掉下去 方法一、 游戲物體加剛體&#xff0c;將游戲物體和地面加collider。如果是地形&#xff0c;可以使用 Terrain Collider&#xff1b;如果是簡單的平面&#xff0c;可以添加 Box Collider 或者 Mesh Collider&#xff08;如果…

git merge rebase

merge操作 Git自己分支合并dev分支 rebase 操作 git rebase

doris 2.1 temporay partition 測試及總結

測試步驟 創建表 drop table order_info_shuffle; CREATE TABLE order_info_shuffle ( order_id varchar(20), user_id varchar(20), goods_id

jmeter性能測試例子

目錄 一、介紹 二、操作例子 設置線程數 添加同步定時器 添加聚合報告 一、介紹 在軟件測試中&#xff0c;一般用jmeter來對接口做性能測試&#xff0c;對對接口進行一個壓力的測試。 簡述&#xff1a; 在接口的線程中設置線程的數量和時間&#xff0c;添加一個定時器…

C# 設計模式(行為型模式):解釋器模式

C# 設計模式&#xff08;行為型模式&#xff09;&#xff1a;解釋器模式 (Interpreter Pattern) 什么是解釋器模式&#xff1f; 解釋器模式&#xff08;Interpreter Pattern&#xff09;是一種行為型設計模式&#xff0c;用于定義一種語言的語法表示&#xff0c;并提供一個解釋…