设计敌人血条模块
在游戏中我们经常需要动态控制血条的渲染,比如在黑魂中遇到Boss时屏幕下方会显示Boss血量条或者是在只狼中Boss对战时屏幕左上角的属性条。在原神中攻击敌人后显示敌人血量条,停止攻击一段时间或敌人失去攻击状态后血量条消失。它们的血量条都是不同的。
假如我们按照上面的例子,给Boss和小兵设计出不一样表现的血量条。分析可以看出它们在渲染显示部分存在差异、而在逻辑上是存在共性部分的。可以抽象出HpBarEntity血条实体的概念,抽象会变化的部分,提取出共性部分,具体实现渲染表现部分。代码如下:
public class HpBarEntity : MonoBehaviour { [SerializeField] private UILabel _nameLabel; [SerializeField] private UIProgressBar _UISlider; private Transform followTarget; public void SetFollowHead(Transform trans) { followTarget=trans; } public void Update() { if (followTarget) { transform.position = CameraMgr.WorldPointToNGUIWorldPoint(followTarget.position+Vector3.up*0.5f); } } public void SetInfo(string entityName,IDamageable damageable) { _nameLabel.text = entityName; UpdateHpBar(damageable); } public void UpdateHpBar(IDamageable damageable) { _UISlider.value = damageable.hp*1.0f / damageable.MaxHP; } }
上面代码中HpBarEntity是血量条的基类提取出了设置属性、更新血条、血条跟随目标对象的行为。血条跟随目标对象这个可能不是每个血条共有的行为,但是考虑再抽象出一个类型会使结构变得复杂,抽象的意义也不是很大,显得很鸡肋。 所以血条是否跟随目标对象取决于 是否设置了目标对象。
对于特殊的血量条,我们可以继续扩展,如主角玩家血量条。主角玩家血量条一般除了血量属性,还存在体力、能量等表现。可以考虑直接在基础上扩展、或者是抽象出属性条、体力条,使用组合模式来控制各个属性的表现。
除了设计血条的类结构,我们还需要考虑血条的复用。游戏中一般每个敌人头上都有血量条,我们其实可以复用血量条表现一致的游戏对象,来降低创建和销毁带来的性能开销。接下来要说的对象池想必大家也很熟悉了,关于对象池可以浏览文章:通用化的对象池设计了解。应用对象池并结合我的项目来看看我们如何复用血量条,代码如下:
public Dictionary<IDamageable, HpBarEntity> hpbarEntityDics = new Dictionary<IDamageable, HpBarEntity>();//血量条实体字典
//获取当前角色对象挂载的血量条实体 public HpBarEntity GetHpBarEntity(IDamageable damageable) { HpBarEntity hpBarEntity; hpbarEntityDics.TryGetValue(damageable, out hpBarEntity); return hpBarEntity; } //获取当前角色对象挂载的血量条实体或申请得到一个新的血量条 public HpBarEntity ApplyHpBarEntity(IDamageable damageable) { HpBarEntity hpBarEntity; hpbarEntityDics.TryGetValue(damageable,out hpBarEntity); if (hpBarEntity==null) { //从对象池中取出 hpBarEntity = GameObjectFactory.Instance.PopItem(PathDefine.UIItemPath+Constants.HpBarEntity).GetComponent<HpBarEntity>(); if (hpBarEntity!=null) { hpbarEntityDics.Add(damageable,hpBarEntity); //获取头顶位置 hpBarEntity.SetFollowHead(damageable.characterFacade.actorPhyiscal.GetCheckPoint(CheckPointType.Head)); } } return hpBarEntity; } //从一个角色对象上移除血量条 public void RemoveHpBarEntity(IDamageable damageable) { if (hpbarEntityDics.ContainsKey(damageable)) { HpBarEntity hpBarEntity=hpbarEntityDics[damageable]; hpBarEntity.SetFollowHead(null); hpbarEntityDics.Remove(damageable); //压入对象池 GameObjectFactory.Instance.PushItem(hpBarEntity.gameObject); } }
控制血量条的显示
敌人受伤时显示血量条-IDamageable类Damage方法
protected virtual void Damage(DamageData damageData) { if (IsDie) { return; } attackerList.Clear(); attackerList.Add(damageData.attacker.GetComponent<IDamageable>()); hp -= damageData.damage; HpBarEntity _HpBarEntity = characterFacade.ApplyHpBarEntity();//获取血量条 _HpBarEntity?.SetInfo(actorSystem.roleDefinition.object_Name, this);//设置血量信息 _HpBarEntity?.gameObject?.SetActive(true);//激活 _HpBarEntity?.UpdateHpBar(this);//更新 if (hp <= 0) { Die(damageData); } PlayHitEffectAudio(IsDie); }
敌人死亡
public virtual void Die(DamageData damageData) { hp = 0; BattleManager.Instance.RemoveHpBarEntity(this);//关闭血量条 dieAction?.Invoke(damageData); }
敌人网格体-渲染移入移除时控制血量条
关于渲染移入移除接口,可见文章进入或退出相机渲染范围内-血条动态显示
public class EnemyController: NPCController { private int inVisible_CheckBarTid=-1; //public MyTimer hitAfterLookAttackWait=new MyTimer();//多少秒后锁定攻击者 public override void Awake() { base.Awake(); } //进入相机渲染范围 public override void OnRenderVisible() { base.OnRenderVisible(); HpBarEntity barEntity = BattleManager.Instance.GetHpBarEntity(damageable); if (barEntity) { barEntity.SetInfo(actorSystem.roleDefinition.object_Name,damageable); barEntity.gameObject.SetActive(true); } } //移出相机渲染范围 public override void OnRenderInVisible() { base.OnRenderInVisible(); HpBarEntity barEntity = BattleManager.Instance.GetHpBarEntity(damageable); if (barEntity) { barEntity.gameObject.SetActive(false); if (inVisible_CheckBarTid!=-1) { GameRoot.Instance.DelTimeTask(inVisible_CheckBarTid); } inVisible_CheckBarTid=GameRoot.Instance.AddTimeTask(5000, () => { if (renderStateListener.isRenderObject==false) { BattleManager.Instance.RemoveHpBarEntity(damageable); inVisible_CheckBarTid = - 1; } }); } } }
来源:麦瑞克博客
链接:https://www.playcreator.cn/archives/unity/159/
本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议,转载请注明!