设计敌人血条模块
在游戏中我们经常需要动态控制血条的渲染,比如在黑魂中遇到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许可协议,转载请注明!