架构设计目录二

    • UI架构
      • 基类BaseWindow
      • Control控制类
      • State状态接口
      • 窗口管理类
      • 状态管理类
    • 角色系统
      • 角色系统设计的框架图
      • 角色实体类父类 IEntity
      • 有限状态机接口EntityFSM
      • 功能实现类Entity
      • FSM具体实现
      • IEntity的后续细分
      • Entity管理类
      • 管理类的扩展
    • 技能系统
      • 父类IEffect
      • 子类中的部分实现
      • 管理类EffectManager


UI架构

先看一下模块架构设计思路图:
在这里插入图片描述
UI 的设计框架有很多的,这次编写的 UI 框架不能说是最好的,它最大的优点就是实现了资源和代码的彻底分离,真正实现了程序和美术人员的分工合作。 UI 框架也是采用了 MVC 设计模式,但是在外面加了一层 State 状态变换,因为每个 UI 切换是在不同的状态之间的变换,这样整个 UI 架构设计就完成了。

游戏的每个 UI 都是使用 Panel 面板制作完成的,每个面板就是一个窗体,用 MVC 中的 View 表示,而用代码表示就是 Window 窗体类,MVC 中的 Control 也同样对应窗体的 Control 类。MVC 中的 Model 表示的是数据的传输,在当前可以直接使用配置文件进行读取加载,或者通过网络进行传输。


基类BaseWindow

先来看 Window 窗体类的设计,每个 UI 对应自己的 Window 窗体类。下面先进行 Window 类模块的代码编写,在写某个模块时,首先做的事情是搞清楚这个模块包括哪些内容,再考虑一下编写此类时扩展是否方便。这些都是编写时需要注意的问题,有的程序员拿过策划需求就开始编写代码,这样导致的后果是一旦策划需求改变,代码中就要重新加功能,搞的很麻烦,这对于程序员来说是大忌。

以 Window 类编写为例,游戏中显示窗体首先要创建窗体,还有窗体可以隐藏、销毁。另外,创建窗体首先要知道窗体的资源名字,还有这个窗体是在哪个场景中创建的,是登录场景还是游戏战斗场景等。因为窗体类不继承 Mono,为了方便使用窗体中的控件,所以还要做初始化窗体控件的功能以及做监听处理。

这些功能对于游戏中的任何 UI 窗体都是适用的,换句话说,所有的 UI 这些功能都是必备的,也就是 UI 的共性。这让人自然而然想到创建一个父类,如果不建父类,每个 Window 类都要写一套逻辑,这会使得代码很乱,而且如果是多个人合作的话,每人都来一套逻辑,后期代码无法维护。所以必须要建一个父类,代码如下:

 public abstract class BaseWindow
{protected Transform mRoot; //UI根结点protected EScenesType mScenesType; //场景类型protected string mResName;         //资源名protected bool mResident;          //是否常驻 protected bool mVisible = false;   //是否可见//类对象初始化public abstract void Init();//类对象释放public abstract void Realse();//窗口控制初始化protected abstract void InitWidget();//窗口控件释放protected abstract void RealseWidget();//游戏事件注册protected abstract void OnAddListener();//游戏事件注消protected abstract void OnRemoveListener();//显示初始化public abstract void OnEnable();//隐藏处理public abstract void OnDisable();//每帧更新public virtual void Update(float deltaTime) { }//取得所以场景类型public EScenesType GetScenseType(){return mScenesType;}//是否已打开public bool IsVisible() { return mVisible;  }//是否常驻public bool IsResident() { return mResident; }//显示public void Show(){if (mRoot == null){if (Create()){InitWidget();}}if (mRoot && mRoot.gameObject.activeSelf == false){mRoot.gameObject.SetActive(true);mVisible = true;OnEnable();OnAddListener();}}//隐藏public void Hide(){if (mRoot && mRoot.gameObject.activeSelf == true){OnRemoveListener();OnDisable();if (mResident){mRoot.gameObject.SetActive(false);}else{RealseWidget();Destroy();}}mVisible = false;}//预加载public void PreLoad(){if (mRoot == null){if (Create()){InitWidget();}}}//延时删除public void DelayDestory(){if (mRoot){RealseWidget();Destroy();}}//创建窗体private bool Create(){if (mRoot){Debug.LogError("Window Create Error Exist!");return false;}if (mResName == null || mResName == ""){Debug.LogError("Window Create Error ResName is empty!");return false;}if (GameMethod.GetUiCamera.transform== null){return false;}GameObject obj = LoadUiResource.LoadRes(GameMethod.GetUiCamera.transform, mResName);if (obj == null){return false;}mRoot = obj.transform;mRoot.gameObject.SetActive(false);return true;}//销毁窗体protected void Destroy(){if (mRoot){LoadUiResource.DestroyLoad(mRoot.gameObject);mRoot = null;}}//取得根节点public Transform GetRoot(){return mRoot;}}

该父类设计成了一个抽象类并提供了一些接口便于子类的实现,这些接口所要实现的具体内容是不同的,父类无法具体一一实现,但是显示、隐藏、破坏这些都是通用的函数,可以在父类中实现。再看一下子类的实现方式,以 LoginWindow 类为例,制作一个简单的 UI 界面,如下图所示:
在这里插入图片描述
在这里插入图片描述
我们就以这两个按钮的界面为例来编写代码:

public class LoginWindow : BaseWindow
{//开始Transform mBtnStart;enum LOGINUI{None = -1,Login,SelectServer,}public LoginWindow() {//场景类型mScenesType = EScenesType.EST_Login;//场景资源mResName = GameConstDefine.LoadGameLoginUI;//是否常驻内存mResident = false;}////////////////////////////继承接口///////////////////////////类对象初始化监听显示和隐藏,为了解耦合public override void Init(){EventCenter.AddListener(EGameEvent.eGameEvent_LoginEnter, Show);EventCenter.AddListener(EGameEvent.eGameEvent_LoginExit, Hide);}//类对象释放public override void Realse(){EventCenter.RemoveListener(EGameEvent.eGameEvent_LoginEnter, Show);EventCenter.RemoveListener(EGameEvent.eGameEvent_LoginExit, Hide);}//窗口控件初始化以及控件监听protected override void InitWidget(){mBtnStart = mRoot.Find("BtnStart");mBtnStart.GetComponent<Button>().onClick.AddListener(OnClickAddButton);DestroyOtherUI();}//消息回调函数private void OnClickAddButton(){//在这里监听按钮的点击事件LoginCtrl.Instance.StartGame();}//删除Login外其他控件,例如public static void DestroyOtherUI(){Canvas canvas = GameMethod.GetCanvas;for (int i = 0; i < canvas.transform.childCount; i++){if (canvas.transform.GetChild(i) != null && canvas.transform.GetChild(i).gameObject != null){GameObject obj = canvas.transform.GetChild(i).gameObject;if (obj.name != "Login(Clone)"){GameObject.DestroyImmediate(obj);}                    }}}//窗口控件释放protected override void RealseWidget(){}//游戏事件注册protected override void OnAddListener(){}//游戏事件注消protected override void OnRemoveListener(){}//显示public override void OnEnable(){}//隐藏public override void OnDisable(){}
}

构造函数对资源文件和 UI 所在的场景类型初始化,以及该 UI 是否常住内存。后面函数是继承自父类,并对它们的具体实现,在函数中有 LoginCtrl 类接口调用,这个跟 LoginWidow 窗体息息相关,它是 MVC 中的 Control。下面再编写 LoginCtrl 类。


Control控制类

控制类的主要作用是播放消息,然后在 Loginwindow 中触发已设置监听的函数,如 Show、显示窗体,控制等:

public class LoginCtrl : Singleton<LoginCtrl>
{public void Enter(){EventCenter.Broadcast(EGameEvent.eGameEvent_LoginEnter);   }public void Exit(){EventCenter.Broadcast(EGameEvent.eGameEvent_LoginExit);}//登陆public void Login(string account, string pass){}//登陆错误反馈public void LoginError(int code){}//登陆失败public void LoginFail(){}//开始游戏public void StartGame(){SceneManager.LoadScene("Play");WindowManager.Instance.ChangeScenseToPlay(EScenesType.EST_Login);GameStateManager.Instance.ChangeGameStateTo(GameStateType.GST_Play);}}

将其设置成单例模式,在 Enter 函数中进行消息广播,另外在函数 StartGame 中使用了 WindowManager 类接口和 GameStateManager 类接口。这两个类是很关键的,后面会贴出。当然如果只有 Loginwindow 和 LoginCtrl 还是无法执行的,我们还缺少 State 状态类,状态类是负责窗体 UI 之间的切换,每个 UI 窗体对应着自己的状态,为了区分不同的 UI 窗体,利用这些状态枚举表示:

public enum GameStateType
{GST_Continue,GST_Login,GST_Role,GST_Loading,GST_Play,//***
}

State状态接口

通常来说,我们会定义设置某个状态、获取状态、进入状态、停止状态、更新状态这些方法等。我们在设计每个模块时,都会先明确这个模块具体要做哪些事情,其实在设计一个类的内容时,可以先多想想,这样类的函数定义自然就有了。另外,提到的这些方法,每个 UI 状态都会包含,这样我们就可以将其抽离出来,定义成一个接口模块供程序使用:

public interface IGameState
{GameStateType GetStateType();void SetStateTo(GameStateType gsType);void Enter();GameStateType Update(float fDeltaTime);void FixedUpdate(float fixedDeltaTime);void Exit();
}

抽象类实现了 oop 中的一个原则,把可变的与不可变的分离,所以 BaseWindow 采用了抽象的定义。UI 通用函数方法在父类中先实现出来,而不通用的函数方法只提供接口。再说接口类,好的接口类定义应该是具有专一功能性的,而不是多功能的,否则会造成接口污染。我们的状态类 IGameState 功能是单一的,所以 IGameState 采用了接口的定义。窗体中的状态类继承 IGameState 类:

class LoginState : IGameState
{private  GameStateType _stateTo;//构造函数public LoginState(){}//获取状态public GameStateType GetStateType(){return GameStateType.GST_Login;}//设置状态public void SetStateTo(GameStateType gs){_stateTo = gs;}//进入状态public void Enter(){SetStateTo(GameStateType.GST_Continue);LoginCtrl.Instance.Enter();        }//停止状态public void Exit(){LoginCtrl.Instance.Exit();}public void FixedUpdate(float fixedDeltaTime){}//更新状态public GameStateType Update(float fDeltaTime){return _stateTo;}}

在这个 Login UI 窗体状态类中实现了 IGameState 中定义的接口函数。

总的来说一个 UI 窗体主要是包含三个代码模块:Window、Ctl、State。Window 和 Ctrl 之间是通过 Event 和接口连接起来的,而 State 和 Ctrl 是通过调用接口连接起来的。模块之间的关系框架:
在这里插入图片描述


窗口管理类

现在需要一个窗体管理类来进行统一调度,这让人会想到工厂模式。那么 WindowMananger 管理类要管理这么多窗体,首先要做的事情是将它们进行注册,也就是将其存储到字典中:

 public enum EScenesType{EST_None,EST_Login,EST_Play,}public enum EWindowType{EWT_LoginWindow, //登录EWT_RoleWindow, //用户EWT_PlayWindow,//战斗}public class WindowManager : Singleton<WindowManager>{public WindowManager(){mWidowDic = new Dictionary<EWindowType, BaseWindow>();mWidowDic[EWindowType.EWT_LoginWindow] = new LoginWindow();mWidowDic[EWindowType.EWT_RoleWindow] = new RoleWindow();mWidowDic[EWindowType.EWT_PlayWindow] = new PlayWindow();}}

定义了窗体所对应自己的类型后,便于区分不同的窗体,构造函数实现了窗体的注册,注册好了窗体后,下面开始窗体方法的实现,比如游戏中不同的窗体之间进行 UI 切换,从一个 UI 切换到另一个 UI。再比如切换窗体到游戏场景、切换窗体返回到登录场景等等,这些方法对应的实现函数如下:

//切换到游戏场景public void ChangeScenseToPlay(EScenesType front){foreach (BaseWindow pWindow in mWidowDic.Values){if (pWindow.GetScenseType() == EScenesType.EST_Play){pWindow.Init();if(pWindow.IsResident()){pWindow.PreLoad();}}else if ((pWindow.GetScenseType() == EScenesType.EST_Login) && (front == EScenesType.EST_Login)){pWindow.Hide();pWindow.Realse();if (pWindow.IsResident()){pWindow.DelayDestory();}}}}//切换到登录场景public void ChangeScenseToLogin(EScenesType front){foreach (BaseWindow pWindow in mWidowDic.Values){if (front == EScenesType.EST_None && pWindow.GetScenseType() == EScenesType.EST_None){pWindow.Init();if (pWindow.IsResident()){pWindow.PreLoad();}}if (pWindow.GetScenseType() == EScenesType.EST_Login){pWindow.Init();if (pWindow.IsResident()){pWindow.PreLoad();}}else if ((pWindow.GetScenseType() == EScenesType.EST_Play) && (front == EScenesType.EST_Play)){pWindow.Hide();pWindow.Realse();if (pWindow.IsResident()){pWindow.DelayDestory();}}}}

上面的两个方法,就是遍历窗体,看看它是对窗体隐藏还是显示,还是对其进行预加载操作。这两个方法会经常使用。另外,还需要提供隐藏所有窗体和更新窗体的接口:

public void HideAllWindow(EScenesType front){foreach (var item in mWidowDic){if (front == item.Value.GetScenseType()){Debug.Log(item.Key);item.Value.Hide();}}}public void Update(float deltaTime){foreach (BaseWindow pWindow in mWidowDic.Values){if (pWindow.IsVisible()){pWindow.Update(deltaTime);}}}

状态管理类

每个 UI 窗体都对应三者:Window、Ctrl、State,这样每个窗体对应的 State 跟 Window 也是同样多的,有这么多状态,就需要一个管理类来进行管理。将其都放到 StateManager 管理类中,这样既管理了 State,也做了状态切换处理:

        public GameStateManager(){gameStates = new Dictionary<GameStateType, IGameState>();IGameState gameState;gameState = new LoginState();gameStates.Add(gameState.GetStateType(), gameState);gameState = new RoleState();gameStates.Add(gameState.GetStateType(), gameState);gameState = new PlayState();gameStates.Add(gameState.GetStateType(), gameState);}

管理类主要是对于这些同类型的类模块的操作,比如要获取到当前状态以及切换状态,设置游戏刚开始的默认状态,还有游戏状态的更新函数 Update / FixedUpdate,关于管理类的方法,为了逻辑的编写,肯定需要一个统一的接口调用。状态管理类 GameStateManager 的其他方法:

// 获取当前状态public IGameState GetCurState(){return currentState;}//改变状态public void ChangeGameStateTo(GameStateType stateType){if (currentState != null && currentState.GetStateType() != GameStateType.GST_Loading && currentState.GetStateType() == stateType) return;if (gameStates.ContainsKey(stateType)){if (currentState != null){currentState.Exit();}currentState = gameStates[stateType];currentState.Enter();}}//进入默认状态public void EnterDefaultState(){ChangeGameStateTo(GameStateType.GST_Login);}public void FixedUpdate(float fixedDeltaTime){if (currentState != null){currentState.FixedUpdate(fixedDeltaTime);}}//更新状态public void Update(float fDeltaTime){GameStateType nextStateType = GameStateType.GST_Continue;if (currentState != null){nextStateType = currentState.Update(fDeltaTime);}if (nextStateType > GameStateType.GST_Continue){ChangeGameStateTo(nextStateType);}}//获取状态public IGameState getState(GameStateType type){if (!gameStates.ContainsKey(type)){return null;}return gameStates[type];}

UI 架构设计基本完整了。


角色系统

大部分游戏都有自己的角色系统,角色系统设计要考虑的问题比较多,下面开始搭建一个角色系统,首先要加载一个角色,这个角色要包括这些属性:资源路径、资源名字、角色拥有的装备、血条、经验值、攻击距离、防御、BUFF、角色等级、重生时间等,另外,我们的角色还会有自己的动作和技能等。

定义好角色属性后,就要考虑实现它们的方法了,角色会设计不同动作和技能状态,这些状态之间会不停的切换,因为角色的状态是有限个的,动作不可能是无限的,这自然会让人想到 FSM 有限状态机的使用。


角色系统设计的框架图

在这里插入图片描述


角色实体类父类 IEntity

游戏中的每个角色都会有这些属性和方法,当然也包括怪物和 NPC。了解了角色的属性和方法后,接下来就要设计代码去实现它们,作为共同拥有的属性,我们可以将其抽离出来作为父类编写。首先定义角色的共有属性,因为角色属性太多,这里只写一下重要的,下面是定义的角色实体类的父类 IEntity 中的片段:

//物理攻击public float PhyAtk{set;get;}//魔法攻击public float MagAtk{set;get;}//物理防御public float PhyDef{set;get;}//魔法防御public float MagDef{set;get;}//血量恢复public float HpRecover{set;get;}//魔法恢复public float MpRecover{set;get;}// 血量public float Hp{set;get;}//魔法public float Mp{private set;get;}//等级public int Level{private set;get;}

以上是列出了一部分的属性定义,另外使用有限状态机对动作和技能进行状态的变换,有限状态机的设计首先要知道状态机的类型,通过类型去区分不同的状态,这个一般使用枚举值表示:

    //状态枚举public enum FSMState
{FSM_STATE_FREE,FSM_STATE_RUN,FSM_STATE_SING,FSM_STATE_RELEASE,FSM_STATE_LEADING,FSM_STATE_LASTING,FSM_STATE_DEAD,FSM_STATE_ADMOVE,FSM_STATE_FORCEMOVE,FSM_STATE_RELIVE,FSM_STATE_IDLE,
}

有限状态机接口EntityFSM

该枚举值定义了动作的一些状态,然后开始状态接口的封装,有限状态机跟我们前面的 State 比较类似:

    //有限状态机接口public interface EntityFSM
{bool CanNotStateChange{set;get;}FSMState State { get; }void Enter(IEntity entity , float stateLast);bool StateChange(IEntity entity , EntityFSM state);void Execute(IEntity entity);void Exit(IEntity Ientity);
}

有限状态机定义了执行的接口和方法,进入状态、改变状态、执行、停止状态这些方法,该类只提供了接口,并没有实现,这样它的实现就会放到其子类中。由于 FSM 有限状态机的切换是通用的,我们还是将它们放到父类 IEntity 类中进行:

    public void OnFSMStateChange(EntityFSM fsm){if (this.FSM != null && this.FSM.StateChange(this, fsm)){return;}if (this.FSM == fsm && this.FSM != null && (this.FSM.State == FSMState.FSM_STATE_DEAD)){return;}if (this.FSM != null){this.FSM.Exit(this);}this.FSM = fsm;if (this.FSM != null)this.RealEntity.FSMStateName = fsm.ToString();this.FSM.Enter(this, 0.0f);}

在该函数的执行过程中,首先判断状态是否为空,如果不为空,将停止当前状态,执行下一个状态。设计思想是通用的接口可以将其放到某个类中定义,比如实体类的移动、待机、技能等接口,这里将其放在父类 IEntity 中,当然这些函数具体可以在子类中实现,而通用的可以在父类中实现,就比如:

  //状态机改变数据public void EntityFSMChangeDataOnSing(Vector3 mvPos, Vector3 mvDir, int skillID, IEntity targetID){EntityFSMPosition = mvPos;EntityFSMDirection = mvDir;EntitySkillID = skillID;entitySkillTarget = targetID;}       public void EntityFSMChangedata(Vector3 mvPos, Vector3 mvDir){EntityFSMPosition = mvPos;EntityFSMDirection = mvDir;}    public virtual void OnEntitySkill(){}//进入Idle状态public virtual void OnEnterIdle(){}//进入Move状态public virtual void OnEnterMove(){}

功能实现类Entity

基本上角色所有的共同属性和方法都定义在 IEntity 类中,也就是核心功能设计。再说一下 Entity 类,核心思想就是播放角色的动作以及设置技能的播放点,它是继承 Mono 的,需要动态的挂接到对象上。新动画状态机或者老动画状态播放都是在此类中实现的,另外,角色身上的通过文本读取的基础属性也是在此类中定义,这样可以方便策划调试运行代码:

public class EntityAttrubte
{public float Hp;public float HpMax;public float Mp;public float MpMax;public float Speed;public float PhyAtk;public float MagAtk;public float PhyDef;public float MagDef;public float AtkSpeed;public float AtkDis;public float HpRecover;public float MpRecover;public float RebornTime;public void AttribbuteUpdate(IEntity entity){Hp = entity.Hp;HpMax = entity.HpMax;Mp = entity.Mp;MpMax = entity.MpMax;Speed = entity.EntityFSMMoveSpeed;PhyAtk = entity.PhyAtk;MagAtk = entity.MagAtk;PhyDef = entity.PhyDef;MagDef = entity.MagDef;AtkSpeed = entity.AtkSpeed;AtkDis = entity.AtkDis;HpRecover = entity.HpRecover;MpRecover = entity.MpRecover;RebornTime = entity.RebornTime;}
}

在此定义了基础属性,然后看一下 Entity 类中的核心设计:

    //初始化挂载点public virtual void Awake(){objAttackPoint = transform.Find("hitpoint");objBuffPoint = transform.Find("buffpoint");objPoint = transform.Find("point");if (objAttackPoint == null || objBuffPoint == null || objPoint == null){}skeletonAnimation = GetComponentInChildren<SkeletonAnimation>();EntityAttribute = new EntityAttrubte();}// 设置移动速度public void SetMoveAnimationSpd(float spd){if (GetComponent<Animation>() == null){return;}AnimationState aState = GetComponent<Animation>()["walk"];if (aState != null){aState.speed = spd;}}//播放动作public void PlayerAnimation(string name){if (name == "" || name == "0"){return;}if (this.GetComponent<Animation>() == null){return;}GetComponent<Animation>().CrossFade(name);}//播放移动动作public void PlayeMoveAnimation(){if (skeletonAnimation != null){var track = skeletonAnimation.AnimationState.SetAnimation(0, "run", true);}}//播放待机动作public void PlayeIdleAnimation(){if (skeletonAnimation != null){var track = skeletonAnimation.AnimationState.SetAnimation(0, "idle", true);}}//播放攻击动作internal void PlayAttackAnimation(){if (skeletonAnimation != null){var track = skeletonAnimation.AnimationState.SetAnimation(1, "shoot", false);}}

在 Awake 函数中实现挂载节点的初始化操作,这里使用的是老动画播放,原理跟新动画是类似的,如果是使用新动画只是将其改成 SetTrigger 触发就可以了。


FSM具体实现

下面再说一下有限状态机 FSM 的具体实现,在前面已经介绍了类 EntityFSM,它只是提供了接口,没有具体实现。下面以角色待机动作为例介绍FSM的使用:

    public class EntityIdleFSM : EntityFSM{public static readonly EntityFSM Instance = new EntityIdleFSM();public FSMState State{get{return FSMState.FSM_STATE_IDLE;}}public bool CanNotStateChange{set;get;}            public bool StateChange(IEntity entity , EntityFSM fsm){return CanNotStateChange;}public void Enter(IEntity entity , float last){entity.OnEnterIdle();}public void Execute(IEntity entity){}public void Exit(IEntity entity){}}

这样角色系统的基础类设计完成了


IEntity的后续细分

下面再回到父类 IEntity,已经在其中实现了角色的通用方法以及角色使用的属性,我们还需要继续实现它的子类,在此将其进行了细分:
在这里插入图片描述
在 IEntity 类下面分了两级,之所以分两级是因为角色还有与网络有关的方法和属性,如果只是放到一个类 IEntity 中,这样 IEntity 就显得很臃肿,代码量会很庞大,不利于维护。

IPlayer 类模块也是 IEntity 的子类,之所以划分出这个类,是因为游戏需要联网,开房间,而此类就涉及到开房间阵营的设置,它不包含具体的数值,它还是一个抽象的玩家实体。因为玩家都具有阵营、房间这些属性,所以也是公有的,抽离出来的。

首先将角色身上的一些函数方法都放在 IPlayer 类里面,它做的事情都是与角色相关的,会比较杂,比如角色身上的相机位置更新、角色的重生、创建角色阴影效果,以及在角色身上动态的添加多个碰撞体,改变角色的移动速度、锁定目标、创建血条等,这些方法都是围绕角色身上展开的:

// 设置锁定对象public virtual void SetSyncLockTarget(IEntity entity){if (SyncLockTarget == entity){return;}this.DeltLockTargetXuetiao(entity);this.SyncLockTarget = entity;}// Entity移动属性变化public override void OnEntityMoveSpeedChange(int value){base.OnEntityMoveSpeedChange(value);HeroConfigInfo heroConfig;if (ConfigReader.HeroXmlInfoDict.TryGetValue(NpcGUIDType, out heroConfig)){float speed = value / heroConfig.HeroMoveSpeed * heroConfig.n32BaseMoveSpeedScaling / 1000;if (speed == 0){return;}this.RealEntity.SetMoveAnimationSpd(speed);}}

以上代码可以根据需求去编写,重点是掌握思想,具体实现可以根据需求去做。接下来再看 IPlayer 类的子 Player 类的设计,前面两个都是角色公有的属性和方法,Player 这个类涉及玩家的具体操作,比如玩家的游戏物品获取、技能相关的释放和冷却、玩家的经验值或者血量、自身角色的移动、动作的播放以及动作事件的回调等,就比如下面的示例方法:

    public Player(UInt64 sGUID, EntityCampType campType): base(sGUID, campType){UserGameItems = new Dictionary<int, int>();UserGameItemsCount = new Dictionary<int, int>();UserGameItemsCoolDown = new Dictionary<int, float>();for (int ct = 0; ct < 6; ct++){UserGameItems.Add(ct, 0);UserGameItemsCount.Add(ct, 0);UserGameItemsCoolDown.Add(ct, 0f);}}//准备释放技能//技能类型public void SendPreparePlaySkill(SkillType skType){int skillID = GetSkillIdBySkillType(skType);//沉默了if (Game.Skill.BuffManager.Instance.isSelfHaveBuffType(1017)){return;}if (skillID == 0){MsgInfoManager.Instance.ShowMsg((int)ERROR_TYPE.eT_AbsentSkillNULL);return;}}//初始化技能升级列表private void InitSkillDic(Dictionary<SkillType, int> skillDic){int id = (int)ObjTypeID;HeroConfigInfo heroInfo = ConfigReader.GetHeroInfo(id);skillDic.Add(SkillType.SKILL_TYPE1, heroInfo.HeroSkillType1);skillDic.Add(SkillType.SKILL_TYPE2, heroInfo.HeroSkillType2);skillDic.Add(SkillType.SKILL_TYPE3, heroInfo.HeroSkillType3);skillDic.Add(SkillType.SKILL_TYPE4, heroInfo.HeroSkillType4);skillDic.Add(SkillType.SKILL_TYPEABSORB1, 0);skillDic.Add(SkillType.SKILL_TYPEABSORB2, 0);}//根据技能类型,换算出满足指定技能的准确idprivate void SetSkillUpdate(SkillType skillType, int lv){int baseId = 0;if (!BaseSkillIdDic.TryGetValue(skillType, out baseId)) return;//技能id不存在SkillManagerConfig info = ConfigReader.GetSkillManagerCfg(baseId);if (baseId == 0 || info == null){return;//不存在技能信息 }for (int i = baseId + SKILL_UPDATE_TOTAL_LEVEL - 1; i >= 0; i--){SkillManagerConfig infoNew = ConfigReader.GetSkillManagerCfg(i);if (i == 0 || infoNew == null || infoNew.n32UpgradeLevel > lv)continue;SkillIdDic[skillType] = i;break;}}

Entity管理类

至此角色系统底层的封装就结束了,接下来再封装一个管理类————EntityManager 类,因为游戏中会生成很多的实体对象,这些实体对象包括玩家自身、其他玩家、怪物 NPC 等,所以需要一个对外提供接口的管理类,还要创建一个对外提供的创建角色的接口,可以用它创建我们的角色。在其中,首先需要创建存储我们所定义实体对象的字典 Dictionary,用于保存游戏中的所有生成的实体对象:

    public static EntityManager Instance{private set;get;}public static Dictionary<UInt64, IEntity> AllEntitys = new Dictionary<UInt64, IEntity>();public enum CampTag{SelfCamp = 1,EnemyCamp = 0,}static int[] HOME_BASE_ID = { 21006, 21007, 21020, 21021 };private static List<IEntity> homeBaseList = new List<IEntity>();public EntityManager(){Instance = this;}

这里将 EntityManager 管理类作为单例模式使用,有了存储实体对象的字典后,就可以对实体对象进行诸如显示和隐藏操作以及删除所有实体的操作,其实就是对保存在 Dictionary 中的对象进行操作:

    //显示实体public void ShowEntity(UInt64 sGUID, Vector3 pos, Vector3 dir){if (!AllEntitys.ContainsKey(sGUID) || AllEntitys[sGUID].realObject == null){return;}AllEntitys[sGUID].realObject.transform.position = pos;AllEntitys[sGUID].realObject.transform.rotation = Quaternion.LookRotation(dir);AllEntitys[sGUID].realObject.SetActive(true);if (AllEntitys[sGUID].FSM != null && AllEntitys[sGUID].FSM.State != Game.FSM.FSMState.FSM_STATE_DEAD){AllEntitys[sGUID].ShowXueTiao();}else if (AllEntitys[sGUID].FSM != null && AllEntitys[sGUID].FSM.State == Game.FSM.FSMState.FSM_STATE_DEAD){AllEntitys[sGUID].HideXueTiao();}}//隐藏实体public void HideEntity(UInt64 sGUID){if (!AllEntitys.ContainsKey(sGUID)){return;}IEntity entity = null;if (EntityManager.AllEntitys.TryGetValue(sGUID, out entity) && entity.entityType == EntityType.Player){}AllEntitys[sGUID].HideXueTiao();AllEntitys[sGUID].realObject.SetActive(false);}//删除实体public void DestoryAllEntity(){List<UInt64> keys = new List<UInt64>();foreach (IEntity entity in AllEntitys.Values){if (entity.entityType != EntityType.Building){keys.Add(entity.GameObjGUID);}}foreach (UInt64 gui in keys){HandleDelectEntity(gui);}}

另外,最重要的操作就是创建模型实体,创建的过程,还包括将其放到对象池的过程,避免频繁的创建和删除,产生内存碎片,同时需要将其在场景中显示出来,并且添加组件代码:

    public GameObject CreateEntityModel(IEntity entity, UInt64 sGUID, Vector3 dir, Vector3 pos){if (entity != null){int id = (int)entity.ObjTypeID;this.SetCommonProperty(entity, id);entity.ModelName = "Jinglingnan_6";if (entity.ModelName == null || entity.ModelName == ""){return null;}string path = GameDefine.GameConstDefine.LoadModelPath;//创建GameObject    string resPath = path + entity.ModelName;entity.realObject = GameObjectPool.Instance.GetGO(resPath);if (entity.realObject == null){Debug.LogError("entity realObject is null");}//填充Entity信息entity.resPath = resPath;entity.objTransform = entity.realObject.transform;entity.realObject.transform.localPosition = pos;entity.realObject.transform.localRotation = Quaternion.LookRotation(dir);if (entity.NPCCateChild != ENPCCateChild.eNPCChild_BUILD_Shop){entity.CreateXueTiao();}AddEntityComponent(entity);return entity.realObject;}return null;}

在函数中,entity 是父类中的对象,它并不是实际意义上的模型实体,它内部定义了 GameObject,这样就可以对其进行赋值操作,使用的是对象池中的函数接口。

在函数中还调用了接口 AddEntityComponent,它用于动态添加角色的脚本组件,因为资源和代码是彻底分离的:

    public static void AddEntityComponent(IEntity entity){//没有,添加Entity组件if (entity.realObject.GetComponent<Entity>() == null){Entity syncEntity = entity.realObject.AddComponent<Entity>() as Entity;entity.RealEntity = syncEntity;}//直接取else{Entity syncEntity = entity.realObject.GetComponent<Entity>() as Entity;entity.RealEntity = syncEntity;}}

以上函数能够动态的添加 Entity 代码组件,如果在游戏中,我们要在场景中增加某个对象呢,还要获取某个对象呢,这些也是这个实体管理类来提供的:

    public void AddEntity(UInt64 sGUID, IEntity entity){if (AllEntitys.ContainsKey(sGUID)){Debug.LogError("Has the same Guid: " + sGUID);return;}AllEntitys.Add(sGUID, entity);}public virtual IEntity GetEntity(UInt64 id){IEntity entity;if (AllEntitys.TryGetValue(id, out entity)){return entity;}return null;}

不论增加还是获取都是通过它的 ID 来进行的,相比字符串查找,效率更高,这样 EntityManager 类就完成了。如果我们还需要在 EntityManager 类的基础上扩展,就需要自己实现管理类去继承 EntityManager。


管理类的扩展

在此又实现了一个 PlayerManager 用于生成 Player,该类继承 EntityManager,它的核心思想就是创建 Player:

public class PlayerManager : EntityManager{public static new PlayerManager Instance {private set;get;}public Dictionary<UInt64, IPlayer> AccountDic = new Dictionary<UInt64, IPlayer>();public PlayerManager(){Instance = this;}public Player LocalPlayer {set;get;}		 public IPlayer LocalAccount{set;get;}public override Ientity HandleCreateEntity (UInt64 sGUID , EntityCampType campType){    Iplayer player;if (GameUserModel.Instance.IsLocalPlayer(sGUID)){player = new Player(sGUID, campType);                }else{player =  new Iplayer(sGUID, campType);}player.GameUserId = sGUID;return player;}public void AddAccount(UInt64 sGUID, IPlayer entity){if (AccountDic.ContainsKey (sGUID)) {		return;}AccountDic.Add (sGUID , entity);}public override void SetCommonProperty(IEntity entity, int id){base.SetCommonProperty(entity, id);IPlayer mpl = (IPlayer)entity;if (mpl.GameUserNick == "" || mpl.GameUserNick == null){}}protected override string GetModeName (int id){}public bool IsLocalSameType(IEntity entity){if(PlayerManager.Instance.LocalPlayer.EntityCamp != entity.EntityCamp)return false;return true;}public void CleanAccount(){for (int i = AccountDic.Count - 1; i >= 0; i--) {AccountDic.Remove (AccountDic.ElementAt(i).Key);}					 }public void RemoveAccountBySeat(uint seat){for (int i = AccountDic.Count - 1; i >= 0; i--) {if (AccountDic.ElementAt(i).Value.GameUserSeat != seat)continue;	AccountDic.Remove (AccountDic.ElementAt(i).Key);break;}					 }public void CleanPlayerWhenGameOver() {foreach (var item in AccountDic.Values) { item.CleanWhenGameOver();}}}

以上的例子,说明了可以在此框架的基础上继续扩展,当然也可以修改。以上实现了对外接口的编写,这样整个角色系统就完成了。


技能系统

在游戏中,技能特效是伴随着角色动作播放的,角色动作可以使用 FSM 播放,技能就是将动作和特效合在一起播放,看一下技能模块设计图:
在这里插入图片描述


父类IEffect

IEffect 模块,所有的技能肯定有共同的属性,为了避免属性被重复的定义,我们将其放到一个父类中,该父类就是 IEffect。首先游戏中会有很多技能,这么多技能如何区分,这就涉及到一个技能类型的定义,技能类型定义可以使用字符串,也可以使用枚举。这里使用枚举表示:

    public enum ESkillEffectType{eET_Passive,eET_Buff,eET_BeAttack,eET_FlyEffect,eET_Normal,eET_Area,eET_Link,}

技能还有一些共同的属性和方法,先定义属性,比如特效的运行时间、资源路径、生命周期、技能释放者和受击者、播放的音效等。这些我们可以自己根据需求去定义:

    //基本信息    public GameObject obj = null;           //特效物体public Transform mTransform = null;protected float currentTime = 0.0f;     //特效运行时间public bool isDead = false;             //特效是否死亡public string resPath;                  //特效资源路径        public string templateName;             //特效模板名称public Int64 projectID = 0;             //特效id  分为服务器创建id 和本地生成id        public uint skillID;                    //特效对应的技能idpublic float cehuaTime = 0.0f;          //特效运动持续时间或者是特效基于外部设置的时间    策划配置      public float artTime = 0.0f;            //美术设置的特效时间                            美术配置public float lifeTime = 0;              //特效生命周期, 0为无限生命周期public UInt64 enOwnerKey;               //技能释放者public UInt64 enTargetKey;              //技能受击者        public AudioSource mAudioSource = null; //声音//运动信息public Vector3 fPosition;public Vector3 fixPosition;public Vector3 dir;public Vector3 distance;public ESkillEffectType mType;

共同属性定义完了,下面定义它的共同方法。要使用特效,首先要创建特效:

    //特效创建接口public void Create(){//创建的时候检查特效projectId,服务器没有设置生成本地idCheckProjectId();//获取特效模板名称templateName = ResourceCommon.getResourceName(resPath);//使用特效缓存机制           obj = GameObjectPool.Instance.GetGO(resPath);if (null == obj){Debugger.LogError("load effect object failed in IEffect::Create" + resPath);return;}//创建完成,修改特效名称,便于调试obj.name = templateName + "_" + projectID.ToString();OnLoadComplete();//获取美术特效脚本信息                effectScript = obj.GetComponent<EffectScript>();if (effectScript == null){Debugger.LogError("cant not find the effect script in " + resPath);return;}artTime = effectScript.lifeTime;//美术配置时间为0,使用策划时间if (effectScript.lifeTime == 0)lifeTime = cehuaTime;//否则使用美术时间elselifeTime = artTime;//特效等级不同,重新设置EffectLodLevel effectLevel = effectScript.lodLevel;EffectLodLevel curLevel = EffectManager.Instance.mLodLevel;if (effectLevel != curLevel){//调整特效显示等级AdjustEffectLodLevel(curLevel);}}

该函数的实现流程是先加载特效,也是从对象池生成,并且将其重新命名,然后调用函数 OnLoadComplete() 去设置特效的发射点,因为特效发射点要根据不同的技能去设置,所以在 IEffect 类中只是定义了一个它的虚函数:

    public virtual void OnLoadComplete(){}

爱他的具体的功能要在特效子类中去实现,当然 IEffect 类并不是只有这一个函数实现,其他的功能函数可以根据需求自己去定义了。


子类中的部分实现

特效父类 IEffect 已定义完成,接下来就要编写具体的子类了,根据需求可以扩展下去。我们先拿出 BeAttackEffect 被动技能举例说明,具体实现一下 OnLoadComplete:

    public override void OnLoadComplete(GameObject obj){//判断enTargetIEntity enTarget;EntityManager.AllEntitys.TryGetValue(enTargetKey, out enTarget);if (enTarget != null && obj != null){//击中点Transform hitpoit = enTarget.RealEntity.transform.FindChild("hitpoint");if (hitpoit != null){//设置父类和位置GetTransform().parent = hitpoit;GetTransform().localPosition = new Vector3(0.0f, 0.0f, 0.0f);}}if (skillID == 0){return;}}

该函数是先在表里查找,如果找到了,则去查找对象的虚拟点,然后将该虚拟点设置给特效。


管理类EffectManager

这么多的技能,我们同样也需要一个管理器 EffectManager 类,用于对外提供创建特效接口:

        //创建基于时间的特效public BeAttackEffect CreateTimeBasedEffect(string res, float time, IEntity entity){if (res == "0")return null;BeAttackEffect effect = new BeAttackEffect();//加载特效信息effect.skillID = 0;             //技能id=0     effect.cehuaTime = time;effect.enTargetKey = entity.GameObjGUID;effect.resPath = res;           //创建effect.Create();AddEffect(effect.projectID, effect);return effect;}

该函数对应的是 BeAttackEffect,将它初始化后,调用 Create 创建加载特效,为了便于管理我们调用了函数 AddEffect,目的是将特效加到表中:

    //添加特效到EffectMap表public void AddEffect(Int64 id, IEffect effect){if (!m_EffectMap.ContainsKey(id)){m_EffectMap.Add(id, effect);}else{Debug.LogError("the id: " + id.ToString() + "effect: " + effect.resPath + "has already exsited in EffectManager::AddEffect");}}
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Nginx教程---01.Nginx入门

    create by 三七二十一 LZ参考视频(年代久远,但万变不离其宗): 链接:https://pan.baidu.com/s/1O_MmN0c3ckM6vbk08n8Qkg 密码:z9zr01_Nginx入门 nginx -高性能Web服务器 一、基础篇 1.Nginx介绍<br> 2.Nginx编译安装<br> 3.Nginx信号控制<br> 1、Nginx介绍…...

    2024/4/17 0:43:16
  2. 数据结构学习笔记-队列长度的计算

    1.通用计算公式:l=(rear-front+n)%n其中:l为当前队列的长度rear为队列尾指针front为队列头指针n为队列可容纳的元素总数(即队列大小)2.公式解析队列中存在一种特殊情况:循环队列,一般定义循环队列的头指针front和尾指针rear均指向队列下标为0的位置,此时front=rear&…...

    2024/4/17 0:43:40
  3. Clementine完整教程

    Clementine完整教程...

    2024/4/17 0:43:41
  4. mogodb日常工作记录

    查询相关 db.getCollection(Examda_News_VisitLog).find({"newsId":"20061109480192959"})db.getCollection(Examda_News_VisitLog).find({"from":"xcx","share":{"$gt":0}}).limit(10); # status: "A"…...

    2024/4/18 6:24:37
  5. JAVA中使用正则匹配json中指定字段的值

    json字符串如下:{"page_size":0,"page_index":0,"page_count":0,"has_next":false,"datas":null,"code":0,"issuccess":true,"msg":null} 解析如下:String regex="\"code\"…...

    2024/4/17 0:45:10
  6. 游戏素材网站推荐!!!

    图像素材:http://www.tekepon.net/fsm/ FSM,主要提供2K素材http://www.mogunet.net/~mack/looseleaf/index.html Loose Leaf,提供XP素材http://theodore.ikebukuro.cc/ Teddy-Plaza,主要提供2K素材,XP少量http://homepage2.nifty.com/nakkuruma…...

    2024/4/17 0:45:11
  7. 最新基于adt-bundle-windows-x86的android开发环境搭建

    本文所使用到的几个名词解释:  1、ADT:AndroidDevelopment Tools 指android开发工具,它eclipse的一个插件,使eclipse与sdk联系起来。  2、jre:java runtimeenvironment 指java运行环境,包含了jvm,即:java虚拟机,正因为有了jvm才实现了java语言的跨平台。  3、jdk:…...

    2024/4/4 22:06:29
  8. IPA反编译

    ...

    2024/4/17 0:45:16
  9. Lua脚本反编译入门之一

    随着越来越多的游戏,软件采用Lua来实现业务逻辑,想搞黑产的同学,时常便会遇见lua脚本,可惜大部分都是编译过的lua脚本,而且还是自定义的。便难倒了很多菜鸟,lua 的实现机制,那可是虚拟机技术,非常难于调试。本教程,便来普及lua 的虚拟机指令及其反编译lua脚本,成为文…...

    2024/4/19 21:10:59
  10. 【原创】mysql正则匹配中文…

    含中文: where name regexp [^ -~] 不含中文: where name not regexp ^[^ -~] 后来说需要查询日文 参考资料得知:utf-8 (unicode) /u4e00-/u9fa5 (中文) /x3130-/x318f (韩文) /xac00-/xd7a3 (朝鲜文) /u0800-/u4e00 (日文)汉字正则: e[4-9][0-9a-f]{4} 含中文:where hex…...

    2024/4/18 6:39:43
  11. ENVI5.3安装教程(含软件下载)

    重要提示:各位安装目录和授权文件存放目录一定不要有中文!!!!!软件下载地址链接:https://pan.baidu.com/s/1PkwCw1xsAfLmaCOCoSr0mg 提取码:qlu8上文地址如果过期或者不能下载的话可以使用此链接http://pan.baidu.com/s/1eRVvk8i,或者艾特博主。最新地址http://www.j…...

    2024/4/17 0:45:04
  12. 蒙哥马利模乘

    Montgomery乘法的数学表达式是A * B * R ^ (-1)mod M。A、B是同位长大数,R是2的M(位长)的次方,R ^ (-1)是指R相对于M的模逆,即R ^ (-1)满足以下条件的数:R * R ^ (-1) mod M = 1;这个条件成立的充要条件是R与M互素,这一点只需要M为奇数即可。 使用蒙哥马利乘法可以做…...

    2024/4/17 0:44:40
  13. Nginx入门(下载安装|基础配置)

    Nginx 下载地址 http://tengine.taobao.org Nginx安装 1. 执行tar zxvf Nginx,tar 命令解压 2. 执行./configure 检查是否具备安装环境 1). gcc环境 安装Nginx需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有gcc环境,需要安装gcc: yum ins…...

    2024/4/17 0:46:22
  14. Ubuntu反编译apk教程

    [TOC] 今日需参考ROM厂商部分APK,故查阅走坑以做记录工具除了参考资源有时候需要参考别人的代码,此时需要做以下准备工具(自备梯子)工具作用下载地址ApkToolGoolge官方提供的APK反编译工具十分强大, 是以下几个工具的综合体https://ibotpeaches.github.io/Apktool/instal…...

    2024/4/20 3:58:41
  15. 如何利用Shader来渲染游戏中的3D角色

    杨航最近在学Unity3D本文主要介绍一下如何利用Shader来渲染游戏中的3D角色,以及如何利用Unity提供的Surface Shader来书写自定义Shader。一、从Shader开始1、通过Assets->Create->Shader来创建一个默认的Shader,并取名“MyShader”。Unity3D教程:3D角色的渲染2、将…...

    2024/4/20 11:17:48
  16. 博途PLC软件的基本操作(最基础)

    1.打开TIA PorrtalV14软件,创建项目 2.点击项目视图,并“添加新设备” 3.共添加了两个设备,第一个很容易找到并添加,第二个如下图方法添加配置设备的ip上传成功最后来个吐槽,CSDN上传图片太麻烦了,弄着弄着很慢浪费时间都不想再写下去,但最后为了以后自己查询方便还是坚…...

    2024/4/19 18:02:45
  17. K8S-05-Jenkins部署springCloud项目-gateway

    Jenkins部署springCloud项目-gateway代码结构及文件Jenkins配置 代码结构及文件application.yml文件内容server:port: 9527 spring:application:name: cloud-gatewaycloud:nacos:discovery:server-addr: 192.168.0.31:8848gateway:discovery:locator:enabled: trueroutes:- id:…...

    2024/4/19 9:02:23
  18. nginx入门安全优化--隐藏版本号的两种方式

    方式一:基于修改配置文件 # vim /usr/local/nginx/conf/nginx.conf//编辑主配置文件 //第二十行添加 http {include mime.types;default_type application/octet-stream;server_tokens off;# service nginx reload //重启服务# curl -I http://192.168.1…...

    2024/4/17 0:43:40
  19. mysql8 windows版密码忘记如何重新设置?

    本机安装位置:D:\Program Files (x86)\java\mysql-8.0.13-winx64重设密码具体过程:还好有一个navicat的连接可以打开,查看其中的用户名和主机名称(一般默认是这个,改了又忘了的话我也没办法了)打开cmd,用管理员的身份运行,先关闭mysql(net stop mysql);然后进入mysql8所…...

    2024/4/17 0:44:46
  20. 高质量PyMOL作图教程

    前言 上一期,小编教大家使用殷赋云计算平台(http://cloud.yinfotek.com)重现了一篇2018年文献《【文献重现】D715-2441 抑制剂与PB2蛋白的结合模式研究》的分子对接计算结果。然而,要发表高质量SCI文章,还需要制作高质量的Figure。本期,我将带大家使用PyMOL软件(搭配Pho…...

    2024/4/18 13:49:43

最新文章

  1. tsconfig.json 常用属性配置和注释

    下面是一个详细的 tsconfig.json 文件示例&#xff0c;其中包含了许多常用的配置选项。这个配置适用于一个使用 TypeScript 进行前端和后端开发的通用项目。 {"compilerOptions": {"target": "es6", // 指定 ECMAScri…...

    2024/4/26 22:25:58
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 【LeetCode热题100】【二叉树】二叉树的中序遍历

    题目链接&#xff1a;94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 中序遍历就是先遍历左子树再遍历根最后遍历右子树 class Solution { public:void traverse(TreeNode *root) {if (!root)return;traverse(root->left);ans.push_back(root->val);tra…...

    2024/4/23 6:15:26
  4. Golang Gin框架

    1、这篇文章我们简要讨论一些Gin框架 主要是给大家一个基本概念 1、Gin主要是分为路由和中间件部分。 Gin底层使用的是net/http的逻辑&#xff0c;net/http主要是说&#xff0c;当来一个网络请求时&#xff0c;go func开启另一个协程去处理后续(类似epoll)。 然后主协程持续…...

    2024/4/23 6:22:17
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/25 18:38:39
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/25 18:39:23
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/25 18:39:22
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/25 18:39:22
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/26 21:56:58
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/25 16:48:44
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/26 16:00:35
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/26 22:01:59
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/25 18:39:12
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/25 18:38:58
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/25 18:38:57
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57