第一部分:人物功能实现——角色受伤与敌人处决

一:角色受伤

        1:攻击定义

        对于造成伤害的武器,为其赋予一个AttackDefination的脚本,其中定义了攻击的来源角色,攻击的基本伤害,体力衰减,为武器添加Trigger碰撞体,在脚本中利用OnTriggerEnter函数触发人物角色TakeDamage函数。

[RequireComponent(typeof(Collider))] 
public class AttackDefinition : MonoBehaviour
{
    public float attackBaseDamage;

    public float energyAmount;         //如果对方是敌人 或者 对方正在普通格挡 会造成对方的体力消耗

    public CharacterStats attacker;

    public DamageType damageType;

    public float DamageAmount => attackBaseDamage + damageType switch
    {
        DamageType.Physical => attacker.CurPhysicalDamage,
        DamageType.Magical => attacker.CurMagicDamage,
        DamageType.True => 0,
        _ => 0
    };


    private void OnTriggerEnter(Collider other)
    {
        other.GetComponent<CharacterStats>()?.TakeDamage(this);

        //碰撞体挂载在子物体上的特殊情况
        if (other.GetComponentInParent<MetalonController>())
            other.GetComponentInParent<CharacterStats>()?.TakeDamage(this);
    }
}

在角色的状态控制器CharacterStats中定义TakeDamge函数:由于涉及到无敌,格挡,音效,虚弱状态,敌人和主角的逻辑不完全相同,暂时分开完成。

通用的部分:

   public virtual void TakeDamage(AttackDefinition attacker)
    {
        if (isDead) return;
        if (IsInvincible) return;
        //设置无敌
        InvincibleAfterHit = true;
        invincibleAfterHitTimeCounter = InvincibleTimeAfterHit;
        //播放音效:受击音效 或者 格挡音效
        PlayHitAudio();
        //后续内容在子类中实现:人物转向 实际受到伤害,格挡的影响,是否进入虚弱状态
    }

在PlayHitAudio中,根据Hit的状态分成3中:受伤,普通格挡,完美格挡。

后续的部分主要是计算受到的伤害,如果死亡,则触发OnDeath事件;如果受伤,则触发OnHurt事件;如果体力到0,则进入虚弱状态,规定虚弱状态可以被处决。

主角的受伤逻辑:规定完美格挡不掉血不掉体力,普通格挡掉的血量减少,但是会消耗体力。

(格挡的实现在后面章节说明)

    public override void TakeDamage(AttackDefinition attacker)
    {
        //如果当前人物不是处于锁定状态 则转向
        if (playerController.GetCurrentLockEnemy == null)
        {
            Vector3 attackerPos = attacker.attacker.transform.position;
            Quaternion toRotation = Quaternion.LookRotation(
                    new Vector3(attackerPos.x, transform.position.y, attackerPos.z) - transform.position);
            StartCoroutine(RotateToAttacker(toRotation));
        }
        
        //通过玩家的动画判断是否处于完美格挡
        IsPerfectGuard = playerAnimationInfo.IsPerfectParry();
        base.TakeDamage(attacker);
        //执行 掉血 + OnTakeDamage事件
        float costHealth = attacker.DamageAmount - attacker.damageType switch
        {
            DamageType.Physical => CurPhysicalDefensive,
            DamageType.Magical => CurMagicalDefensive,
            DamageType.True => 0,
            _ => 0
        };
        costHealth = Mathf.Clamp(costHealth, 0, MaxHealth);
        //如果当前处于格挡状态
        if (IsGuard)
        {
            //如果是完美格挡的话:不掉血 + 不掉体力
            if (IsPerfectGuard) costHealth = 0;
            //普通格挡:只掉原先1/5的血,但会消耗体力 
            else
            {
                costHealth /= 3;
                if (!IsExecuted)
                    ExpendEnergy(attacker.energyAmount);
            }
        }
        if (costHealth >= CurHealth)
        {
            CurHealth = 0;
            isDead = true;
            OnDie?.Invoke();
            return;
        }
        else
        {
            CurHealth -= costHealth;
            OnTakeDamage?.Invoke();
        }
        //如果角色还没有死亡,且体力值为0,则进入为期1秒的虚弱状态,敌人处于虚弱状态时可以被处决
        if (CurEnergy == 0 && !IsWeakState)
        {
            IsWeakState = true;
            PoolManager.Instance.TakeGameObject("Timer").GetComponent<Timer>().CreateTime(2f, () =>
            {
                IsWeakState = false;
            });

        }
    }

敌人的受伤逻辑:大差不差,暂时没有给敌人设置完美格挡。

    public override void TakeDamage(AttackDefinition attacker)
    {
        base.TakeDamage(attacker);
        Vector3 attackerPos = attacker.attacker.transform.position;
        Quaternion toRotation =
            Quaternion.LookRotation(
                new Vector3(attackerPos.x, transform.position.y, attackerPos.z) - transform.position);
        StartCoroutine(RotateToAttacker(toRotation));
        //执行 掉血 + OnTakeDamage事件
        float costHealth = attacker.DamageAmount - attacker.damageType switch
        {
            DamageType.Physical => CurPhysicalDefensive,
            DamageType.Magical => CurMagicalDefensive,
            DamageType.True => 0,
            _ => 0
        };
        costHealth = Mathf.Clamp(costHealth, 0, MaxHealth);
        //如果当前处于格挡状态(默认为普通格挡)
        if (IsGuard) costHealth /= 3;
        //如果不处于被处决的状态 (敌人默认直接扣除体力)
        if (!IsExecuted)
            ExpendEnergy(attacker.energyAmount);
        if (costHealth >= CurHealth)
        {
            CurHealth = 0;
            isDead = true;
            //GlobalEvent.CallOnEnemyDeath(enemy); 已经再敌人动画结束后调用
            OnDie?.Invoke();
        }
        else
        {
            CurHealth -= costHealth;
            OnTakeDamage?.Invoke();
        }
        enemyStatsBarUI.UpdateStats(CurHealth, MaxHealth,CurEnergy,MaxEnergy);
        //如果角色还没有死亡,且体力值为0,则进入为期1秒的虚弱状态,敌人处于虚弱状态时可以被处决
        if (!isDead && CurEnergy == 0 && !IsWeakState) 
        {
            IsWeakState = true;
            //将该敌人加入到虚弱敌人表中
            GameManager.Instance.AddWeakEnemy(enemy);
            GlobalEvent.CallEnemyEnterWeakState(enemy);
            PoolManager.Instance.TakeGameObject("Timer").GetComponent<Timer>().CreateTime(2f, () => 
            {
                if(enemy!=null)
                {
                    if (!enemy.IsExecuted)
                    {
                        IsWeakState = false;
                        GlobalEvent.CallEnemyExitWeakState(enemy);
                        CurEnergy = MaxEnergy; 
                    }
                    GameManager.Instance.RemoveWeakEnemy(enemy);
                } 
            });

        }
    }

说明:GOPoolManager是对象池,从中拿取了一个计时器,简化延迟操作的逻辑。

二:处决敌人

在GameManager中保存两个哈希集合:

 public HashSet<EnemyController> enemies;         //记录所有的敌人

 public HashSet<EnemyController> weakEnemies;  //记录所有处于虚弱状态的敌人

其中enemies用于实现锁定功能,weakEnemies用于实现处决功能。

在上述敌人的受伤逻辑中,如果敌人的体力降到0之后会进入一个虚弱的状态,并且加入到weakEnemies中。

在人物的普通攻击判断中,额外判断是否能够进行处决

//判定是否能够对于处于虚弱状态的敌人进行处决
        if (SelectWeakEnemy() != null) 
        {
            //执行实际的处决
            anim.SetTrigger("Execution");
        }

SelectWeakEnemy用于实现查找位于角色前方,且距离小于某个值的处于虚弱状态的敌人:

(查找规则:在满足距离的条件下,优先选择角度最接近的敌人)

//综合判断是否有敌人可以被处决
    private EnemyController SelectWeakEnemy()
    {
        //具体要求:距离小于某个值 + 角度为人物的前方   (根据角度选择最佳的目标) 
        EnemyController currentEnemy = null;
        float minAngle = 180;
        foreach(var enemy in GameManager.Instance.weakEnemies)
        {
            if(Vector3.Distance(transform.position, enemy.transform.position) < 3f
                && transform.IsFacingTarget(enemy.transform))
            {
                //enemy满足要求 比较得到更好的敌人
                if (currentEnemy == null) currentEnemy = enemy;
                else
                {
                    if (Vector3.Angle(transform.position, enemy.transform.position) < minAngle)
                        currentEnemy = enemy;
                }
            }
        }
        if(currentEnemy!=null)
        {
            currentEnemy.IsExecuted = true;
            
        }
        return currentEnemy;
    }

在查找到虚弱敌人后,进行处决操作,同时设置敌人的处决状态:

    public bool IsExecuted { get => isExecuted;
        set
        {
            isExecuted = value;
            if (anim != null) anim.SetBool("IsExecuted", value);
            if (value)
            {
                GOPoolManager.Instance.TakeGameObject("Timer").GetComponent<Timer>().CreateTime(4f,
                () => { IsExecuted = false;enemyCharacterStats.IsWeakState = false; IsHurt = false; }) ;
            }
        }
    }

上述代码实现了最简单的处决。具体的处决动画,敌人被处决动画可以自行决定,参数和处决方式也可以进行额外的扩充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值