B4:Unity制作Moba类游戏——小兵AI系统

news2024/11/23 19:22:47

           若想取得战争的胜利,必先控好兵线。

                                                             ———— 麦克阿瑟

           是时候让敌人经历一下我们兵线的洗礼。

                                                             ———— 拿破仑

 在LOL对局中,职业选手对兵线的控制可以说是达到了“运筹帷幄之中,决胜千里之外”。其实普通玩家只要控好兵线,在对线中一样可以打开优势。

这一篇会介绍,Moba游戏的小兵AI系统在游戏中的推进规律,相信看完此篇,就可以像我一样,怒升两个段位。(黑铁5——黑铁3

小兵AI系统非常简单!我们会用到这几点技术:

构建易扩展的AI基类和数据结构

有限状态机FSM控制小兵AI行为状态

NavMesh自动寻路系统控制小兵移动

缓存池CatchTool 控制小兵动态创建和销毁

……

直接上代码…………………………………………

第一步,定义AI系统基类,基类中定义多个AI必须的行为类

public class AI : MonoBehaviour
{
    /// <summary>
    /// AI自身对象
    /// </summary>
    public GameObject obj;
    /// <summary>
    /// 自身ID
    /// </summary>
    public int AIID;
    /// <summary>
    /// 名字
    /// </summary>
    public string Name;
    /// <summary>
    /// 唯一识别ID
    /// </summary>
    public int UniqueID;
    /// <summary>
    /// AI类型
    /// </summary>
    public AIType AIType;
    /// <summary>
    /// 阵营
    /// </summary>
    public CampEnum CampEnum;
    /// <summary>
    /// 是否存活
    /// </summary>
    public bool isAlive;
    /// <summary>
    /// AI皮肤
    /// </summary>
    public AISkin skin = new AISkin();
    /// <summary>
    /// AI属性
    /// </summary>
    public AIAttribute attribute = new AIAttribute();
    /// <summary>
    /// AI属性变化事件
    /// </summary>
    public AttributeEvent attributeEvent = new AttributeEvent();
    /// <summary>
    /// AI成长属性
    /// </summary>
    public AIGrowUp growUp = new AIGrowUp();
    /// <summary>
    /// 播放动画
    /// </summary>
    public AIAnimation aiAnimation = new AIAnimation();

AI小兵派生类:

/// <summary>
/// 小兵AI派生类
/// </summary>
public class SoldierAI : AI
{
    /// <summary>
    /// 自动寻路组件
    /// </summary>
    public AINaveMesh aINaveMesh;
    /// <summary>
    /// 小兵状态机组件
    /// </summary>
    public SoldierBehaviour soldierBehaviour;
    /// <summary>
    /// 小兵侦察距离检测类
    /// </summary>
    public SoldierDetectionCollider soldierDetectionCollider;
    /// <summary>
    /// 小兵战斗距离检测类
    /// </summary>
    public SoldierAttackCollider soldierAttackCollider;
    /// <summary>
    /// 小兵技能
    /// </summary>
    public SoldierSkill soldierSkill;
    /// <summary>
    /// 小兵UI画布
    /// </summary>
    public SoldierCanvas soldierCanvas;
    /// <summary>
    /// 小兵类型
    /// </summary>
    public string SoldierType;
    /// <summary>
    /// 是否死亡
    /// </summary>
    private bool isDeath = false;

定义小兵AI管理器类,用于管理小兵AI的创建、销毁、属性成长等等

/// <summary>
/// 小兵管理器类
/// </summary>
public class SoldierManager : MonoSingle<SoldierManager>
{

    /// <summary>
    /// 小兵加载波次
    /// </summary>
    private int Frequency = 0;
    /// <summary>
    /// 加载小兵间隔
    /// </summary>
    private float LoadInterval = 0;
    /// <summary>
    /// 倒计时
    /// </summary>
    private float Countdown = 1f;
    /// <summary>
    /// 间隔时间
    /// </summary>
    private float IntervalTime = 30f;
    /// <summary>
    /// 判定是否开始小兵系统
    /// </summary>
    private bool isStart = false;
    /// <summary>
    /// 
    /// </summary>
    private TerrainEnum GameTerrain;
    /// <summary>
    /// 对象池小兵预制体
    /// </summary>
    public Dictionary<string, SoldierAI> SoldierPrefabs = new Dictionary<string, SoldierAI>();

    //小兵临时对象

    public Queue<SoldierAI> Temp_SoldierMelee_Blue = new Queue<SoldierAI>();
    public Queue<SoldierAI> Temp_SoldierRemote_Blue = new Queue<SoldierAI>();
    public Queue<SoldierAI> Temp_GunTruck_Blue = new Queue<SoldierAI>();
    public Queue<SoldierAI> Temp_SoldierMelee_Red = new Queue<SoldierAI>();
    public Queue<SoldierAI> Temp_SoldierRemote_Red = new Queue<SoldierAI>();
    public Queue<SoldierAI> Temp_GunTruck_Red = new Queue<SoldierAI>();

    /// <summary>
    /// 所有小兵容器
    /// Key:小兵唯一识别符
    /// Value:小兵AI
    /// </summary>
    public Dictionary<int, SoldierAI> Soldiers_Dic = new Dictionary<int, SoldierAI>();
    /// <summary>
    /// 是否刷新小兵
    /// </summary>
    private bool isRefresh = false;
    /// <summary>
    /// 当前刷新帧
    /// </summary>
    private float refreshCDNow = 0;
    /// <summary>
    /// 每个小兵刷新间隔
    /// </summary>
    private float refreshCD = 0.7f;
    /// <summary>
    /// 
    /// </summary>
    private int refreshNumber = 0;
    /// <summary>
    /// 小兵类型列表
    /// </summary>
    private string[] refreshList = new string[7];

定义小兵缓存池,并创建小兵缓存

  //根据游戏模式加载小兵缓存池
        int SoldierRemote = 0;
        int SoldierMelee = 0;
        int GunTruck = 0;

        switch (GameTerrain)
        {
            case TerrainEnum.Gorge:
                SoldierRemote = 27;
                SoldierMelee = 27;
                GunTruck = 9;
                break;
            case TerrainEnum.Bridge:
                SoldierRemote = 9;
                SoldierMelee = 9;
                GunTruck = 3;
                break;
            case TerrainEnum.Jungle:
                SoldierRemote = 27;
                SoldierMelee = 27;
                GunTruck = 9;
                break;
            default:
                break;
        }
        //创建小兵缓存池
        ToolManager.objectPool_C.Init_Public("SoldierRemote_Blue", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("SoldierRemote_Blue", SoldierPrefabs["SoldierRemote_Blue"].obj, SoldierRemote);
        ToolManager.objectPool_C.Init_Public("SoldierMelee_Blue", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("SoldierMelee_Blue", SoldierPrefabs["SoldierMelee_Blue"].obj, SoldierMelee);
        ToolManager.objectPool_C.Init_Public("GunTruck_Blue", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("GunTruck_Blue", SoldierPrefabs["GunTruck_Blue"].obj, GunTruck);

        ToolManager.objectPool_C.Init_Public("SoldierRemote_Red", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("SoldierRemote_Red", SoldierPrefabs["SoldierRemote_Red"].obj, SoldierRemote);
        ToolManager.objectPool_C.Init_Public("SoldierMelee_Red", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("SoldierMelee_Red", SoldierPrefabs["SoldierMelee_Red"].obj, SoldierMelee);
        ToolManager.objectPool_C.Init_Public("GunTruck_Red", PoolParentType.Model);
        ToolManager.objectPool_C.SetPool_Public("GunTruck_Red", SoldierPrefabs["GunTruck_Red"].obj, GunTruck);

第二步,和场景类似,将小兵模型预制体打包成ab包,供加载使用

 打包ab包方法:

 /// <summary>
    /// 编译资源
    /// </summary>
    /// <param name="targetPath">目标位置</param>
    /// <param name="prefabs">预制体</param>
    /// <param name="scenes">场景</param>
    /// <param name="buildTarget">目标平台</param>
    [MenuItem("开始打包/打包Obj")]
    public static void BuildingObj()
    {
        AssetBundleManifest prs = null;
        try
        {
            UnityEngine.Object[] selects = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
            //添加资源  
            foreach (UnityEngine.Object obj in selects)
            {
                List<AssetBundleBuild> prefabBuilds = new List<AssetBundleBuild>();  //预制体资源
                AssetBundleBuild build = new AssetBundleBuild();
                build.assetBundleName = obj.name + ".assetBundle";
                string assetPath = AssetDatabase.GetAssetPath(obj);
                build.assetNames = new string[] { assetPath };
                prefabBuilds.Add(build);

                string prefabPath = Environment.CurrentDirectory + "/DownLoad/Assetbundle/" + obj.name;
                if (!Directory.Exists(prefabPath)) Directory.CreateDirectory(prefabPath);
                try
                {
                    prs = BuildPipeline.BuildAssetBundles(prefabPath, prefabBuilds.ToArray(), BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
                }
                catch (System.Exception ex)
                {
                    Debug.Log("预制体 打包 失败:" + ex.ToString());
                }
            }
        }
        catch (UnityException e)
        {
            Debug.Log("预制体 打包 失败:" + e.ToString());
            prs = null;
        }
        if (prs != null)
        {
            Debug.Log("预制体 打包 成功");
        }
        else
        {
            Debug.Log("预制体 打包 失败");
        }
        //刷新编辑器(不写的话要手动刷新,否则打包的资源不能及时在Project视图内显示)
        AssetDatabase.Refresh();
    }

 加载ab包方法:

 UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(URL);
                        www.SendWebRequest();
                        while (!www.isDone)
                        {
                            yield return null;
                        }
                        yield return www;
                        assetBundle = DownloadHandlerAssetBundle.GetContent(www);
                        if (assetBundle != null)
                        {
                            GameObject[] objects = assetBundle.LoadAllAssets<GameObject>();
                            yield return new WaitForEndOfFrame();
                            GameObject model;
                            if (objects[0])
                            {
                                try
                                {
                                    model = Instantiate(objects[0]);
                                    action(model);
                                }
                                catch (System.Exception EX)
                                {
                                    iDebug.YiYan("克隆模型物体失败!" + EX, DebugColor.red);
                                    model = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                    action(model);
                                }
                            }
                            else
                            {
                                iDebug.YiYan("AB包为空!", DebugColor.red);
                                model = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                action(model);
                            }
                        }
                        else
                        {
                            iDebug.YiYan("AB包为空", DebugColor.red);
                        }
                        if (assetBundle != null)
                        {
                            assetBundle.Unload(false);
                        }
                        www.Dispose();

*第三步,小兵AI之间自动检测战斗系统

这个是AI系统中比较重要的环节,双方小兵按照规定移动轨迹走到线上,然后开始自动战斗,其中AI状态大概分为:

自动寻路状态           对线状态          攻击状态            防守状态                

死亡状态             停止状态             追击状态             强制攻击状态


小兵的行为应该在遇到对应变化时适时切换这几种状态,而切换的方法则要用到设计模式中常用的模式:状态模式,也就是FSM有限状态机,代码如下:

状态机基类:

/// <summary>
/// 状态机基类
/// </summary>
public class BaseState
{
    /// <summary>
    /// 状态名称
    /// </summary>
    private string m_StateName = "BaseState";
    public string StateName
    {
        get { return m_StateName; }
        set { m_StateName = value; }
    }
    /// <summary>
    /// 控制器
    /// </summary>
    protected BaseStateController m_Controller = null;
    /// <summary>
    /// AI行为逻辑
    /// </summary>
    public StateBehaviour m_Behaviour = null;
    /// <summary>
    /// 构造函数——建造者
    /// </summary>
    /// <param name="Controller"></param>
    public BaseState(BaseStateController Controller, StateBehaviour behaviour = null)
    {
        m_Controller = Controller;
        if (behaviour != null)
            m_Behaviour = behaviour;
    }
    /// <summary>
    /// 状态开始
    /// </summary>
    public virtual void StateBegin() { }
    /// <summary>
    /// 状态更新
    /// </summary>
    public virtual void StateUpdate() { }
    /// <summary>
    /// 状态结束
    /// </summary>
    public virtual void StateEnd() { }


    /// <summary>
    /// 输出当前状态
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return string.Format("[StateName = {0}]", StateName);
    }


}

状态机控制器类:

/// <summary>
/// 状态机控制器类
/// </summary>
public class BaseStateController
{
    /// <summary>
    /// 切换的当前状态
    /// </summary>
    private BaseState m_State;
    /// <summary>
    /// 是否已经开始执行当前状态
    /// </summary>
    private bool m_bRunBegin = false;
    /// <summary>
    /// 构造
    /// </summary>
    public BaseStateController() { }

    /// <summary>
    /// 状态机操作方法——执行设置状态方法,执行结束状态方法
    /// 1、执行上一个状态的结束方法
    /// 2、设置新的状态
    /// </summary>
    /// <param name="State"></param>
    /// <param name="Value"></param>
    public void SetState(BaseState State)
    {
        m_bRunBegin = false;
        //通知前一个状态结束
        if (m_State != null)
        {
            m_State.StateEnd();
        }
        m_State = State;
    }
    /// <summary>
    /// 状态机操作方法——执行开始状态方法
    /// </summary>
    public void StateBegin()
    {
        if (m_State != null && m_bRunBegin == false)
        {
            m_State.StateBegin();
            m_bRunBegin = true;
        }
    }
    /// <summary>
    /// 状态机操作方法——执行更新状态方法
    /// </summary>
    public void StateUpdate()
    {
        if (m_State != null && m_bRunBegin == true)
        {
            m_State.StateUpdate();
        }
    }
}

状态机控制器:

public class StateBehaviour : MonoBehaviour
{
    /// <summary>
    /// ID
    /// </summary>
    public int ID;
    /// <summary>
    /// 名字
    /// </summary>
    public string Name;
    /// <summary>
    /// 状态集合
    /// </summary>
    public Dictionary<string, BaseState> StateDic;
    /// <summary>
    /// 状态机控制器
    /// </summary>
    public BaseStateController StateController;

    public virtual void Awake()
    {
        StateDic = new Dictionary<string, BaseState>();
        StateController = new BaseStateController();
    }

    public virtual void Start()
    {
       
    }

    public virtual void Update()
    {
        StateController.StateUpdate();
    }

    /// <summary>
    /// 初始化状态机
    /// </summary>
    public virtual void Init(int id, string name)
    {
        ID = id;
        Name = name;
 
    }

    /// <summary>
    /// 设置状态
    /// </summary>
    /// <param name="state"></param>
    public virtual void SetState(string state)
    {
        StateController.SetState(StateDic[state]);
        StateController.StateBegin();
    }






}

小兵AI状态机管理器

public class SoldierBehaviour : StateBehaviour
{
    /// <summary>
    /// 本身物体
    /// </summary>
    public GameObject Local;
    /// <summary>
    /// 目标物体
    /// </summary>
    public GameObject Target;
    /// <summary>
    /// 自身AI对象
    /// </summary>
    public SoldierAI AI;

    public override void Awake()
    {
        base.Awake();
        StateDic.Add("SoldierState_Alignment", new SoldierState_Alignment(StateController, this));
        StateDic.Add("SoldierState_Attack", new SoldierState_Attack(StateController, this));
        StateDic.Add("SoldierState_CrazyAttack", new SoldierState_CrazyAttack(StateController, this));
        StateDic.Add("SoldierState_Defense", new SoldierState_Defense(StateController, this));
        StateDic.Add("SoldierState_Death", new SoldierState_Death(StateController, this));
        StateDic.Add("SoldierState_Pathfinding", new SoldierState_Pathfinding(StateController, this));
        StateDic.Add("SoldierState_Pursuit", new SoldierState_Pursuit(StateController, this));
        StateDic.Add("SoldierState_Stop", new SoldierState_Stop(StateController, this));
    }

    public override void Start()
    {
        base.Start();
    
    }

    public override void Update()
    {
        base.Update();

    }

    /// <summary>
    /// 初始化状态机
    /// </summary>
    public void Init(int id, string name, SoldierAI ai)
    {
        base.Init(id, name);
        AI = ai;
     //   iDebug.YiYan("小兵状态机初始化");
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    public void SetGameState(string name)
    {
        SetState(name);
    }

    /// <summary>
    /// 结束系统
    /// </summary>
    public void End()
    {
        Target = null;

    }
}

然后,就是在小兵进行途中,在小兵不同的状态下写好当前状态应该做的行为,和判定切换状态的条件行为,举个例子:

小兵在自动寻路状态下在兵线上走,突然攻击预警范围内有敌人出现,则自动切换到追击状态。

等走到攻击范围内,则切换到攻击状态,开始攻击敌人。

当敌人离开攻击范围但尚未离开预警范围,则继续切换到追击状态。

当敌人离开预警范围,则切换到自动寻路状态。

自动寻路状态代码:

/// <summary>
    /// 状态开始
    /// </summary>
    public override void StateBegin()
    {
       // iDebug.YiYan("开始State:SoldierState_Pathfinding" + soldierBehaviour.AI.obj.GetInstanceID());

        //播放行走动画

        //获取寻路目标
        soldierBehaviour.AI.aINaveMesh.SetState(true);
        soldierBehaviour.AI.aINaveMesh.SetFinalTarget();

    }

    /// <summary>
    /// 状态更新
    /// </summary>
    public override void StateUpdate()
    {
        //判断自身区域触发器是否有敌人
        if (soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Count > 0)
        {
            soldierBehaviour.AI.aINaveMesh.Target = soldierBehaviour.AI.soldierDetectionCollider.EnemyList[0];
            soldierBehaviour.SetState("SoldierState_Pursuit");
        }


    }

追击状态代码:

 /// <summary>
    /// 状态开始
    /// </summary>
    public override void StateBegin()
    {
      //  iDebug.YiYan("开始State:SoldierState_Pursuit" + soldierBehaviour.AI.obj.GetInstanceID());
        //播放行走动画

        if (soldierBehaviour.AI.aINaveMesh.Target != null && soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
        { 
            soldierBehaviour.AI.aINaveMesh.SetDestinationTarget(soldierBehaviour.AI.aINaveMesh.Target.obj); 
        }
    }

    /// <summary>
    /// 状态更新
    /// </summary>
    public override void StateUpdate()
    {
        //判断自身区域触发器是否有敌人
        if (soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
        {
            soldierBehaviour.AI.aINaveMesh.SetState(false);
            soldierBehaviour.SetState("SoldierState_Attack");
        }
        if (!soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
        {
            soldierBehaviour.SetState("SoldierState_Pathfinding");
        }
    }

攻击状态代码:

/// <summary>
    /// 状态开始
    /// </summary>
    public override void StateBegin()
    {
       // iDebug.YiYan("开始State:SoldierState_Attack" + soldierBehaviour.AI.obj.GetInstanceID());
        if (soldierBehaviour.AI.aINaveMesh.Target != null && soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
        {
            soldierBehaviour.AI.soldierSkill.SetTarget(soldierBehaviour.AI, soldierBehaviour.AI.aINaveMesh.Target);
            soldierBehaviour.AI.soldierSkill.AutoAttack(true);
        }
    }

    /// <summary>
    /// 状态更新
    /// </summary>
    public override void StateUpdate()
    {
        //持续对目标释放技能

        //播放攻击动画

        if (!soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target) && soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
        {
            soldierBehaviour.AI.aINaveMesh.SetState(true);
            soldierBehaviour.SetState("SoldierState_Pursuit");
        }
        else if (!soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target)|| soldierBehaviour.AI.aINaveMesh.Target.attribute.attributeValueNow.HP <= 0)
        {
            soldierBehaviour.AI.aINaveMesh.SetFinalTarget();
            soldierBehaviour.AI.aINaveMesh.SetState(true);
            soldierBehaviour.SetState("SoldierState_Pathfinding");
         //   iDebug.YiYan("切换目标");
        }
        else 
        {
           // iDebug.YiYan("不断攻击");
        }

    }

判定效果就像这样~~~ :

是不是非常简单易懂(笑脸+手动狗头~~)

最后,放一段小兵对线的效果:

(为了连贯性,所以把UI界面和资源导入的效果都录入了) 

 这里有的小伙伴会有疑问了,小兵的战斗数值和防御塔的属性数值是多少,如何设置呢?

哈~ 当然是在数据库中,由服务器统一分发了

 数值就动态读取,然后赋予到AI基类里即可,后面的篇章会介绍技能系统和战斗计算,那时候再详细介绍数值调用的详细逻辑吧

↓↓↓↓↓↓

文末福利:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91156.html

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

相关文章

Java Servlet详解(补充,极其重要)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

SwiftUI 中列表行(List Row)展开和收起无动画或动画诡异的解决

文章目录 问题现象问题分析1. 为什么 List 行展开与收起没有动画效果?2. 第一种解决方法3. 另一种巧妙的解决总结结束语问题现象 SwiftUI 中展开(expand)和收起(collapse)列表行(List Row)是一个常见的操作,不过默认来说这样的操作不会有动画效果: 如上图所示,我们为…

粒子滤波算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

CpG ODN——艾美捷ODN 1826 (TLRGRADE)说明书

艾美捷CpG ODN系列——ODN 1826 (TLRGRADE)&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷CpG ODN 丨ODN 1826 (TLRGRADE)化学性质&#xff1a; 备选名称&#xff1a;CpG-B…

Suspense组件

先上官网&#xff1a;https://cn.vuejs.org/guide/built-ins/suspense.html 注意一下 <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 在使用了之后在浏览器控制台会有如下打印&#xff0c;至少目前是…

【大数据】有关zookeeper的问题

如图&#xff0c;启动zookeeper失败&#xff0c;输入 zkServer.sh start-foreground 查看失败原因 Invalid config&#xff0c;我得知是配置文件出了问题&#xff0c;但是检查配置文件没有发现错误 最终在配置文件末尾配置参数结尾发现了未删除的空格 将三个节点配置文件中的…

C/C++ 和 Java的编译运行机制比较 个人理解

计算机程序语言按程序的执行方式可分为编译型语言和解释性语言。 编译型语言是指使用专用的编译器&#xff0c;针对某操作系统将高级语言源代码一次性地翻译成可被该系统硬件执行的机器码(包括机器指令和操作数&#xff09;&#xff0c;并包装成该系统所能识别的可执行程序的格…

同事开源我的微服务深度实践笔记到 GitHub,短短 3 天竟吸粉 1W+

说Spring成就了Java&#xff0c;Spring是Java程序员必修课之一&#xff0c;应该没人反对吧&#xff1f;前几年面试最常问的且可以顺利拿到高薪的技能是Spring&#xff0c;随着Spring体系的壮大&#xff0c;除非你在简历上添加Spring Boot和Spring Cloud的技能&#xff0c;才可以…

Kubernetes——Debug Static Pod

1. 问题背景 注意&#xff0c;我这里的Static Pod并非Kubernetes的Static Pod&#xff0c;而是需要把想要Debug的程序放到Delve环境中重新打包一个镜像。因为还有另外一种场景&#xff0c;那就是我们需要不重启Running Pod&#xff0c;为了和这种方式区分&#xff0c;才以此为…

彻底卸载并重装Anaconda环境与Python的方法

本文介绍在Windows平台下&#xff0c;彻底删除Anaconda环境与其自带Python版本&#xff0c;并进行重新安装的方法。 最近&#xff0c;由于原有Anaconda环境中的部分第三方库出现了冲突的情况&#xff0c;且基于“Anaconda Prompt (anaconda3)”也无法升级Anaconda与相关库了&am…

《超新星纪元》

《超新星纪元》 关于作者 刘慈欣&#xff0c;髙级工程师&#xff0c;科普作家&#xff0c;被誉 为"中国当代科幻第一人"。自上世纪90年代开始&#xff0c;他一边在发电厂担任计算机工程师&#xff0c;一边利用业余时间出版了13本小说集&#xff0c;连续数年获得中国…

这是一篇讲解用户行为分析的推荐书单和总结

写在前面 技术文延迟了 本来计划参加活动的还有一篇&#xff0c;应该是一篇技术翻译文&#xff0c;但是那篇文章太难了&#xff0c;看我过我以往文章的同学&#xff0c;应该能理解&#xff0c;我的文章很少有3000字数以下的&#xff0c;而且如果不是来自谷歌&#xff08;主要…

深蓝学院-多传感器融合定位课程-第9章-基于图优化的建图方法

专栏文章: 深蓝学院-多传感器融合定位课程-第1章-概述_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第2章-3D激光里程计I_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第3章-3D激光里程计II_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第…

2022品牌出海:日本网红营销白皮书

日本作为世界第三大经济体&#xff0c;同时也是世界第四大电子商务市场&#xff0c;亚洲第二大消费市场&#xff0c;其经济水平和消费能力都非常出色。对出海企业来说&#xff0c;日本是一个非常有吸引力的市场。日本的网红营销市场也非常成熟&#xff0c;在疫情的影响下&#…

java设计模式之原型模式(prototype Pattern)

原型模式主要在于对象的克隆&#xff0c;所以也叫克隆模式 其实就是利用java中的Object对象中的clone方法实现一个对象的克隆。此方法需要注意的是&#xff0c;一个对象想要实现克隆&#xff0c;就必须实现一个标志性接口Cloneable 现在先来说一下浅克隆 这玩意也叫表皮克隆&…

【前端开发学习】1.前端引入和HTML标签

文章目录1.快速开发网站2. 浏览器能识别的标签2.1 编码&#xff08;head&#xff09;2.2 Title&#xff08;head&#xff09;2.3 标题&#xff08;body&#xff09;2.4 div 和 span&#xff08;body&#xff09;2.5 超链接&#xff08;body&#xff09;2.6 图片&#xff08;bod…

基于微信小程序的居民疫情服务系统springboot框架-计算机毕业设计

项目介绍 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;居民疫情服务系统小程序被用户普遍使用&#xff0c;为…

一个基于.Net开发齐全的加密库

网络安全&#xff0c;对于我们项目日常开发&#xff0c;是非常重要的&#xff0c;比如密码加密存储、密码加密传输、SSL证书、端对端加密等等。 这些加密算法&#xff0c;每个知识点都包含了很多内容&#xff0c;自己实现都需要花费很多时间&#xff0c;所以今天就给大家推荐一…

Qt 模型视图编程之增删行列

背景 Qt 模型视图编程中模型定义了标准接口对数据进行访问&#xff0c;可根据需求继承对应的抽象模型类来实现自定义的数据模型。一个基本的数据模型至少要实现以下虚函数&#xff1a; ①&#xff0e;rowCount&#xff1a;行数&#xff0c;返回要显示多少行&#xff1b; ②&…

net基于asp.net的社区团购网站-计算机毕业设计

项目介绍 社区团购系统依托社区团购系统和社区门店,是现在的一个重大市场和发展方向,通过研究企业在社区团购系统环境下的营销模式创新,对于普通的零售业和传统社区团购系统的转型发展具有重要的理论意义。随着互联网行业的发展,人们的生活方式发生着重大变化,人们越来越倾向于…