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

news2025/1/8 19:54:42

【动作节点数据共享】

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

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

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

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

  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做划分,可以做更复杂的分类,以便于收集数据做数据分析或Debug

        private 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();
            //SaveHistory
            reqHistory.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);//获取数据所在的BB
            var 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.coloradmin.cn/o/2273398.html

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

相关文章

HTML+CSS+JS制作中华传统文化主题网站(内附源码,含5个页面)

一、作品介绍 HTMLCSSJS制作一个中华传统文化主题网站&#xff0c;包含首页、文化艺术页、传统工艺页、文化遗产页、关于我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部导航区 包含网站 Logo、主导航菜单&#xff…

笔记-使用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创…

【竞技宝】CS2:HLTV2024职业选手排名TOP8-broky

北京时间2025年1月7日,HLTV年度选手排名正在持续公布中,今日凌晨正式公布了今年的TOP8为FAZE战队的broky。 选手简介 broky是一位来自拉脱维亚的职业CS选手,现年23岁。2018年7月,broky获得了FPL资格,连续几季在榜上前5。他的首次赛场留名是跟随拉脱维亚本土战队Wolsung出征BES…

Java到底是值传递还是引用传递????

在搞懂这个问题之前, 我们要首先了解什么是值传递, 什么是引用传递? 值传递: 传递的是数据的副本&#xff0c;修改副本不会影响原始数据。引用传递: 传递的是数据的引用&#xff08;地址&#xff09;&#xff0c;修改引用会直接影响原始数据. 也就是说&#xff0c;值传递和引…

Vue3国际化多语言的切换

参考链接: link Vue3国际化多语言的切换 一、安装 vue-i18n 和 element-plus vue-i18n 是一个国际化插件&#xff0c;专为 Vue.js 应用程序设计&#xff0c;用于实现多语言支持。它允许你将应用程序的文本、格式和消息转换为用户的首选语言&#xff0c;从而提供本地化体验。…

2024AAAI SCTNet论文阅读笔记

文章目录 SCTNet: Single-Branch CNN with Transformer Semantic Information for Real-Time Segmentation摘要背景创新点方法Conv-Former Block卷积注意力机制前馈网络FFN 语义信息对齐模块主干特征对齐共享解码头对齐 总体架构backbone解码器头 对齐损失 实验SOTA效果对比Cit…

xss-labs关卡记录15-20关

十五关 随便传一个参数&#xff0c;然后右击查看源码发现&#xff0c;这里有一个陌生的东西&#xff0c;就是ng-include。这里就是&#xff1a; ng-include指令就是文件包涵的意思&#xff0c;用来包涵外部的html文件&#xff0c;如果包涵的内容是地址&#xff0c;需要加引号。…

计算机网络 (30)多协议标签交换MPLS

前言 多协议标签交换&#xff08;Multi-Protocol Label Switching&#xff0c;MPLS&#xff09;是一种在开放的通信网上利用标签引导数据高速、高效传输的新技术。 一、基本概念 MPLS是一种第三代网络架构技术&#xff0c;旨在提供高速、可靠的IP骨干网络交换。它通过将IP地址映…

打造三甲医院人工智能矩阵新引擎(四):医疗趋势预测大模型篇 EpiForecast与DeepHealthNet合成应用

一、引言 1.1 研究背景与意义 在当今数字化时代,医疗领域积累了海量的数据,涵盖电子病历、医学影像、基因序列、临床检验结果等多源异构信息。这些数据蕴含着疾病发生发展、治疗反应、疫情传播等规律,为医疗趋势预测提供了数据基础。准确的医疗趋势预测能辅助医疗机构提前…

小白学Pytorch

小白学Pytorch 发现一个比较好的教程&#xff0c;对于自己来说比较合适&#xff0c;适合从零开始的教程。 1、搭建一个简单的网络 https://www.cnblogs.com/PythonLearner/p/13587092.html 搭建网络这步说的比较清楚&#xff1a; 我们使用nn包中的Sequential搭建网络&#…

基于RedHat9部署WordPress+WooCommerce架设购物网站

系统版本信息&#xff1a;Red Hat Enterprise Linux release 9.2 (Plow) WordPress版本信息&#xff1a;wordpress-6.6.2-zh_CN WooCommerce版本信息&#xff1a;woocommerce.9.5.1 环境架构&#xff1a;LNMP&#xff08;RedHat9nginx1.20.1PHP 8.0.27MySQL8.0.30&#xff09; …

Spring源码分析之事件机制——观察者模式(一)

目录 事件基类定义 事件监听器接口 事件发布者接口及实现 事件广播器实现 小小总结 Spring源码分析之事件机制——观察者模式&#xff08;一&#xff09;-CSDN博客 Spring源码分析之事件机制——观察者模式&#xff08;二&#xff09;-CSDN博客 Spring源码分析之事件机制…

JDK、JRE、JVM三者的关系、JDK8的新特性、JVM内存结构,堆栈的区别

1&#xff0e;JDK、JRE、JVM三者的关系 JDK (Java Development Kit)----Java开发工具包&#xff0c;用于Java程序的开发。 JRE (Java Runtime Environment)----Java运行时环境&#xff0c;只能运行.class文件&#xff0c;不能编译。 JVM (Java Virtual Machine)----Java虚拟…

【Linux】文件的压缩与解压

目录 gzip和 gunzip bzip2 和 bunzip2(特点和gzip相似) xz和unxz(特点和gzip相似) zip 和 unzip tar gzip和 gunzip 特点&#xff1a;只能对单个的普通文件进行压缩 不能进行归档&#xff0c;压缩或解压后的源文件都不存在 压缩后所生成的压缩格式是.gz格式 压缩&…

LInux单机安装Redis

1. 安装gee工具包 由于Redis是基于c语言编写的所以安装的时候需要先安装gee以及gcc的依赖,yum云用不了可以看一下这个 linux 替换yum源镜像_更换yum镜像源-CSDN博客 yum install -y gcc tcl 2. 添加redis的压缩包 3. 上传到Linux 上传到 /usr/local/src 目录、这个目录一般用于…

VSCode 使用鼠标滚轮控制字体

一、 文件 | 首选项 | 设置 二、单击在 settings.json中编辑 "editor.mouseWheelZoom": true 注注注意&#xff1a;保存哦&#xff01;ctrlS 三、测试 按住ctrl鼠标滚轮&#xff0c;控制字体大小

enzymejest TDD与BDD开发实战

一、前端自动化测试需要测什么 1. 函数的执行逻辑&#xff0c;对于给定的输入&#xff0c;输出是否符合预期。 2. 用户行为的响应逻辑。 - 对于单元测试而言&#xff0c;测试粒度较细&#xff0c;需要测试内部状态的变更与相应函数是否成功被调用。 - 对于集成测试而言&a…

TCP通信原理学习

TCP三次握手和四次挥手以及为什么_哔哩哔哩_bilibili

空间不足导致Oracle集群内存使用率暴增

一、现象 操作系统内存使用率告警&#xff0c;已达到98%,&#xff0c;告警内容如下&#xff1a; 【全景监控&#xff1a;Oracle主机内存使用监控】 【主机名】&#xff1a;XXXXX11 【主机IP】主机IP&#xff1a;*.126.15 【告警内容】当前内存使用率为98.9%&#xff0c;超警…

嵌入式入门Day38

C Day1 第一个C程序C中的输入输出输出操作coutcin练习 命名空间使用方法自定义命名空间冲突问题 C对字符串的扩充C风格字符串的使用定义以及初始化C风格字符串与C风格字符串的转换C风格的字符串的关系运算常用的成员变量输入方法 布尔类型C对堆区空间使用的扩充作业 第一个C程序…