Unity2D 商业游戏案例 - 梦幻西游(第二季 框架设计篇)

news2024/11/20 7:15:18

00 网址 来源

siki学院的(1年有限期到期前下载的项目,现在已经过期,所以自己理清项目)
所以更多的不是学习这个项目,而是学习理清该类型的项目的思路
Unity2D 商业游戏案例 - 梦幻西游(第二季 框架设计篇)
。。。。
Unity2D 回合制游戏案例 - 梦幻西游(第一季 战斗逻辑篇)【B站的第一季的部分视频】
在这里插入图片描述

00 插件

Cinemachine Camera
自己的代码库,用到的主要是对Component类组件的拓展

00 知识点

StateMachineBehaviour的使用
Trigger写的一个游戏框架 有 System,Model,Command,Event,IOC(单例的容器)等概念

00 了解 修饰符

modify :做了修改
stars:改改后,值得收藏的代码
watch:进去跑一下逻辑
bug:自己修改后报错的地方
scene:场景,以场景为分割线划分内容

01 总体代码---------------------

VS解决方案视图,自己做的类图

枚举类型是自己拆出来的,后面有
。。。
.cd是类图。
在这里插入图片描述

modify partial 分开脚本、手动控制 Init()

.cd是类图。类图只能看个大概的继承关系,最好的是将引用到的脚本尽可能地集合起来。
比如CharacterFigthAI,用 partial 分开脚本,用 手动控制 Init() 代替 Awake()、Start()
在这里插入图片描述

modify 动态添加

这种写法能做清晰地知道脚本要控制哪些UI,哪些按钮、Slider有监听事件

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


//*****************************************
//创建人: Trigger 
//功能说明:战斗UI管理
//***************************************** 

/// <summary>战斗UI管理</summary>
public class FightUIManager : MonoBehaviour,IController
{
    private Button defendCommandBtn;
    private Button skillCommandBtn;
    private Button useItemCommandBtn;
    private GameObject fightCommandPanelGo;

    public void Init()
    {
        gameObject.SetActive(true);
        fightCommandPanelGo =   transform.FindChildDeep( "Emp_FightCommand").gameObject;
        fightCommandPanelGo.SetActive(true);
        skillCommandBtn =       transform.FindChildDeep( "Btn_Skill").GetComponent<Button>();
        useItemCommandBtn =     transform.FindChildDeep( "Btn_UseItem").GetComponent<Button>();
        defendCommandBtn =      transform.FindChildDeep( "Btn_Defend").GetComponent<Button>();

        //
        skillCommandBtn.onClick.AddListener(ClickSkillBtn);
        useItemCommandBtn.onClick.AddListener(ClickUseItemBtn);
        defendCommandBtn.onClick.AddListener(ClickDefendBtn);

        //
        this.RegistEvent<OpenOrCloseFightCommandPanelEvent>(OpenOrCloseFightCommandPanel);
        fightCommandPanelGo.SetActive(false);
    }

watch 脚本入口

GameStartInstance调用了XYQArchitecture.Init()

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:XYQ游戏架构
//***************************************** 
public class XYQArchitecture : Architecture<XYQArchitecture>
{
    protected override void Init()
    {
        RegistSystem<ISystem>(new UISystem());
        RegistSystem<ISkillSystem>(new SkillSystem());
        RegistSystem<IFightSystem>(new FightSystem());
        RegistSystem<ISceneSystem>(new SceneSystem());

        RegistModel<IPlayerDataModel>(new PlayerDataModel());
        RegistModel<ISkillDataModel>(new SkillDataModel());
        RegistModel<ICharacterDataModel>(new CharacterDataModel());
    }
}

modify 枚举类型

将业务具体代码(SpecificCode)的枚举类型全部提到一个文件夹下。
另一个是架构代码,我先不动。
在这里插入图片描述

modify 结构体

将业务具体代码(SpecificCode)的struct类型(不继承,因为有的:ICommand)全部提到一个文件夹下。(后面推的时候觉得应该做的,不是一开始就知道要这样做)
在这里插入图片描述

modify 各种字符串变量

Invoke的方法名,路径,节点名,资源名,路径等用静态类和cosnt存起来,方便查引用和变量汇总
举例Tags(标签)

/****************************************************
    文件:Tags.cs
	作者:lenovo
    邮箱: 
    日期:2022/7/15 12:57:58
	功能:
*****************************************************/


public static class Tags
{
    public const string Canvas = "Canvas";
    public const string PLAYER = "Player";
    public const string BULLET = "Bullet";
    public const string ENEMY = "Enemy";
  
    /// <summary>屏障</summary>
    public const string SHIELD = "Shield";
    public const string ITEM = "Item";
}

modify FightSystem

汇总到GameObjectPath,GameObjectName

    public void Init()
    {
        CharacterPrefab = ExtendResources.Get<GameObject>(GameObjectPath.Prefab_CharacterFight).GetComponent<CharacterFightAI>();
        Transform tf = GameObject.Find(GameObjectName.FightNavMesh).transform;
        PlayerInitPosTrans = tf.Find(GameObjectName.PlayerPos);
        GetPositionsTrans(tf, GameObjectName.EnemyPos, ref enemyInitPosTrans);
        GetPositionsTrans(tf, GameObjectName.PlayerDieStartMovePath, ref playerDieStartMovePath);
        GetPositionsTrans(tf, GameObjectName.PlayerDieEndMovePath, ref playerDieEndMovePath);
        GetPositionsTrans(tf, GameObjectName.EnemyDieStartMovePath, ref enemyDieStartMovePath);
        GetPositionsTrans(tf, GameObjectName.EnemyDieEndMovePath, ref enemyDieEndMovePath);
    }

bug 紧凑

像这种没有空行的,不知道是原来就这样,还是被软件重置了
在这里插入图片描述

stars 简写

个人习惯

attack,atk
button,btn
commnd,cmd
config,cfg
count,cnt
current,cur
from和to,from攻击者,to被攻击者。表示攻击这个行为,从 from 到 to
list,lst
manager,mgr
object,obj
request,req
response,rsp
system,sys(可能与sync比较近)
target,tar

02 Scene 进入游戏----------------

watch 开头动画

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using static ExtendTweenMethods;

public class LoginScene : MonoBehaviour
{
    /// <summary>bgTrans的父节点,拖拽赋值</summary>
    public Transform bg;
    public Transform[] bgTrans;
    public Vector3[] targetPos;
    public ExtendTweenMethods.Tween[] tweens;

    // Start is called before the first frame update
    void Start()
    {
        InitBgTrans();
        InitTargetPos();
        InitTweens();

    }

    #region 辅助


    private void InitBgTrans()
    {
        //4张背景图(从左到右)的trans
        bgTrans = new Transform[4];
        for (int i = 0; i < bgTrans.Length; i++)
        {
            bgTrans[i] = bg.GetChild(i);
        }        
    }
    private void InitTargetPos()
    {
        //4张背景图012(从)的目标位置123
        targetPos = new Vector3[bgTrans.Length];
        for (int i = 0; i < bgTrans.Length-1; i++)
        {
            targetPos[i] = bgTrans[i + 1].position; 
        }
        //右边两张不动,左边两个一直两班倒(因为23是为了给01确定目标位置,不移动的)
        targetPos[0] = bgTrans[0].position; //单独空出targetPos[0]来给右边的换到左边        
    }

    void InitTweens()
    { 
        tweens = new ExtendTweenMethods.Tween[2];
        tweens[0]= bgTrans[0].DoMove( target:targetPos[1], time:50, loop:100);//左边跑50
        tweens[1] = bgTrans[1].DoMove(target: targetPos[1], time:25, loop:1).SetOnComplete(() => //右边跑25     
        {
            bgTrans[1].position = targetPos[0];//右边放左边
            bgTrans[1].DoMove(targetPos[1], 50, 100);//继续Move
        });    
    }

    void KillTweens(Tween[] tweens )
    { 
        for (int i = 0; i < tweens.Length; i++)
        {
            tweens[i].Kill();
        }    
    }

    public void LoadGame()
    {
        KillTweens(tweens);
        SceneManager.LoadScene(1);
    }
    public void ExitGame()
    {
        Application.Quit();
    }
    #endregion
}

在这里插入图片描述

stars DeepFindChild

因为想把

GameStartInstance.DeepFindChild(Transform t, string childName);

改成

t.FindChildDeep( string childName);

少写很多。需要改以下

public class GameStartInstance : MonoBehaviour
{
	......
    /// <summary>
    /// 深度查找子对象transform引用
    /// </summary>
    /// <param name="root">父对象</param>
    /// <param name="childName">具体查找的子对象名称</param>
    /// <returns></returns>
    public static Transform DeepFindChild(Transform root, string childName)
    {
        Transform result = null;
        result = root.Find(childName);
        if (!result)
        {
            foreach (Transform item in root)
            {
                result = DeepFindChild(item, childName);
                if (result != null)
                {
                    return result;
                }
            }
        }
        return result;
    }
}

改成

public static class ExtendComponent
{
    /// <summary>
    /// 深度查找子对象transform引用
    /// </summary>
    /// <param name="root">父对象</param>
    /// <param name="childName">具体查找的子对象名称</param>
    /// <returns></returns>
    public static Transform FindChildDeep(this Transform root, string childName)
    {
        Transform result = null;
        result = root.Find(childName);
        if (!result)
        {
            foreach (Transform item in root)
            {
                result = FindChildDeep(item, childName);
                if (result != null)
                {
                    return result;
                }
            }
        }
        return result;
    }
}

03 Scene 世界-----------------------

bug 原来的世界场景

右边的原版的场景直接露出来(包括白色图片(转场用的))
在这里插入图片描述

watch 总体ui

除了聊天框,其他的UI都没做
在这里插入图片描述

stars 删掉GameRes

从继承于MonoBehaviour改成 this Resource,和一个被启动脚本GameStartInstance调用的实例启动方法。
删除掉GameRes(先把GameResCtrl+R,Ctrl+R成)
节省挂脚本的事,查看和控制顺序。

现在Resources是sealed类,不能用this大法,也不能partical大法。只能塞进一个新建的静态类



/****************************************************

	文件:
	作者:WWS
	日期:2022/10/31 15:25:09
	功能:追要对Unity的Componetn组件的拓展方法(this大法)
        静态类不能有实例构造器。
        静态类不能有任何实例成员。
        静态类不能使用abstract或sealed修饰符。 
        静态类默认继承自System.Object根类,不能显式指定任何其他基类。

 *****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using Object= UnityEngine.Object;




public static class ExtendResources
{

    private static Dictionary<string, Object> resDict;
    private static Dictionary<string, Object[]> resArrayDict;

    /// <summary>
    /// GameStartInstance中调用
    /// </summary>
    public static void Init()
    {
        resDict = new Dictionary<string, Object>();
        resArrayDict = new Dictionary<string, Object[]>();
    }

    /// <summary>
    /// 文件路径
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="resPath"></param>
    /// <returns></returns>
    public static T Get<T>(this Resources resources, string resPath) where T : Object
    {
        if (resDict.ContainsKey(resPath))
        {
            return resDict[resPath] as T;
        }
        else
        {
            var res = Resources.Load(resPath);
            resDict.Add(resPath, res);
            return res as T;
        }
    }


    /// <summary>
    /// 文件夹路径
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="resPath">文件夹下所有</param>
    /// <returns></returns>
    public static T[] GetAll<T>(this Resources resources, string resPath) where T : Object
    {
        Object[] objArray;
        if (resDict.ContainsKey(resPath))
        {
            objArray = resArrayDict[resPath];
        }
        else
        {
            var res = Resources.LoadAll(resPath);
            resArrayDict.Add(resPath, res);
            objArray = res;
        }
        T[] TArray = new T[objArray.Length];
        for (int i = 0; i < TArray.Length; i++)
        {
            TArray[i] = objArray[i] as T;
        }
        return TArray;
    }

}


stars Component.trasform.xxx

    /// <summary>位置。少写个.transform</summary>
    public static Vector3 Position(this Component c) 
    {
        return c.transform.position;
    }

之后可以这样用
在这里插入图片描述

modify 单例GameStartInstance

单例要么 大写开头,要么 _小写开头。觉得是不是Mono,单例都只有一个,所以用常用的_instance,Instance区分公私。

public class GameStartInstance : MonoBehaviour
{
    public static GameStartInstance MonoInstance;

改成

public class GameStartInstance : MonoBehaviour
{
    #region 单例
    private static GameStartInstance _instance;      

    public static GameStartInstance Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameStartInstance();
            }
            return _instance;
        }

        set
        {
            _instance = value;
        }
    }


    #endregion
    private void Awake()
    {

        ExtendResources.Init();

        if (startArchitecture)
        {
            startArchitectureInstance = StartArchitecture.Instance;
            singletonsList = new List<ISingleton>()
            {
                AudioSourceManager.Instance.Init(),
            };
            startArchitectureInstance.SetGameArchitecture(new XYQArchitecture());
            //
            _instance = this;
            GetComponent<UIMgr>().Init();            
            fightLogicController.Init();
            //
            DontDestroyOnLoad(gameObject);
        }


    }

bug 战斗时的Nav不激活报Null

之前

也是防止紫色的FightNav碍眼想隐藏它
在这里插入图片描述
bug unity里面文件夹名字不能包含 . ,编译不通过加不了脚本

之后

Init()被GameStartInstance调用

public class FightLogicController : MonoBehaviour, IController
{
    private bool hasInit;

    public void Init()
    {
        transform.Find("FightBG").gameObject.SetActive(true);
        hasInit = true;
        gameObject.SetActive(false);    


    }

在这里插入图片描述

watch 人物行走

位置

两个Nav区域向SceneSystem中的CharacterAI发起请求,stopping

在这里插入图片描述

watch NotWalkableArea 、SetWillStoppingStateCommand

NotWalkableArea 发起Command,具体是SetWillStoppingStateCommand
SetWillStoppingStateCommand 向 SceneSystem 中的发起请求,控制其中的 CharacterAI.willStopping。所以CharacterAI是控制人物行走的脚本,它挂载人物上。上面也挂有动画切换的脚本CharacterAnimatorController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>检测玩家是否点击到不可走区域</summary>
public class NotWalkableArea : MonoBehaviour,IController
{
    private void OnMouseDown()
    {
        this.SendCommnd<SetWillStoppingStateCommand>();
    }
}
using UnityEngine;
/// <summary>
///  遇“墙”停下来
/// </summary>
public struct SetWillStoppingStateCommand : ICommand
{
    public void Execute(object dataObj)
    {
        this.GetSystem<ISceneSystem>().PlayerNormalAI.willStopping = true;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:场景系统
//***************************************** 
public class SceneSystem : ISceneSystem
{


    #region 字属
	......
    public CharacterAI PlayerNormalAI { private set; get; }
	......
    #endregion
    ......

watch 单击双击

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
//*****************************************
//创建人: Trigger 
//功能说明:非战斗状态下的寻路
//*****************************************        、


/// <summary>非战斗状态下的寻路</summary>
public class CharacterAI : MonoBehaviour
{
	......
    private void GetNotWalkableAreaMovePoint()
    {
        if (willStopping)
        {
            Ray2D ray = new Ray2D(transform.position, targetPos - transform.position);
            RaycastHit2D raycastHit2D = Physics2D.Raycast(ray.origin, ray.direction);
            if (raycastHit2D)
            {
                targetPos = raycastHit2D.point;
                targetPos -= 0.1f * (targetPos - transform.position);
            }
            willStopping = false;
            targetPos.z = transform.position.z;
            meshAgent.SetDestination(targetPos);
        }
    }

    /// <summary>单击人物追过去</summary>
    private void ClickMouse()
    {
        targetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        targetPos.z = transform.position.z;
        meshAgent.SetDestination(targetPos);
        //
        if (Time.time - createEffectTimer >= 0.05f)
        {
            createEffectTimer = Time.time;
            Instantiate(clickEffectGo, targetPos, Quaternion.identity);
        }
    }


    /// <summary>双击人物跟随鼠标</summary>
    private void DoubleClickMouse()
    {
        if (followMouse)//开启开关,人物跟随鼠标移动
        {
            ClickMouse();
        }
        else
        {
            if (Time.time-followMouseTimer>=0.4f)
            {
                //已超出规定时间,重新计时
                followMouseTimer = Time.time;
                clickCount = 0;
            }
            else
            {
                //在时间间隔内
                if (clickCount>1)
                {
                    //双击
                    followMouse = true;
                }
            }
        }
    }
    #endregion
}

watch 人物遇敌 NormalModeMananger

位置

在这里插入图片描述

提取要点

IController
JudgeEnterTheFightCommand

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:游戏管理(判断是否进入战斗)
//***************************************** 
public class NormalModeMananger :MonoBehaviour,IController
{
    private void Update()
    {
        this.SendCommnd<JudgeEnterTheFightCommand>();
    }
}

看类图

NormalModeMananger 也就是IController做了SendCommand,具体的Command是去GetSystem,并且调用其中的方法。
具体的System是SceneSystem:ISystem,具体方法是吗,每隔8秒给个概率(80%)会不会遇敌。
也就是

    /// <summary>时间概率遇敌</summary>
    public void JudgeEnterTheFight()
    {
        if (Time.time - EnterFightTimer >= 8)
        {
            if (Random.Range(0, 5) >= 1)
            {
                //进入战斗
                EnterOrExitFightMode(true);
            }
            else
            {
                //重新计时
                SetEnterFightState(true);
            }
        }
    }

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

总结

IBelongToArchitecture是最低层的接口,基此有各种功能接口。
IController,ICommand,ISystem又会实现所需要的各种功能接口。

遇敌具体代码

public class SceneSystem : ISceneSystem
{
    /// <summary>时间概率遇敌</summary>
    public void JudgeEnterTheFight()
    {
        if (Input.GetKeyDown(KeyCode.E))  //价格快捷键,方便测试
        {
            EnterOrExitFightMode(true); 
        }
        //
        if (Time.time - EnterFightTimer >= 8)
        {
            if (Random.Range(0, 5) >= 1)
            {
                EnterOrExitFightMode(true); //进入战斗
            }
            else
            {
                SetEnterFightState(true);//重新计时
            }
        }
    }
    ......

stars&bug 世界到战斗的转场

modify 手动控制顺序

bug1 一开始是全白的,因为要被引用,不能先SetActive(false)(尝试隐藏方便看,但运行没转场了)。
bug2 UI夏有几个脚本没引用,控制顺序不明显。
所以将一下脚本挂在GameStartInstance 的节点上,并被引用。
4个UI的脚本的Init(),就是将Awake()和Start()合并成Init()。

public class GameStartInstance : MonoBehaviour
{
	......
    #region 生命


    private void Awake()
    {

        ExtendResources.Init();


        if (startArchitecture)
        {
            startArchitectureInstance = StartArchitecture.Instance;
            singletonsList = new List<ISingleton>()
            {
                AudioSourceManager.Instance.Init(),
            };
            startArchitectureInstance.SetGameArchitecture(new XYQArchitecture());
            GetComponent<UIMgr>().Init();
            DontDestroyOnLoad(gameObject);
        }


    }
/****************************************************
    文件:UIMgr.cs
	作者:lenovo
    邮箱: 
    日期:2023/3/27 14:19:2
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 

    public class UIMgr : MonoBehaviour
    {
        #region 字段

        public CameraCapture cameraCapture;
        public FightUIManager fightUIManager;
        public ItemUIManager itemUIManager;
        public SkillUIManager skillUIManager;

    #endregion

    #region 生命

    /// <summary>需要StartArchitecture.Instance;</summary>
    public void Init()
    {
        cameraCapture = transform.FindChildDeep("RawImage_CaptureCamera").GetComponent<CameraCapture>()  ;
        fightUIManager=transform.FindChildDeep("Emp_FightUI").GetComponent<FightUIManager>();
        itemUIManager=transform.FindChildDeep("Emp_ItemUI").GetComponent<ItemUIManager>();
        skillUIManager=transform.FindChildDeep("Emp_SkillUI").GetComponent<SkillUIManager>();

        cameraCapture.Init();
        fightUIManager.Init();
        itemUIManager.Init();
        skillUIManager.Init();
    }


    #endregion 
    }

初始界面,方便看

在这里插入图片描述

在这里插入图片描述

转场效果代码

以下原代码

/****************************************************

	文件:
	作者:WWS
	日期:2023/03/25 13:17:08
	功能:摄像机截屏。作为正常转战斗的转场

*****************************************************/

//*****************************************
//创建人: Trigger 
//功能说明:摄像机截屏
//***************************************** 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class CameraCapture : MonoBehaviour,IController
{
    private RawImage ri;
    private float blurValue;
    void Start()
    {
        ri = GetComponent<RawImage>();
        gameObject.SetActive(false);
        this.RegistEvent<CaptrueCameraAndSetMaterialValueEvent>(CaptrueCameraAndSetMaterialValue);
    }

    void Update()
    {
        if (ri.gameObject.activeSelf)
        {
            blurValue += Time.deltaTime;
            ri.material.SetFloat("_Blur", blurValue);//模糊
            ri.color -= new Color(0, 0, 0, Time.deltaTime);
            if (ri.color.a <= 0)
            {
                ri.gameObject.SetActive(false);
            }
        }
    }

    /// <summary>
    /// 相机截图
    /// </summary>
    /// <param name="camera">截屏相机</param>
    /// <param name="rect">截屏区域</param>
    /// <returns></returns>
    public Texture2D CaptureCamera(Camera camera, Rect rect)
    {
        RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);
        camera.targetTexture = rt;
        camera.Render();
        RenderTexture.active = rt;
        Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height);
        screenShot.ReadPixels(rect, 0, 0);
        screenShot.Apply();
        camera.targetTexture = null;
        RenderTexture.active = null;
        GameObject.Destroy(rt);
        return screenShot;
    }
    /// <summary>
    /// 设置截屏UI所需材质的值
    /// </summary>
    /// <param name="texture"></param>
    public void SetMaterialValue(Texture texture)
    {
        ri.material.SetTexture("_MainTex", texture);
        ri.color = new Color(ri.color.r, ri.color.g, ri.color.b, 1);
        ri.gameObject.SetActive(true);
        blurValue = 0;
    }

    private void CaptrueCameraAndSetMaterialValue(object obj)
    {
        SetMaterialValue(CaptureCamera(Camera.main,new Rect(0,0,800,600)));
    }
}

Scene 战斗-----------------------

bug Invoke不好用的地方

是字符串,导致ctrl+单击,点不到那里
所以一个静态类来汇总(可能会有更好的方法)
在这里插入图片描述

/****************************************************

	文件:InvokeMethod.cs
	作者:WWS
	日期:2023/04/04 20:58:16
	功能:

*****************************************************/

public static class InvokeMethod
{
    public const string PlayIdleAniamtion = "PlayIdleAniamtion";
}

bug 战斗时的道具,技能面板

bug演示

嫌弃碍眼隐藏,导致运行点击时这连个面板加载不了。
也是跟转场一样,在转场中有一起解决
在这里插入图片描述

看到UI下刮油脚本的Panel

转场,FightUI,ItemUI,SkillUI,一共四个
在这里插入图片描述

去相应的脚本SetActive

watch 遇敌战斗SceneSystem(由上面人物遇敌引入)

转入的代码

重点是OpenOrCloseFightCommandPanelEvent

    public void EnterOrExitFightMode(bool enter)
    {
        if (enter)
        {
            this.SendEvent<CaptrueCameraAndSetMaterialValueEvent>();
            AudioSourceManager.Instance.PlayMusic("Fight/FightBG" + Random.Range(1, 4));
        }
        else
        {
            AudioSourceManager.Instance.PlayMusic("DongHaiWan");
        }
        NormalModeGo.SetActive(!enter);
        FightModeGo.SetActive(enter);
        SetEnterFightState(!enter);
        EnterFightTimer = Time.time;
        CanEnterFight = !enter;
        Vector3 pos = Camera.main.transform.position;
        pos.z = 0;
        FightModeGo.transform.position = pos;
        this.SendEvent<OpenOrCloseFightCommandPanelEvent>(enter);   
    }

类图

由 UISystem 的 FightUI 的 OpenOrCloseFightCommandPanelEvent转过来
在这里插入图片描述

查它的引用,有FightSystem

modify CharacterFightAI代码太多,改用partial

原来的CharacterFightAI被拆分了
这几个都是 见名知义 的,就不写summary了
在这里插入图片描述

bug ExtendResources

加载一个audio的路径,想把该方法写进同名(功能性趋同)但不同命名空间(一根据实际工程,一块复用性高)的ExtendResources,办不到。
同名,就要用partial,partial就要同一个命名空间。

暂时在实际工程下新建一个类来存储改方法。

watch 这3块

在这里插入图片描述

watch FightSystem的目录

Cammand、Behaviour、总控脚本和拆分出来的CharacterFightAI(就是CharacterFightAI.xxx)

在这里插入图片描述

敌人、角色的生成

在FightSystem找到了实例脚本CreateCharactersCommand ,倒查引用过来的
并且结果挂在FightNavMesh上
CreateCharactersCommand
CreateCharactersCommand
在这里插入图片描述

CreateCharactersCommand

using UnityEngine;
using System.Linq;
/// <summary>
/// 创建人:Trigger<para/> 
/// 命令名称:在战斗开始后生成战斗敌人与玩家<para/> 
/// 参数:
/// </summary>
public struct CreateCharactersCommand : ICommand
{
    public void Execute(object dataObj)
    {
        //玩家
        IFightSystem ifs = this.GetSystem<IFightSystem>();
        CharacterFightAI playerAI= InstantiateCharacter(ifs.CharacterPrefab,ifs.PlayerInitPosTrans);
        ifs.PlayerAI = playerAI;
        playerAI.isPlayer = true;
        ifs.CurrentActAIsList.Add(playerAI);
        InitCharacterData(playerAI);


        //敌人
        int num = Random.Range(1,10);
        for (int i = 0; i < num; i++)
        {
            CharacterFightAI enemyAI = InstantiateCharacter(ifs.CharacterPrefab, ifs.EnemyInitPosTrans[i]); 
            ifs.EnemyAIList.Add(enemyAI);
            ifs.CurrentActAIsList.Add(enemyAI);
            enemyAI.isPlayer = false;
            InitCharacterData(enemyAI);
        }
        ifs.CurrentActAIsList = ifs.CurrentActAIsList.OrderByDescending(p => p.actRate).ToList();
    }



    #region 辅助


    private void InitCharacterData(CharacterFightAI cfa)
    {
        IFightSystem ifs = this.GetSystem<IFightSystem>();
        if (cfa.isPlayer)
        {
            cfa.actRate = 8;
            cfa.SetDieMovePath(ifs.PlayerDieStartMovePath,ifs.PlayerDieEndMovePath);
            cfa.tag = "Player";
            cfa.characterInfo = this.GetModel<ICharacterDataModel>().GetCharacterInfo(2);           
        }
        else
        {
            cfa.actRate = Random.Range(1,10);
            cfa.SetDieMovePath(ifs.EnemyDieStartMovePath, ifs.EnemyDieEndMovePath);
            cfa.tag = "Enemy";
            cfa.characterInfo = this.GetModel<ICharacterDataModel>().GetCharacterInfo(Random.Range(1,6));
        }
        cfa.name = cfa.characterInfo.pathName;
    }


   /// <summary>
   /// 实例角色
   /// </summary>
   /// <param name="fightAI"></param>
   /// <param name="parent">父节点</param>
   /// <param name="localPos">局部坐标</param>
   /// <returns></returns>
    CharacterFightAI InstantiateCharacter(CharacterFightAI fightAI,Transform parent)
    {
        CharacterFightAI ai = GameObject.Instantiate(fightAI, parent);
        ai.transform.localPosition = Vector3.zero;

        return ai;
    }
    #endregion

}

FightLogicController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
//*****************************************
//创建人: Trigger 
//功能说明:游戏战斗逻辑控制
//***************************************** 

/// <summary>游戏战斗逻辑控制</summary>
public class FightLogicController : MonoBehaviour, IController
{
    private bool hasInit;

    public void Init()
    {
        transform.Find("FightBG").gameObject.SetActive(true);
        hasInit = true;
        gameObject.SetActive(false);    
    }

    private void OnEnable()
    {
        if (hasInit)
        {
            this.SendCommnd<CreateCharactersCommand>();
            this.SendCommnd<ResetFightLogicStateCommand>();
        }
    }

GameStartInstance

using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:游戏入口实例,初始化并管理所有的管理者,提供mono方法
//***************************************** 

/// <summary>游戏入口实例,初始化并管理所有的管理者,提供mono方法</summary>
public class GameStartInstance : MonoBehaviour
{

    private List<ISingleton> singletonsList;
    //
    private StartArchitecture startArchitectureInstance;
    public bool startArchitecture;
    public FightLogicController fightLogicController; //拖拽赋值




    #region 单例
    private static GameStartInstance _instance;      

    public static GameStartInstance Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameStartInstance();
            }
            return _instance;
        }

        set
        {
            _instance = value;
        }
    }


    #endregion


    #region 生命


    private void Awake()
    {

        ExtendResources.Init();


        if (startArchitecture)
        {
            startArchitectureInstance = StartArchitecture.Instance;
            singletonsList = new List<ISingleton>()
            {
                AudioSourceManager.Instance.Init(),
            };
            startArchitectureInstance.SetGameArchitecture(new XYQArchitecture());
            //
            _instance = this;
            GetComponent<UIMgr>().Init();            
            fightLogicController.Init();
            //
            DontDestroyOnLoad(gameObject);
        }


    }

watch CharacterFightAI(FightSystem的Behaviour的挂载点)

CharacterFightAIBehaviour没被CharacterFightAI引用,因为它是该文件下的脚本的父类

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

watch 战斗时角色的UI面板

也就是把战斗系统的UI(FightUIManager)放UI系统。
入口脚本GameStartInstance调用UIMgr(我加进来的一个总的管理),里面就有要找的FightUIManager

/****************************************************
    文件:UIMgr.cs
	作者:lenovo
    邮箱: 
    日期:2023/3/27 14:19:2
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 

public class UIMgr : MonoBehaviour
{
    #region 字段

    CameraCapture cameraCapture;
    FightUIManager fightUIManager;
    ItemUIManager itemUIManager;
    SkillUIManager skillUIManager;

    #endregion

    #region 生命

    /// <summary>需要StartArchitecture.Instance;</summary>
    public void Init()
    {
        cameraCapture = transform.FindChildDeep("RawImage_CaptureCamera").GetComponent<CameraCapture>()  ;
        fightUIManager=transform.FindChildDeep("Emp_FightUI").GetComponent<FightUIManager>();
        itemUIManager=transform.FindChildDeep("Emp_ItemUI").GetComponent<ItemUIManager>();
        skillUIManager=transform.FindChildDeep("Emp_SkillUI").GetComponent<SkillUIManager>();

        cameraCapture.Init();
        fightUIManager.Init();
        itemUIManager.Init();
        skillUIManager.Init();
    }


    #endregion 
}

在这里插入图片描述

watch 人物放技能、防御、使用物品

如下,一个面板,三个按钮
在这里插入图片描述

    private Button defendCommandBtn;
    private Button skillCommandBtn;
    private Button useItemCommandBtn;
    private GameObject fightCommandPanelGo;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


//*****************************************
//创建人: Trigger 
//功能说明:战斗UI管理
//***************************************** 

/// <summary>战斗UI管理</summary>
public class FightUIManager : MonoBehaviour,IController
{
    private Button defendCommandBtn;
    private Button skillCommandBtn;
    private Button useItemCommandBtn;
    private GameObject fightCommandPanelGo;

    public void Init()
    {
        gameObject.SetActive(true);
        fightCommandPanelGo =   transform.FindChildDeep( "Emp_FightCommand").gameObject;
        fightCommandPanelGo.SetActive(true);
        skillCommandBtn =       transform.FindChildDeep( "Btn_Skill").GetComponent<Button>();
        useItemCommandBtn =     transform.FindChildDeep( "Btn_UseItem").GetComponent<Button>();
        defendCommandBtn =      transform.FindChildDeep( "Btn_Defend").GetComponent<Button>();

        //
        skillCommandBtn.onClick.AddListener(ClickSkillBtn);
        useItemCommandBtn.onClick.AddListener(ClickUseItemBtn);
        defendCommandBtn.onClick.AddListener(ClickDefendBtn);

        //
        this.RegistEvent<OpenOrCloseFightCommandPanelEvent>(OpenOrCloseFightCommandPanel);
        fightCommandPanelGo.SetActive(false);
    }


    #region 辅助


    /// <summary>
    /// 使用技能指令按钮事件
    /// </summary>
    private void ClickSkillBtn()
    {
        this.SendEvent<OpenOrCloseSkillPanelEvent>(true);
    }


    /// <summary>
    /// 使用物品指令按钮事件
    /// </summary>
    public void ClickUseItemBtn()
    {
        this.SendCommnd<CloseAllFightUIPanelCommand>();
        this.SendEvent<OpenOrCloseFightBagPanelEvent>(true);
    }


    /// <summary>
    /// 防御指令按钮事件
    /// </summary>
    public void ClickDefendBtn()
    {
        this.SendCommnd<CloseAllFightUIPanelCommand>();
        this.SendCommnd<SetCharacterActCodeCommand>
        (
            new SetCharacterActCodeCommandParams()
            {
                actCode=ActCode.DEFEND
            }
        );
    }


    /// <summary>
    /// 关闭或开启战斗指令面板
    /// </summary>
    /// <param name="obj"></param>
    private void OpenOrCloseFightCommandPanel(object obj)
    {
        fightCommandPanelGo.SetActive((bool)obj);
    }


    #endregion

}


stars FindButtonDeep

为了达到以下效果,所以有FindButtonDeep

transform.FindButtonDeep( "Btn_SkillHengSaoQianJun");

SkillUIManager

/// <summary>技能UI管理</summary>
public class SkillUIManager : MonoBehaviour,IController
{
    private Button[] skillBtns;

    public void Init()
    {
        gameObject.SetActive(true);
        skillBtns = new Button[4];
        skillBtns[0] = transform.FindButtonDeep( "Btn_SkillHengSaoQianJun");
        skillBtns[1] = transform.FindButtonDeep( "Btn_SkillJiJiWaiWai");
        skillBtns[2] = transform.FindButtonDeep( "Btn_SkillFanJianZhiJi");
        skillBtns[3] = transform.FindButtonDeep( "Btn_SkillHuaYu");
        ......

ExtendComponent(各种Component的拓展方法)

    /// <summary>
    /// 深度查找子对象transform引用
    /// </summary>
    /// <param name="root">父对象</param>
    /// <param name="childName">具体查找的子对象名称</param>
    /// <returns></returns>
    public static Button FindButtonDeep(this Transform root, string childName)
    {
        Transform result = null;
        result = root.Find(childName);
        if (!result)
        {
            foreach (Transform item in root)
            {
                result = FindChildDeep(item, childName);
                if (result != null)
                {
                    return result.GetComponent<Button>();
                }
            }
        }
        return result.GetComponent<Button>();
    }

    /// <summary>
    /// 深度查找子对象transform引用
    /// </summary>
    /// <param name="root">父对象</param>
    /// <param name="childName">具体查找的子对象名称</param>
    /// <returns></returns>
    public static Transform FindChildDeep(this Transform root, string childName)
    {
        Transform result = null;
        result = root.Find(childName);
        if (!result)
        {
            foreach (Transform item in root)
            {
                result = FindChildDeep(item, childName);
                if (result != null)
                {
                    return result;
                }
            }
        }
        return result;
    }

stars SpriteRenderer

为了这种效果

public class CharacterMouseDetection : CharacterFightAIBehaviour
{  
	......
    //spriteRenderer.material.SetColor("_Color", color);
    spriteRenderer.SetColor( color);
public static class ExtendComponent
{
	......
    public static void SetColor(this SpriteRenderer spriteRenderer, Color color)
    {
        spriteRenderer.material.SetColor("_Color", color);
    }

watch 人物平A、技能攻击

modify CharacterMouseDetection

进入可以知道直接点击敌人,就是平A。
所以找以下的单击类CharacterMouseDetection 。
重点是SetCharacterActCodeCommandParams(struct)、SetPlayerTargetAICommand。

using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.EventSystems;
//*****************************************
//创建人: Trigger 
//功能说明:人物鼠标检测
//***************************************** 
/// <summary>人物鼠标检测(Detection侦查察觉检测)</summary>
public class CharacterMouseDetection : CharacterFightAIBehaviour
{  
    private Color initColor; 

    protected override void Awake()
    {
        base.Awake();
        initColor = spriteRenderer.color;
    }


    #region 系统


    private void OnMouseDown()
    {
        if (IfCanClick())
        {
            this.SendCommnd<SetTargetAICommand>(fromAI); //当前点击的对象fromAI是Player的目标
            this.SendEvent<SetCurrentCursorStateEvent>(CursorIconState.NORMAL);//鼠标样式
            //
            if (this.GetSystem<ISkillSystem>().UsingSkill)
            {
                Attack( SkillAttackPara() );
            }
            else
            {
                Attack( NormalAttackPara() );
            }
        }
    }


    private void OnMouseOver()
    {
        Color color = new Color
        (
            initColor.r * Mathf.Pow(2, 1),//n次幂 
            initColor.g * Mathf.Pow(2, 1), 
            initColor.b * Mathf.Pow(2, 1)
        );

        spriteRenderer.SetColor( color);
        if (IfCanClick())
        {
            if (this.GetSystem<ISkillSystem>().UsingSkill)
            {
                this.SendEvent<SetCurrentCursorStateEvent>(CursorIconState.SKILL);
            }
            else
            {
                this.SendEvent<SetCurrentCursorStateEvent>(CursorIconState.ATTACK);
            }
        }
        else
        {
            if (!EventSystem.current.IsPointerOverGameObject())
            {
                this.SendEvent<SetCurrentCursorStateEvent>(CursorIconState.FORBID);
            }
        }
    }


    private void OnMouseExit()
    {
        spriteRenderer.SetColor(initColor);
        this.SendEvent<SetCurrentCursorStateEvent>(CursorIconState.NORMAL);
    }
    #endregion




    #region 辅助




    /// <summary>
    /// 当前人物是否可以点击
    /// </summary>
    /// <returns></returns>
    public bool IfCanClick()
    {
        bool isRightTarget = false;
        ISkillSystem iks = this.GetSystem<ISkillSystem>();
        if (iks.CurrentSkillID == 5)
        {
            isRightTarget = gameObject.CompareTag("Player");
        }
        else
        {
            isRightTarget = gameObject.CompareTag("Enemy");

        }
        return isRightTarget 
            && !fightSystem.IsPerformingLogic 
            && !EventSystem.current.IsPointerOverGameObject();
    }




    void Attack(SetCharacterActCodeCommandParams para)
    {
        this.SendCommnd<SetCharacterActCodeCommand>( para );
        this.SendCommnd<SetPlayerTargetAICommand>(fromAI);
        fightSystem.PlayerAI.toAI = null;
    }

    SetCharacterActCodeCommandParams NormalAttackPara()
    {
        Vector3 tarPos = fightSystem.PlayerAI.GetCurrentAITargetPos();

        return new SetCharacterActCodeCommandParams()
        {
            actCode = ActCode.ATTACK,
            actObj = new ActObj()
            {
                attackPos = tarPos
            }
        };
    }

    SetCharacterActCodeCommandParams SkillAttackPara()
    {          
        int skillID = this.GetSystem<ISkillSystem>().CurrentSkillID;
        Vector3 tarPos = fightSystem.PlayerAI.GetCurrentAITargetPos();
        return new SetCharacterActCodeCommandParams()
        {
            actCode = ActCode.SKILL,
            actObj = new ActObj()
            {
                attackPos = tarPos,
                skillInfo = this.GetModel<ISkillDataModel>().GetSkillInfo(skillID)
            }
        };
    }

    #endregion  



}

SetCharacterActCodeCommandParams

/// <summary>
/// 设置玩家行为码的命令参数
/// </summary>
public struct SetCharacterActCodeCommandParams
{
    /// <summary>动作码</summary>
    public ActCode actCode;
    /// <summary>行动信息参数</summary>
    public ActObj actObj;
}

SetPlayerTargetAICommand

SetPlayerTargetAICommand 中对 PlayerTargetAI 进行赋值,那就是有相关的判空引用。
只有一个引用 fightSystem.PlayerAI.targetAI ,找targetAI 。

using UnityEngine;
/// <summary>
/// 创建人:Trigger 
/// 命令名称:设置玩家目标AI
/// 参数:CharacterFightAI
/// </summary>
public struct SetPlayerTargetAICommand : ICommand
{
    public void Execute(object dataObj)
    {
        this.GetSystem<IFightSystem>().PlayerTargetAI = (CharacterFightAI)dataObj;
    }
}

AttackFightBehaviour

targetAI 22处引用,有关攻击只有 在AttackFightBehaviour(里面只有进行音效和动画处理)

    /// <summary>
    /// 攻击行为
    /// </summary>
    public void AttackBehaviour()
    {
        AudioSourceManager.Instance.PlayCharacterSound("Attack",gameObject.name);
        cac.PlayAttackAnimation();
    }

在这里插入图片描述

watch 人物混乱

从 CharacterFightAI 拆分出来的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
//*****************************************
//创建人: Trigger 
//功能说明:人物战斗AI
//***************************************** 

/// <summary>人物战斗AI</summary>
public partial class CharacterFightAI : MonoBehaviour,IController
{


    #region 生命


    private void OnDestroy()
    {
        if (chaosTween != null)
        {
            chaosTween.Pause();
            chaosTween.Kill();
        }
    }
    #endregion  


    #region 混乱


    /// <summary>
    /// 混乱移动
    /// </summary>
    public void DoChaosMoveTween()
    {
        if (chaosTween != null)
        {
            return;
        }
        DoChaosMove();
    }



    private void DoChaosMove()
    {
        chaosTween = animatorTrans
            .DoMove(GetChaosMoveTarget(), 0.05f) //左摇
            .SetOnComplete
            (
                () =>
                {
                    chaosTween = animatorTrans
                    .DoMove(GetRendererInitPos(), 0.05f) //右晃
                    .SetOnComplete(DoChaosMove);
                }
            );
    }


    private Vector3 GetChaosMoveTarget()
    {
        Vector3 startPos = GetRendererInitPos();
        Vector2 randomPos = Random.insideUnitCircle * 0.1f;
        return startPos + new Vector3(randomPos.x, randomPos.y, startPos.z);
    }


    private Vector3 GetRendererInitPos()
    {
        return transform.TransformPoint( new Vector3(0, 0.3f, 0) );
    }
    #endregion
}

watch 人物回复、扣血

从 CharacterFightAI.UseSkillOrItem.cs 得到使用物品和技能来回复

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:人物战斗状态数据显示
//***************************************** 

/// <summary>人物战斗状态数据显示</summary>
public class CharacterFightValueState : CharacterFightAIBehaviour
{


    private CharacterCanvas characterCanvas;



    #region 生命


    void Start()
    {
        characterCanvas = GetComponentInChildren<CharacterCanvas>();
        if (fromAI.isPlayer)
        {
            fromAI.HP = PlayerData().CurrentMaxHP.Value;
            fromAI.currentHP = PlayerData().CurrentHP.Value;
            fromAI.MP = PlayerData().MaxMP.Value;
            fromAI.currentMP = PlayerData().CurrentMP.Value;
        }
        else
        {
            characterCanvas.HideSlider();
        }

        string characterName = this.GetModel<ICharacterDataModel>().GetCharacterInfo(fromAI.characterInfo.ID).name;
        characterCanvas.SetCharacterName( characterName );
        fromAI.currentHP = fromAI.HP;
    }
    #endregion




    #region 辅助


    /// <summary>
    /// 显示跟血量变化相关的内容
    /// </summary>
    public void ShowHPValueChange(int changeValue)
    {
        NumCanvas nc = Instantiate(
            ExtendResources.Get<GameObject>("Prefabs/CharacterNumCanvas"),
            spriteRenderer.Position(),
            Quaternion.identity
        ).GetComponent<NumCanvas>();
        nc.ShowNum(changeValue);
        //
        fromAI.currentHP += changeValue;
        if (fromAI.currentHP >= fromAI.HP)
        {
            fromAI.currentHP = fromAI.HP;
        }
        
        if (fromAI.isPlayer)
        {
            characterCanvas.SetHPSliderValue((float)fromAI.currentHP / fromAI.HP);
            this.SendCommnd<ChangePlayerHPCommand>(changeValue);
        }
    }

    /// <summary>
    /// 显示跟蓝耗变化相关的内容
    /// </summary>
    public void ShowMPValueChange(int changeValue)
    {
        fromAI.currentMP += changeValue;
        if (fromAI.currentMP >= fromAI.MP)
        {
            fromAI.currentMP = fromAI.MP;
        }
        if (fromAI.isPlayer)
        {
            this.SendCommnd<ChangePlayerMPCommand>(changeValue);
        }
    }

    IPlayerDataModel PlayerData()
    {
        return this.GetModel<IPlayerDataModel>();
    }
    #endregion


}

watch 人物闪避、防御

CharacterFightAI 中拆分的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
//*****************************************
//创建人: Trigger 
//功能说明:人物战斗AI
//***************************************** 

/// <summary>人物战斗AI</summary>
public partial class CharacterFightAI : MonoBehaviour,IController
{


    #region 闪避与防御
    /// <summary>
    /// 闪避行为
    /// </summary>
    public void DodgeBehaviour()
    {
        defendAndDodgeFightBehaviour.DodgeBehaviour();
    }


    /// <summary>
    /// 防御行为
    /// </summary>
    public void DefendBehaviour()
    {
        defendAndDodgeFightBehaviour.DefendBehaviour();
    }


    /// <summary>
    /// 移动到防御位置并返回
    /// </summary>
    /// <param name="animationTime">动画时间</param>
    /// <param name="callBack">需要在动画完成后进行的额外回调</param>
    public void ToDenfendPos(float animationTime, UnityAction callBack = null)
    {
        defendAndDodgeFightBehaviour.ToDenfendPos(animationTime, callBack);
    }
    #endregion
}
using UnityEngine;
/// <summary>
/// 创建人:Trigger 
/// 命令名称:人物死亡命令
/// 参数:
/// </summary>
public struct CharacterDieCommand : ICommand
{
    public void Execute(object dataObj)
    {
        IFightSystem fightSystem = this.GetSystem<IFightSystem>();
        AudioSourceManager.Instance.PlayCharacterSound("FlyAway");
        Time.timeScale = 1;
        fightSystem.DieCount++;
        fightSystem.EnterCurrentRound = false;
    }
}

watch 人物受击、死亡

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:人物受击与死亡行为
//***************************************** 

/// <summary>人物受击与死亡行为</summary>
public class HitAndDieFightBehaviour : CharacterFightAIBehaviour
{


    /// <summary> 受击行为 </summary>
    public void HitBehaviour(bool canDodge = true, bool canDefend = true)
    {
        meshAgent.isStopped = true;
        
        if (Random.Range(0, 3) >= 2 && canDodge)//闪避
        {
            fromAI.DodgeBehaviour();//闪避成功
            return;
        }
        
        if (fromAI.actCode == ActCode.DEFEND && canDefend)//防御
        {
            fromAI.DefendBehaviour();
            return;
        }
        //受击
        int random = Random.Range(60,120);
        fromAI.ShowHPValueChange(-random);
        fromAI.SetCurrentLookDir();
        cac.PlayHitAnimation();
        this.SendCommnd<CreateHitEffectCommand>();
        JudgeIfDie();
    }


    /// <summary> 判断是否死亡 </summary>
    public void JudgeIfDie()
    {
        if (fromAI.currentHP <= 0)
        {
            //死亡
            cac.PlayDieAnimation();
            Time.timeScale = 0.3f;
            transform.DoMove(fromAI.GetTargetPosTrans(fromAI.currentLookDir).position, 0.2f)
               .SetOnComplete(
                () =>
                {
                    meshAgent.isStopped = false;
                    meshAgent.speed = 15;
                }
            );
        }
        else
        {       
            fromAI.ToDenfendPos(0.2f); //受击
        }
    }


    /// <summary> 死亡行为 </summary>
    public void DieBehaviour()
    {
        this.SendCommnd<CharacterDieCommand>();
        fromAI.characterState = CharacterState.DEAD;
        fromAI.SetMovingState(false);        
        //主要目的是为了放横扫一类的技能没有在原始位置,要回去
        fromAI.ResetState();       
    }
}

结束当前回合

watch 链接起来FightSystem的所有Command

watch 敌人动作

CharacterFightAI
设置了ActCode、 ActObj,传给 CharacterFightAI

using UnityEngine;
/// <summary>
/// 创建人:Trigger 
/// 命令名称:随机敌人AI的行动命令
/// 参数:
/// </summary>
public struct RandomEnemyActCommand : ICommand
{
    public void Execute(object obj)
    {
        IFightSystem sys = this.GetSystem<IFightSystem>();
        CharacterFightAI fromAI = sys.CurrentAI;
        fromAI.toAI = sys.PlayerAI;
        Vector3 tarPos= fromAI.GetCurrentAITargetPos(); 
        //
        ActCode ac = (ActCode)Random.Range(0,4);
        ActObj ao = new ActObj();
        //
        switch (ac)
        {
            case ActCode.ATTACK:
                ao.attackPos = tarPos;
                break;
            case ActCode.DEFEND:
                break;
            case ActCode.SKILL:
                { 
                    int skillID = Random.Range(1, 6);
                    switch (skillID)
                    {
                        case 1:
                            ao.attackPos = tarPos;
                            break;
                        case 2:
                        case 4:
                            skillID = 3;
                            break;
                        case 5:
                            this.SendCommnd<GetRandomCharacterCommand>();
                            break;
                        default: break;
                    }               
                    ao.skillInfo = this.GetModel<ISkillDataModel>().GetSkillInfo(skillID);                
                }
                break;
            case ActCode.USEITEM:
                ao.itemID = 0;
                break;
            default: break;
        }
        fromAI.SetActCodeAndObjValue(ac, ao);
    }
}

bug 修改后角色攻击后不返回(没学过)

bug在伤害数值和伤害特效出现后发生的原地动画不返回
AttackFightBehaviour.AttackTarget()打点
0会返回,但是一直是1
在这里插入图片描述

01 bug NumCanvas

伤害数值的脚本
忘记忘记怎么改的了
在这里插入图片描述
在这里插入图片描述

02 watch 尝试找移动的代码

在这里插入图片描述

03 watch 不报错也这样

04 watch DefendAndDodgeFightBehaviour.ToDenfendPos()

是被攻击者的动作
DefendAndDodgeFightBehaviour.ToDenfendPos()

05 watch 报错时将角色的State手动改为Dead会退出战斗,其他状态没有变化

06 bug发生和原来的,有一个脚本 在场景中引用的 情况不同

01 查脚本AttackStateBehaviour引用

上面是我改的,下面是原版的
该脚本不继承于Component,不能加到节点上

在这里插入图片描述

02 AttackStateBehaviour.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttackStateBehaviour : UnityEngine.StateMachineBehaviour
{

    private CharacterFightAI fightAI;

    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        if (!fightAI)
        {
            fightAI = animator.transform.GetComponentInParent<CharacterFightAI>();
        }
    }

    // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    //override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    
    //}

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        fightAI.SetSkillMoveAction();
    }

    // OnStateMove is called right after Animator.OnAnimatorMove()
    //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    // Implement code that processes and affects root motion
    //}

    // OnStateIK is called right after Animator.OnAnimatorIK()
    //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    //{
    //    // Implement code that sets up animation IK (inverse kinematics)
    //}
}

03 原版的UnityEngine.StateMachineBehaviour与Animator

发现它是加载在动画器上的状态上
在这里插入图片描述

04 我的UnityEngine.StateMachineBehaviour与Animator

可以看到报错了
在这里插入图片描述

modify 拆分CharacterAnimatorController

public partial class CharacterFightAI : MonoBehaviour,IController
{
	......
    public CharacterState characterState;
/// <summary>角色状态</summary>
public enum CharacterState
{
    NORMAL,
    /// <summary>休息状态</summary>
    REST,
    /// <summary>混乱状态</summary>
    CHAOS,
    DEAD
}

找到ActCode.ATTACK;

fromAI.toAI.HitBehaviour();条件改清楚

CharacterAnimatorController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:控制人物动画切换
//***************************************** 

/// <summary>控制人物动画切换</summary>
public partial class CharacterAnimatorController : MonoBehaviour
{
    private Animator animator;
    public bool isMoving;
    private CharacterFightAI fightAI;



    private void Awake()
    {
        animator = GetComponentInChildren<Animator>();
        fightAI = GetComponentInParent<CharacterFightAI>();        
    }


    private void Start()
    {
        if (fightAI)
        {
            string path = "Character/" + fightAI.characterInfo.pathName + "/Fight/FightController";
            animator.runtimeAnimatorController = ExtendResources.Get<RuntimeAnimatorController>(path);
        } 
    }
}

CharacterAnimatorController.AnimationEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:控制人物动画切换
//***************************************** 

/// <summary>控制人物动画切换</summary>
public partial class CharacterAnimatorController : MonoBehaviour
{

    /// <summary>挂在Timeline时间轴上的帧的时间</summary>
    private void AttackAnimationEvent()
    {
        fightAI.AttackTarget();
    }


    /// <summary>挂在Timeline时间轴上的帧的时间</summary>
    private void UseSkillOrItemAnimationEvent()
    {
        fightAI.UseSkillOrItemAction();
    }
}

CharacterAnimatorController.PlayAnimation

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//*****************************************
//创建人: Trigger 
//功能说明:控制人物动画切换
//***************************************** 

/// <summary>控制人物动画切换</summary>
public partial class CharacterAnimatorController : MonoBehaviour
{
   /// <summary>
   /// 播放位移动画
   /// </summary>
   /// <param name="curPos">当前位置</param>
   /// <param name="navPos">导航点</param>
   /// <param name="tarPos">最终目标点</param>
    public void PlayLocomotionAnimation(Vector3 curPos,Vector3 navPos,Vector3 tarPos)
    {
        Vector3 lookDir = navPos-curPos;
        if (lookDir.magnitude<=0.0001f)
        {
            lookDir = tarPos - curPos;
        }


        if (Vector3.Distance(curPos,tarPos)>0.3f)
        {
            animator.SetFloat("LookX", lookDir.normalized.x);
            animator.SetFloat("LookY", lookDir.normalized.y);
            animator.SetBool("MoveState",true);
            isMoving = true;
        }
        else if(Vector3.Distance(curPos, tarPos) < 0.15f)
        {
            animator.SetBool("MoveState", false);
            isMoving = false;
        }
    }



    public void PlayHitAnimation()
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetTrigger("Hit");
    }

    public void PlayDieAnimation()
    {
        animator.SetBool("Die",true);
    }

    public void PlayMoveAnimation(float moveValue)
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetInteger("MoveValue",1);
    }

    public void PlayIdleAnimation()
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetInteger("MoveValue", 0);
    }

    public void PlayAttackAnimation()
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetTrigger("Attack");
    }

    public void PlaySkillAnimation()
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetTrigger("Skill");
    }

    public void PlayDefendAnimation()
    {
        SetLookDir(fightAI.currentLookDir);
        animator.SetTrigger("Defend");
    }

    public void SetLookDir(Vector2 lookDir)
    {
        animator.SetFloat("LookDirX", lookDir.x);
        animator.SetFloat("LookDirY", lookDir.y);
    }
}

bug CharacterFightAI报空

拆分CharacterAnimatorController时脚本丢失(拖拽复制的缺点)
之前以下丢失了
在这里插入图片描述

bug 进入战斗实例失败

修改代码后发生,SceneSystem.Init()报空
实际主角是 骨精灵 ,这里是 侠客, 脚本缺少CharacterAnimationController,脚本没激活
总结就是没初始化成功,后面的敌人也直接没有实例
。。。。
原因是CharacterAnimationController,预制体没有应该用AddComopnent而不是GetComopnent
GetOrAddComponent是自己的库
。。。。
CharacterFightAI

    private void Awake()
    {
        animatorTrans = GetComponentInChildren<Animator>().transform;
        cac = gameObject.GetOrAddComponent<CharacterAnimatorController>();
        characterCanvas = GetComponentInChildren<CharacterCanvas>();
        cac.Init();
        characterCanvas.Init();

在这里插入图片描述

stars GetOrAddComponent

    /// <summary>
    /// 获取或增加组件。
    /// </summary>
    /// <typeparam name="T">要获取或增加的组件。</typeparam>
    /// <param name="gameObject">目标对象。</param>
    /// <returns>获取或增加的组件。</returns>
    public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
    {
        T component = gameObject.GetComponent<T>();
        if (component == null)
        {
            component = gameObject.AddComponent<T>();
        }

        return component;
    }

bug Exception: 不存在ID为0的人物

01 ID是fromAI.characterInfo.ID

Init()在CharacterFightAI中调用
其中fromAI.characterInfo数据为空

/// <summary>人物战斗状态数据显示</summary>
public class CharacterFightValueState : CharacterFightAIBehaviour
{
  public override  void Init()
    {
        base.Init();
        characterCanvas = GetComponentInChildren<CharacterCanvas>();
        if (fromAI.isPlayer)
        {
            fromAI.HP = PlayerData().CurrentMaxHP.Value;
            fromAI.currentHP = PlayerData().CurrentHP.Value;
            fromAI.MP = PlayerData().MaxMP.Value;
            fromAI.currentMP = PlayerData().CurrentMP.Value;
        }
        else
        {
            characterCanvas.HideSlider();
        }

        string characterName = this.GetModel<ICharacterDataModel>().GetCharacterInfo(fromAI.characterInfo.ID).name;
        characterCanvas.SetCharacterName( characterName );
        fromAI.currentHP = fromAI.HP;
    }

02 fromAI在基类 CharacterFightAIBehaviour 中

03 CharacterFightAI.characterInfo

CharacterFightAI.characterInfo

/// <summary>人物战斗AI</summary>
public partial class CharacterFightAI : MonoBehaviour,IController
{
    #region 字属
    ......
    public CharacterInfo characterInfo;

04 characterInfo在CharacterDataModel 中初始化

为0,就是没赋值过,默认为0
public class CharacterDataModel : ICharacterDataModel
{
private Dictionary<int, CharacterInfo> characterInfoDict;
private Dictionary<string, CharacterInfo> characterInfoStrDict;

05 bug 应为是接口,报错的位置没挑到实际的位置

新建 CharacterInfo GetCharacterInfo(CharacterInfo characterInfo )来找准位置

/// <summary>人物战斗状态数据显示</summary>
public class CharacterFightValueState : CharacterFightAIBehaviour
{
  public override  void Init()
    {
        base.Init();
        characterCanvas = GetComponentInChildren<CharacterCanvas>();
        if (fromAI.isPlayer)
        {
            fromAI.HP = PlayerData().CurrentMaxHP.Value;
            fromAI.currentHP = PlayerData().CurrentHP.Value;
            fromAI.MP = PlayerData().MaxMP.Value;
            fromAI.currentMP = PlayerData().CurrentMP.Value;
        }
        else
        {
            characterCanvas.HideSlider();
        }
        fromAI.currentHP = fromAI.HP;
        //
        string characterName = GetCharacterInfo(fromAI.characterInfo).name;
        characterCanvas.SetCharacterName( characterName );

    }
    #endregion

    #region 辅助

    /// <summary>
    /// 方便找bug,不然报错在接口,不清晰
    /// </summary>
    CharacterInfo GetCharacterInfo(CharacterInfo characterInfo )
    {
        if (characterInfo.ID >= 0)
        {
            throw new System.Exception("ID异常:"+characterInfo.ID);
        }
        return this.GetModel<ICharacterDataModel>().GetCharacterInfo(characterInfo.ID);
    }

06 bug 手动控制顺序

01
实例角色,就会调用CharacterFightAI
找CharacterInfo 的逻辑也在 CharacterFightAI 上,也会被调用。
02
而CreateCharacterCommand的命令中,赋值 CharacterInfo 在 实例角色 之后。导致找 CharacterInfo 的逻辑发生时,CharacterInfo 还没赋值,所以报错。
03
所以改成 Init(),手动控制顺序。Init 就是 原来的 Awake(),Start()等
cfa.characterInfo = this.GetModel().GetCharacterInfo(Random.Range(1, 6));
}

cfa.Init();

using UnityEngine;
using System.Linq;

/// <summary>
/// 命令名称:在战斗开始后生成战斗敌人与玩家
/// </summary>

public struct CreateCharactersCommand : ICommand
{
    public void Execute(object dataObj)
    {
        //玩家
        IFightSystem ifs = this.GetSystem<IFightSystem>();
        ifs.PlayerAI = GameObject.Instantiate(ifs.CharacterPrefab, ifs.PlayerInitPosTrans);
        ifs.PlayerAI.transform.localPosition = Vector3.zero;
        ifs.PlayerAI.isPlayer = true;
        ifs.CurrentActAIsList.Add(ifs.PlayerAI);
        InitCharacterData(ifs.PlayerAI);
        //敌人
        int num = Random.Range(1, 10);
        for (int i = 0; i < num; i++)
        {
            ifs.EnemyAIList.Add(GameObject.Instantiate(ifs.CharacterPrefab, ifs.EnemyInitPosTrans[i]));
            ifs.EnemyAIList[i].transform.localPosition = Vector3.zero;
            ifs.CurrentActAIsList.Add(ifs.EnemyAIList[i]);
            ifs.EnemyAIList[i].isPlayer = false;
            InitCharacterData(ifs.EnemyAIList[i]);
        }
        ifs.CurrentActAIsList = ifs.CurrentActAIsList.OrderByDescending(p => p.actRate).ToList();
    }

    private void InitCharacterData(CharacterFightAI cfa)
    {
        IFightSystem ifs = this.GetSystem<IFightSystem>();
        if (cfa.isPlayer)
        {
            cfa.actRate = 8;
            cfa.SetDieMovePath(ifs.PlayerDieStartMovePath, ifs.PlayerDieEndMovePath);
            cfa.tag = Tags.PLAYER;
            cfa.characterInfo = this.GetModel<ICharacterDataModel>().GetCharacterInfo(2);
        }
        else
        {
            cfa.actRate = Random.Range(1, 10);
            cfa.SetDieMovePath(ifs.EnemyDieStartMovePath, ifs.EnemyDieEndMovePath);
            cfa.tag = Tags.ENEMY;
            cfa.characterInfo = this.GetModel<ICharacterDataModel>().GetCharacterInfo(Random.Range(1, 6));
        }
        cfa.name = cfa.characterInfo.pathName;
        cfa.Init();
    }
}

bug 位置不对

01 运行中位置、看向、角色名、角色模型(很少可能全一样,而且主角写死了是骨精灵)都错误

那就是初始位置和看向都没有及时初始化
在这里插入图片描述

02 错误位置,赋值与使用的顺序

01 基类CharacterFightAIBehaviour有
protected CharacterFightAI fromAI;
02 报的错误就是
fromAI.characterInfo.ID为0,数据字典(一些json数据)ID是1-5。也就是fromAI的初始characterInfo有问题
03 fromAI就是同节点的CharacterFightAI

。。。。
CharacterFightValueState

public class CharacterFightValueState : CharacterFightAIBehaviour
{
  	public override  void Init()
    {
        base.Init();
        characterCanvas = GetComponentInChildren<CharacterCanvas>();
        if (fromAI.isPlayer)
        {
            fromAI.HP = PlayerData().CurrentMaxHP.Value;
            fromAI.currentHP = PlayerData().CurrentHP.Value;
            fromAI.MP = PlayerData().MaxMP.Value;
            fromAI.currentMP = PlayerData().CurrentMP.Value;
        }
        else
        {
            characterCanvas.HideSlider();
        }

        string characterName = this.GetModel<ICharacterDataModel>().GetCharacterInfo(fromAI.characterInfo.ID).name;
        characterCanvas.SetCharacterName( characterName );
        fromAI.currentHP = fromAI.HP;
    }

stars 找子节点数组,GetChildren,GetChildrenDeep

    /// <summary>
    /// 找到 自身 下的 所有子节点  children
    /// </summary>
    /// <param name="parent"></param>
    /// <param name="children"></param>
    /// <returns></returns>
    public static Transform[] GetChildren(this Transform parent, out Transform[] children)
    {

        children = new Transform[parent.childCount];
        for (int i = 0; i < children.Length; i++)
        {
            children[i] = parent.GetChild(i);
        }

        return children;
    }


    /// <summary>
    ///  找到 自身 的 叫 parentName的子节点 下的所有子节点 children 
    /// </summary>
    /// <param name="t"></param>
    /// <param name="parentName"></param>
    /// <param name="children"></param>
    /// <returns></returns>
    public static Transform[] GetChildrenDeep(this Transform t, string parentName,  out Transform[] children)
    {
        Transform parent = t.FindChildDeep(parentName);
        children = new Transform[parent.childCount];
        for (int i = 0; i < children.Length; i++)
        {
            children[i] = parent.GetChild(i);
        }

        return children;
    }

starts modify AnimatorPara.cs

/****************************************************

	文件:
	作者:WWS
	日期:2023/04/10 20:05:40
	功能:AnimatorPara动画器的参数汇总

*****************************************************/

using UnityEngine;

public static class AnimatorPara
{
    public const string LookX = "LookX";
    public const string LookY = "LookY";
    public const string LookDirX = "LookDirX";
    public const string LookDirY = "LookDirY";
    public const string MoveState = "MoveState";
    public const string Hit = "Hit";
    public const string Die = "Die";
    public const string MoveValue = "MoveValue";
    public const string Attack = "Attack";
    public const string Skill = "Skill";
    public const string Defend = "Defend";
}

bug 重启

发生过敌人位置不对,敌人的技能接连很快释放。但是重启unity后就好了。
在这里插入图片描述

bug 不明没赋值的提示

图中的组件明明有赋值,同样的也有其它组件也这样写。
但是只有这个显示没赋值的提示。
不影响运行,但看着难受。
在这里插入图片描述

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

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

相关文章

python+vue 在线考试系统的设计与实现

1.用户登录 用户要通过本系统查询对课程信息进行下载&#xff0c;必须先输入用户名和密码进行登陆。为了避免非其他人员都可以获得登陆权限&#xff0c;登陆系统不设注册过程&#xff0c;所有用户和教师的登陆信息将事先由管理人员直接对数据库进行录入。 2.教师 教师登录系统后…

【排序】排序这样写才对Ⅱ -冒泡排序与快速排序Ⅰ

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

【Spring6】| Spring6整合JUnit

目录 一&#xff1a;Spring6整合JUnit 1. Spring对JUnit4的支持 2. Spring对JUnit5的支持 一&#xff1a;Spring6整合JUnit 1. Spring对JUnit4的支持 准备工作&#xff1a;pom.xml 注&#xff1a;以前是直接使用单元测试Junit&#xff0c;现在使用Spring对Junit的整合&…

快递电子运单上,电话应隐藏6位以上,禁止显示这些信息

我国快递年业务量达千亿件&#xff0c;快递电子运单是应用于快递外包装的重要单据&#xff0c;每年耗用量很大。在强化个人信息保护方面&#xff0c;《快递电子运单》国家标准要求快递企业、电商经营主体等采取措施&#xff0c;避免在电子运单上显示完整的收寄件人个人信息。 …

【机器学习】P14 Tensorflow 使用指南 Dense Sequential Tensorflow 实现

Tensorflow 第一节&#xff1a;使用指南Tensorflow 安装神经网络一些基本概念隐藏层和输出层&#xff1a;神经元的输出公式Tensorflow 全连接层 Dense 与 顺序模型 SequentialDense LayerSequential Model代码实现一个神经网络实现方式一&#xff1a;手写神经网络* 实现方式二&…

JavaScript基础入门全解析(上)

JavaScript基础语法 什么是JavaScript&#xff08;简称js&#xff09; 1.首先了解前端页面的组成&#xff08;前端页面的三层结构&#xff09; ●HTML 表示了你的页面内有什么&#xff0c;组成页面的骨架 &#xff08;结构层&#xff09; ●CSS 表示了你的页面中每一个内容是…

Linux系统中安装新版本nacos(centos7)

1. 背景需求 由于一些限制,在客户现场的Linux操作系统中,没有安装docker k8s等容器,无法直接使用镜像安装,而且客户要求只能在原始的操作系统中安装最新版的nacos,(为什么需要安装最新版的nacos,因为检测国网检测到之前版本的nacos有漏洞,需要安装新版的nacos). 2. 下载nacos…

Windows10+Cmake+VS2019编译opencv

主要参考&#xff1a;Windows10CmakeVS2019编译opencv&#xff08;超级详细&#xff09;_vs编译opencv_乐安世家的博客-CSDN博客 OpenCV&#xff1a;Releases - OpenCV 想直接简单使用的话&#xff0c;不需要自己编译&#xff0c;下载编译好的就可以 假如需要用到opencv-contr…

【Python入门第四十九天】Python丨NumPy 数组拆分

拆分 NumPy 数组 拆分是连接的反向操作。 连接&#xff08;Joining&#xff09;是将多个数组合并为一个&#xff0c;拆分&#xff08;Spliting&#xff09;将一个数组拆分为多个。 我们使用 array_split() 分割数组&#xff0c;将要分割的数组和分割数传递给它。 实例 将数…

Docker教程:如何将Helix QAC创建为一个容器并运行?

在这个Docker教程中&#xff0c;你将了解到如何将Helix QAC创建为一个容器化的镜像并运行。 Docker的基本定义是一个开源且流行的操作系统级虚拟化&#xff08;通常称为“容器化”&#xff09;技术&#xff0c;它是轻量级且可移植的&#xff0c;主要在Linux和Windows上运行。D…

Linux主机 SSH 通过密钥登录

我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux 服务器。但是&#xff0c;一般的密码方式登录&#xff0c;容易有密码被暴力破解的问题。所以&#xff0c;一般我们会将 SSH 的端口设置为默认的 22 以外的端口&#xff0c;或者禁用 root 账户登录。其实&#xff0c;有一个更…

换电脑 NoteExpress 数据备份迁移

前言 主要操作是跟着这篇博客做的&#xff1a;NoteExpress数据库备份和转移。但也有一些不一样的地方 旧电脑NoteExpress(NE)版本3.7&#xff0c;新电脑版本3.8 旧电脑 导出配置文件 桌面找到图标&#xff0c;打开位置&#xff0c;点击配置备份&#xff08;绿色的图标&#…

水库安全运行智慧管理平台解决方案筑牢防汛“安全墙”

解决方案 水库安全运行智慧管理系统解决方案&#xff0c;系统主要由降雨量监测站、水库水位监测站、大坝安全监测中的渗流量、渗流压力和变形监测站及视频和图像监测站等站点组成&#xff0c;同时建立规范、统一的监测平台&#xff0c;集数据传输、信息共享、数据储存于一体&a…

图解HTTP阅读笔记:第4章 返回结果的HTTP状态码

《图解HTTP》第四章读书笔记 图解HTTP第4章&#xff1a;返回结果的HTTP状态码4.1 状态码告知从服务器端返回的请求结果4.2 2XX成功4.2.1 200 OK4.2.2 204 No Content4.2.3 206 Parital Content4.3 3XX重定向4.3.1 301 Moved Permanently4.3.2 302 Found4.3.3 303 See Other4.3.…

服务(第三篇)Apache配置与应用

httpd服务支持的虚拟主机类型包括以下三种: 1.基于域名&#xff1a;为每个虚拟主机使用不同的域名&#xff0c;但是其对应的 IP 地址是相同的。 2.基于IP地址&#xff1a;为每个虚拟主机使用不同的域名&#xff0c;且各自对应的IP地址也不相同。这种方式需要为服务器配备多个网…

Femto基站及其射频方案

关于部署3G femto基站 3GPP定义第三代移动通信的目的是要为客户提供全方位的移动多媒体体验。但很多条件下&#xff0c;这一目的并未实现&#xff0c;尤其是在边远地区或居民聚集区。 一个可行的解决方案是在家庭范围内部署无需宏node-B基站即可提供最大移动数据速率的家用基站…

26基于模型预测控制MPC的永磁同步电机MATLAB代码

资源地址&#xff1a; 基于模型预测控制&#xff08;MPC&#xff09;的永磁同步电机Matlab代码-电子商务文档类资源-CSDN文库 主要内容&#xff1a; 包含单电流环MPC仿真&#xff08;仅电流环使用MPC策略&#xff0c;速度环使用PI调节器&#xff09;、速度环和电流环MPC仿真…

C ++匿名函数:揭开C++ Lambda表达式的神秘面纱

潜意识编程&#xff1a;揭秘C Lambda表达式的神秘面纱 Subconscious Programming: Unveiling the Mystery of C Lambda Expressions 引言&#xff1a;Lambda表达式的魅力 (The Charm of C Lambda Expressions)Lambda表达式简介与基本概念 (Introduction and Basic Concepts of …

【Linux】浅析Input子系统

文章目录1. 框架1.1 数据结构1.2 evdev_handler1.3 evdev_init1.4 input_register_handler2. 应用如何打开节点并读取到事件数据2.1 evdev_fops2.2 evdev_open2.3 evdev_release2.4 evdev_read2.5 evdev_write2.6 evdev_poll2.7 evdev_fasync2.8 evdev_ioctl2.9 evdev_ioctl_co…

ADManager Plus:简化 Active Directory 管理的完美工具

在企业中&#xff0c;Active Directory&#xff08;AD&#xff09;是一个非常重要的组件&#xff0c;用于管理和控制所有计算机和用户的访问权限。然而&#xff0c;AD的管理和维护需要一定的技术能力和时间成本。为了简化这个过程&#xff0c;ManageEngine 推出了 ADManager Pl…