自学内容网 自学内容网

Unity类银河战士恶魔城学习总结(P120 BUff Item Effect各种增益效果)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili

教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/

演示:给冰与火之歌加上了持续火焰伤害的buff,这样武器后续攻击到的敌人就会收到火焰伤害

Buff_Effect.cs

实现了可以自由创建物品效果,并且通过添加buff来赋予属性

1. 枚举 StatType 的定义

2. Buff_Effect 类的功能

 继承自 ItemEffect,并通过 CreateAssetMenu 特性使其可以在Unity编辑器中创建实例。Buff_Effect 类的主要功能是应用Buff效果来暂时提升玩家角色的某个属性。

3. ExcuteEffect 方法

  • 目的:该方法是核心逻辑,用来执行Buff效果,临时增加玩家某个属性。
  • 过程
    1. 获取当前玩家角色的 PlayerStats 组件,用来访问和修改角色的各项属性。
    2. 通过调用 stats.IncreaseStatBy(),给某个特定的属性增加指定数值,并设置Buff的持续时间。

4. StatToModify 方法

  • 目的:根据 buffType 返回需要修改的属性。
  • 实现:通过一系列 if-else 条件检查,匹配 buffType 枚举值,找到玩家角色对应的属性(例如敏捷、智力等)。如果匹配到 StatType.agility,则返回玩家的敏捷属性 stats.agility
  • 结果:根据不同的Buff类型,该方法会返回玩家角色的相应属性,以便在 ExcuteEffect 中对这个属性施加增益效果。
using UnityEngine;

//2024年11月11日

public enum StatType//枚举 StatType 的定义
{
    stealth,
    agility,
    intelligence,
    vitality,
    damage,
    critChance,
    critPower,
    health,
    armor,
    evasion,
    magicRes,
    fireDamage,
    iceDamage,
    lightingDamage
}




[CreateAssetMenu(fileName = "Buff Effect", menuName = "Data/Item effect/Buff effect")]
public class Buff_Effect : ItemEffect
{
    private PlayerStats stats;
    [SerializeField] private StatType buffType;// Buff 的类型
    [SerializeField] private int buffAmount; // Buff 的增加量
    [SerializeField] private float buffDuration;// Buff 持续时间


    public override void ExcuteEffect(Transform _enemyPositon)
    {
        stats = PlayerManager.instance.player.GetComponent<PlayerStats>();//获取当前玩家角色的 PlayerStats 组件,用来访问和修改角色的各项属性。
        stats.IncreaseStatBy(buffAmount, buffDuration, StatToModify());
    }


    private Stat StatToModify()//根据 buffType 返回需要修改的属性。
    {
        if (buffType == StatType.stealth) return stats.strength;
        else if (buffType == StatType.agility) return stats.agility;
        else if (buffType == StatType.intelligence) return stats.intelligence;
        else if (buffType == StatType.vitality) return stats.vitality;
        else if (buffType == StatType.damage) return stats.damage;
        else if (buffType == StatType.critChance) return stats.critChance;
        else if (buffType == StatType.critPower) return stats.critPower;
        else if (buffType == StatType.health) return stats.maxHealth;
        else if (buffType == StatType.armor) return stats.armor;
        else if (buffType == StatType.evasion) return stats.evasion;
        else if (buffType == StatType.magicRes) return stats.magicResistance;
        else if (buffType == StatType.fireDamage) return stats.fireDamage;
        else if (buffType == StatType.iceDamage) return stats.iceDamage;
        else if (buffType == StatType.lightingDamage) return stats.lightningDamage;

        else return null;
    }

}

CharacterStats.cs

添加部分

在Update()下方添加这个函数,和Update()平行

    public virtual void IncreaseStatBy(int _modifier,float _duration,Stat _statToModify)
    {
        StartCoroutine(StatModCoruntine(_modifier, _duration, _statToModify));
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//10月25日
//10月26日
public class CharacterStats : MonoBehaviour
{
    private EntityFX fx;


    [Header("主属性")]
    public Stat strength;//力量,1点增加1攻击力和%1爆伤
    public Stat agility;//敏捷,1点增加1%闪避和%1暴击率
    public Stat intelligence;//智力,1点增加1法术强度和%1魔抗
    public Stat vitality;//活力,1点增加3生命值


    [Header("攻击属性")]//offensive stats
    public Stat damage;
    public Stat critChance;//暴击率
    public Stat critPower;//暴击伤害,默认%150



    [Header("防守属性")]//defensive stats
    public Stat maxHealth;
    public Stat armor;//护甲
    public Stat evasion;//闪避
    public Stat magicResistance;//魔抗


    [Header("魔法属性")]//magic stats
    public Stat fireDamage;
    public Stat iceDamage;
    public Stat lightningDamage;


    public bool isIgnited;//是否燃烧,持续伤害
    public bool isChilled;//是否冻结,削弱护甲20%
    public bool isShocked;//是否感电,减少命中率20%


    [SerializeField] private float ailmentsDuration = 4;//异常状态持续时间
    private float ignitedTimer;
    private float chilledTimer;
    private float shockedTimer;



    private float igniteDamageCoolDown = .3f;//燃烧伤害间隔时间
    private float igniteDamageTimer;//燃烧伤害计时器
    private int igniteDamage;//燃烧伤害
    [SerializeField] private GameObject shockStrikePrefab;
    private int shockDamage;


    public int currentHealth;

    public System.Action onHealthChanged;
    public bool isDead { get; private set; }



    protected virtual void Start()
    {
        critPower.SetDefaultValue(150);//暴击伤害默认150%
        currentHealth = GetMaxHealthValue();//一开始血条满的

        fx = GetComponent<EntityFX>();
    }


    protected virtual void Update()
    {
        ignitedTimer -= Time.deltaTime;//燃烧时间减少
        chilledTimer -= Time.deltaTime;
        shockedTimer -= Time.deltaTime;

        igniteDamageTimer -= Time.deltaTime;//燃烧伤害计时器减少


        if (ignitedTimer < 0)
            isIgnited = false;


        if (chilledTimer < 0)
            isChilled = false;


        if (shockedTimer < 0)
            isShocked = false;

        if(isIgnited)
            ApplyIgniteDamage();
    }



    public virtual void IncreaseStatBy(int _modifier,float _duration,Stat _statToModify)
    {
        StartCoroutine(StatModCoruntine(_modifier, _duration, _statToModify));
    }


    private IEnumerator StatModCoruntine(int _modifier, float _duration, Stat _statToModify)//加buff的协程
    {
        _statToModify.AddModifier(_modifier);//添加一个buff

        yield return new WaitForSeconds(_duration);

        _statToModify.RemoveModifier(_modifier);//移除一个buff
    }


    public virtual void DoDamage(CharacterStats _targetStats)//只是一次物理攻击
    {
        if (TargetCanAvoidAttack(_targetStats))
            return;

        int totalDamage = damage.GetValue() + strength.GetValue();

        if (Cancrit())
        {
            //Debug.Log("Crit Hit");
            totalDamage = CalculateCriticalDamage(totalDamage);
            //Debug.Log(" 总的暴击伤害是"+ totalDamage);//Total crit damage is
        }


        totalDamage = CheckTargetArmor(_targetStats, totalDamage);
        _targetStats.TakeDamage(totalDamage);//把造成的给到继承CharacterStats的类


        DoMagicDamage(_targetStats);//如果普通攻击不想要魔法伤害移除

    }




    #region Magic Damage and ailments

    public virtual void DoMagicDamage(CharacterStats _targetStats)//只是一次魔法攻击
    {

        int _fireDamage = fireDamage.GetValue();
        int _iceDamage = iceDamage.GetValue();
        int _lightningDamage = lightningDamage.GetValue();



        int totalMagicalDamage = _fireDamage + _iceDamage + _lightningDamage + intelligence.GetValue();
        totalMagicalDamage = CheckTargetResistance(_targetStats, totalMagicalDamage);

        _targetStats.TakeDamage(totalMagicalDamage);//把造成的给到继承CharacterStats的类


        //important
        if (Mathf.Max(_fireDamage, _iceDamage, _lightningDamage) <= 0)//可以保证下面的while循环不会无限循环
            return;


        AttempToApplyAilements(_targetStats, _fireDamage, _iceDamage, _lightningDamage);

    }

    private  void AttempToApplyAilements(CharacterStats _targetStats, int _fireDamage, int _iceDamage, int _lightningDamage)
    {
        //判断魔法伤害类型
        bool canApplyIgnite = _fireDamage > _iceDamage && _fireDamage > _lightningDamage;
        bool canApplyChill = _iceDamage > _fireDamage && _iceDamage > _lightningDamage;
        bool canApplyShock = _lightningDamage > _fireDamage && _lightningDamage > _iceDamage;



        while (!canApplyIgnite && !canApplyChill && !canApplyShock)
        {
            //三个if同时判断大小,可以完成一个随机属性伤害的boss
            if (Random.value < .33f && _fireDamage > 0)//Random.value用于生成一个介于 0.0 和 1.0 之间的随机浮点数
            {
                canApplyIgnite = true;
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                //Debug.Log("ignited" );
                return;
            }

            if (Random.value < .5f && _lightningDamage > 0)
            {
                canApplyShock = true;
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                //Debug.Log("shocked" );
                return;
            }

            if (Random.value < .99f && _iceDamage > 0)
            {
                canApplyChill = true;
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                //Debug.Log("iced" );
                return;
            }
        }

        if (canApplyIgnite)
        {
            _targetStats.SetupIgniteDamage(Mathf.RoundToInt(_fireDamage * .2f));
        }

        if (canApplyShock)
        {
            _targetStats.SetupShockDamage(Mathf.RoundToInt(_lightningDamage * .1f));
        }

        _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
    }

    public void ApplyAilments(bool _ignite, bool _chill, bool _shock)//应用异常状态
    {
        bool canApplyIgnite = !isIgnited && !isChilled && !isShocked;
        bool canApplyChill = !isIgnited && !isChilled && !isShocked;
        bool canApplyShock = !isIgnited && !isChilled;//没有其他异常状态才能进入一个异常状态



        //if (isIgnited || isChilled || isShocked)//如果进入一个异常状态就不能进入其他状态了
        //    return;

        if (_ignite && canApplyIgnite)
        {
            isIgnited = _ignite;
            ignitedTimer = ailmentsDuration;

            fx.IgniteFxFor(ailmentsDuration);
        }

        if (_chill && canApplyChill)
        {
            isChilled = _chill;
            chilledTimer = ailmentsDuration;

            float slowPercentage = .2f;//减速百分比

            GetComponent<Entity>().SlowEntityBy(slowPercentage, ailmentsDuration);//减速20%
            fx.ChillFxFor(ailmentsDuration);
        }

        if (_shock && canApplyShock)
        {
            if (!isShocked)
            {
                ApplyShock(_shock);
            }
            else
            {
                if (GetComponent<Player>() != null)//防止敌人打玩家自己被电
                    return;


                HitNearsetTargerWithShockStrike();
            }
        }
    }

    public void ApplyShock(bool _shock)
    {
        if(isShocked)//已经进入感电就不如再次进入
            return;

        isShocked = _shock;
        shockedTimer = ailmentsDuration;

        fx.ShockFxFor(ailmentsDuration);
    }

    private void HitNearsetTargerWithShockStrike()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 25);//碰撞体检测周围的敌人

        float closestDistance = Mathf.Infinity;
        Transform closestEnemy = null;

        foreach (var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null && Vector2.Distance(transform.position, hit.transform.position) > 1)//如果是敌人并且不是自己,防止自己被电
            {
                float distanceToEnemy = Vector2.Distance(transform.position, hit.transform.position);

                if (distanceToEnemy < closestDistance)
                {
                    closestDistance = distanceToEnemy;
                    closestEnemy = hit.transform;
                }
            }

            if (closestEnemy == null)
                closestEnemy = transform;


        }


        //寻找最近的敌人然后雷击
        if (closestEnemy != null)
        {
            GameObject newShockStrike = Instantiate(shockStrikePrefab, transform.position, Quaternion.identity);

            newShockStrike.GetComponent<ShockStrike_Controller>().Setup(shockDamage, closestEnemy.GetComponent<CharacterStats>());
        }
    }//闪电攻击附近的目标

    private void ApplyIgniteDamage()
    {
        if (igniteDamageTimer < 0 )
        {

            DecreaseHealthBy(igniteDamage);

            //currentHealth -= igniteDamage;

            if (currentHealth < 0 && !isDead)
                Die();

            igniteDamageTimer = igniteDamageCoolDown;
        }
    }
    public void SetupIgniteDamage(int _damage) => igniteDamage = _damage;//设置燃烧伤害

    public void SetupShockDamage(int _damage) => shockDamage = _damage;//设置感电伤害

    #endregion



    public virtual void TakeDamage(int _damage)//造成伤害函数,返回伤害值
    {
        DecreaseHealthBy(_damage);

        GetComponent<Entity>().DamageImpact();
        fx.StartCoroutine("FlashFX");


        Debug.Log(_damage);

        if (currentHealth < 0  && !isDead)
            Die();//人被杀就会死


    }


    public virtual void IncreaseHealthBy(int _amount)//加血函数
    {
        currentHealth += _amount;

        if (currentHealth > GetMaxHealthValue())
            currentHealth = GetMaxHealthValue();

        if (onHealthChanged != null)
            onHealthChanged();
    }


    protected virtual void DecreaseHealthBy(int _damage)//受到伤害的数值变化
    {
        currentHealth -= _damage;

        if (onHealthChanged != null)
            onHealthChanged();
    }




    protected virtual void Die()
    {
        isDead = true;
    }


    #region Stat calculations
    private int CheckTargetResistance(CharacterStats _targetStats, int totalMagicalDamage)//计算魔法伤害(魔抗
    {
        totalMagicalDamage -= _targetStats.magicResistance.GetValue() + (_targetStats.intelligence.GetValue() * 3);//减去魔抗值
        totalMagicalDamage = Mathf.Clamp(totalMagicalDamage, 0, int.MaxValue);//Clamp限制血量数值
        return totalMagicalDamage;
    }

    private int CheckTargetArmor(CharacterStats _targetStats, int totalDamage)//计算物理伤害(护甲
    {
        if (_targetStats.isChilled)
            totalDamage -= Mathf.RoundToInt(_targetStats.armor.GetValue() * .8f);//减去对方的护甲值
        else
            totalDamage -= _targetStats.armor.GetValue();//减去对方的护甲值


        totalDamage = Mathf.Clamp(totalDamage, 0, int.MaxValue);//最小值是0,最大值是int.MaxValue,防止别人打我加血
        return totalDamage;
    }



    private bool TargetCanAvoidAttack(CharacterStats _targetStats)//检测闪避
    {
        int totalEvasion = _targetStats.evasion.GetValue() + _targetStats.agility.GetValue();//总的闪避率

        if (isShocked)
            totalEvasion += 20;

        if (Random.Range(0, 100) < totalEvasion)
        {
            Debug.Log("Attack Avoided");
            return true;
        }

        return false;
    }

    private bool Cancrit()//暴击检测
    {
        int totalCritChance = critChance.GetValue() + agility.GetValue();//总的暴击率

        if (Random.Range(0, 100) < totalCritChance)
        {
            return true;
        }

        return false;

    }

    private int CalculateCriticalDamage(int _damage)//计算爆伤
    {
        float totalCritPower = (critPower.GetValue() + strength.GetValue()) * .01f;
        //Debug.Log("总的暴击率: " + totalCritPower);//total crit power

        float critDamage = _damage * totalCritPower;
        //Debug.Log("取整之后的爆伤" + critDamage);//crit damage before round up

        return Mathf.RoundToInt(critDamage);
    }



    public int GetMaxHealthValue()
    {
        return maxHealth.GetValue() + vitality.GetValue() * 5;
    }//获取最大生命值
    #endregion
}


原文地址:https://blog.csdn.net/suzh1qian/article/details/143682773

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!