Unity类银河恶魔城学习记录12-14 p136 Merge Skill Tree with Sword skill源代码

news2024/11/20 1:44:55

 Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码

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

CharacterStats.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting.Antlr3.Runtime.Misc;
using UnityEngine;
public enum StatType
{

    strength,
    agility,
    intelligence,
    vitality,
    damage,
    critChance,
    critPower,
    Health,
    armor,
    evasion,
    magicResistance,
    fireDamage,
    iceDamage,
    lightingDamage
}
public class CharacterStats : MonoBehaviour
{
    private EntityFX fx;


    [Header("Major stats")]
    public Stat strength; // 力量 增伤1点 爆伤增加 1% 物抗
    public Stat agility;// 敏捷 闪避 1% 闪避几率增加 1%
    public Stat intelligence;// 1 点 魔法伤害 1点魔抗 
    public Stat vitality;//加血的

    [Header("Offensive stats")]
    public Stat damage;
    public Stat critChance;      // 暴击率
    public Stat critPower;       //150% 爆伤

    [Header("Defensive stats")]
    public Stat Health;
    public Stat armor;
    public Stat evasion;//闪避值
    public Stat magicResistance;

    [Header("Magic stats")]
    public Stat fireDamage;
    public Stat iceDamage;
    public Stat lightingDamage;


    public bool isIgnited;  // 持续烧伤
    public bool isChilded;  // 削弱护甲 20%
    public bool isShocked;  // 降低敌人命中率

    [SerializeField] private float ailmentsDuration = 4;
    private float ignitedTimer;
    private float chilledTimer;
    private float shockedTimer;


    private float igniteDamageCooldown = .3f;
    private float ignitedDamageTimer;
    private int igniteDamage;
    [SerializeField] private GameObject shockStrikePrefab;
    private int shockDamage;


    public System.Action onHealthChanged;//使角色在Stat里调用UI层的函数
                                         //此函数调用了更新HealthUI函数
    public bool isDead { get; private set; }
    private bool isVulnerable;//脆弱效果

    [SerializeField] public int currentHealth;


    protected virtual void Start()
    {
        critPower.SetDefaultValue(150);//设置默认爆伤
        currentHealth = GetMaxHealthValue();

        fx = GetComponent<EntityFX>();
    }

    public void MakeVulnerableFor(float _duration) => StartCoroutine(VulnerableCorutine(_duration));//脆弱效果函数
    private IEnumerator VulnerableCorutine(float _duration)
    {
        isVulnerable = true;
        yield return new WaitForSeconds(_duration);
        isVulnerable = false;
    }

    protected virtual void Update()
    {
        //所有的状态都设置上默认持续时间,持续过了就结束状态
        ignitedTimer -= Time.deltaTime;
        chilledTimer -= Time.deltaTime;
        shockedTimer -= Time.deltaTime;
        ignitedDamageTimer -= Time.deltaTime;

        if (ignitedTimer < 0)
            isIgnited = false;
        if (chilledTimer < 0)
            isChilded = false;
        if (shockedTimer < 0)
            isShocked = false;

        //被点燃后,出现多段伤害后点燃停止
        if(isIgnited)
        ApplyIgnitedDamage();
    }

    
    public virtual void IncreaseStatBy(int _modifier, float _duration,Stat _statToModify)
    {
        StartCoroutine(StatModCoroutine(_modifier, _duration, _statToModify));
    }
    private IEnumerator StatModCoroutine(int _modifier, float _duration, Stat _statToModify)
    {
        _statToModify.AddModifier(_modifier);
        yield return new WaitForSeconds(_duration);
        _statToModify.RemoveModifier(_modifier);
    }
    public virtual void DoDamage(CharacterStats _targetStats)//计算后造成伤害函数
    {
        if (TargetCanAvoidAttack(_targetStats))设置闪避
        {
            return;
        }
        int totleDamage = damage.GetValue() + strength.GetValue();

        //爆伤设置
        if (CanCrit())
        {
            totleDamage = CalculateCriticalDamage(totleDamage);
        }

        totleDamage = CheckTargetArmor(_targetStats, totleDamage);//设置防御

        _targetStats.TakeDamage(totleDamage);

        DoMagicaDamage(_targetStats); // 可以去了也可以不去

    }
    protected virtual void Die()
    {
        isDead = true;
    }
    public virtual void TakeDamage(int _damage)//造成伤害是出特效
    {
        fx.StartCoroutine("FlashFX");//IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
                                     //https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
        DecreaseHealthBy(_damage);

        GetComponent<Entity>().DamageImpact();

        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)//此函数用来改变当前生命值,不调用特效
    {
        if (isVulnerable)
            _damage = Mathf.RoundToInt(_damage * 1.1f);

        currentHealth -= _damage;



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

    #region Magical damage and ailements
    private void ApplyIgnitedDamage()
    {
        if (ignitedDamageTimer < 0 )
        {
            DecreaseHealthBy(igniteDamage);
            if (currentHealth < 0 && !isDead)
                Die();
            ignitedDamageTimer = igniteDamageCooldown;
        }
    }被点燃后,出现多段伤害后点燃停止
    public virtual void DoMagicaDamage(CharacterStats _targetStats)//法伤计算和造成元素效果调用的地方
    {
        int _fireDamage = fireDamage.GetValue();
        int _iceDamage = iceDamage.GetValue();
        int _lightingDamage = lightingDamage.GetValue();

        int totleMagicalDamage = _fireDamage + _iceDamage + _lightingDamage + intelligence.GetValue();
        totleMagicalDamage = CheckTargetResistance(_targetStats, totleMagicalDamage);

        _targetStats.TakeDamage(totleMagicalDamage);

        //防止循环在所有元素伤害为0时出现死循环
        if (Mathf.Max(_fireDamage, _iceDamage, _lightingDamage) <= 0)
            return;

        //让元素效果取决与伤害


        //为了防止出现元素伤害一致而导致无法触发元素效果
        //循环判断触发某个元素效果

        AttemptyToApplyAilement(_targetStats, _fireDamage, _iceDamage, _lightingDamage);
        
    }

    private  void AttemptyToApplyAilement(CharacterStats _targetStats, int _fireDamage, int _iceDamage, int _lightingDamage)
    {
        bool canApplyIgnite = _fireDamage > _iceDamage && _fireDamage > _lightingDamage;
        bool canApplyChill = _iceDamage > _lightingDamage && _iceDamage > _fireDamage;
        bool canApplyShock = _lightingDamage > _fireDamage && _lightingDamage > _iceDamage;



        while (!canApplyIgnite && !canApplyChill && !canApplyShock)
        {
            if (Random.value < .25f)
            {
                canApplyIgnite = true;
                Debug.Log("Ignited");
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                return;
            }
            if (Random.value < .35f)
            {
                canApplyChill = true;
                Debug.Log("Chilled");
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                return;
            }
            if (Random.value < .55f)
            {
                canApplyShock = true;
                Debug.Log("Shocked");
                _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
                return;
            }

        }


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

        if (canApplyShock)
            _targetStats.SetupShockStrikeDamage(Mathf.RoundToInt(_lightingDamage * .1f));

        //给点燃伤害赋值

        _targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
    }//造成元素效果

    public void ApplyAilments(bool _ignite, bool _chill, bool _shock)//判断异常状态
    {
        bool canApplyIgnite = !isIgnited && !isChilded && !isShocked;
        bool canApplyChill = !isIgnited && !isChilded && !isShocked;
        bool canApplyShock = !isIgnited && !isChilded;
        //使当isShock为真时Shock里的函数仍然可以调用

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

            fx.IgniteFxFor(ailmentsDuration);
        }
        if (_chill && canApplyChill)
        {
            isChilded = _chill;
            chilledTimer = ailmentsDuration;

            float slowPercentage = .2f;

            GetComponent<Entity>().SlowEntityBy(slowPercentage, ailmentsDuration);

            fx.ChillFxFor(ailmentsDuration);
        }
        if (_shock && canApplyShock)
        {
            if(!isShocked)
            {
                ApplyShock(_shock);
            }
            else
            {
                if (GetComponent<Player>() != null)//防止出现敌人使玩家进入shock状态后也出现闪电
                    return;
                HitNearestTargetWithShockStrike();
            }//isShock为真时反复执行的函数为寻找最近的敌人,创建闪电实例并传入数据

        }
    }

    public void ApplyShock(bool _shock)
    {
        if (isShocked)
            return;

        isShocked = _shock;
        shockedTimer = ailmentsDuration;

        fx.ShockFxFor(ailmentsDuration);
    }//触电变色效果

    private void HitNearestTargetWithShockStrike()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 25);//找到环绕自己的所有碰撞器

        float closestDistance = Mathf.Infinity;//正无穷大的表示形式(只读)
        Transform closestEnemy = null;


        //https://docs.unity3d.com/cn/current/ScriptReference/Mathf.Infinity.html
        foreach (var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null && Vector2.Distance(transform.position, hit.transform.position) > 1)// 防止最近的敌人就是Shock状态敌人自己
            {
                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>());
        }
    }//给最近的敌人以雷劈

    public void SetupIgniteDamage(int _damage) => igniteDamage = _damage;//给点燃伤害赋值
    public void SetupShockStrikeDamage(int _damage) => shockDamage = _damage;//雷电伤害赋值

    #endregion

    #region Stat calculations

    private int CheckTargetResistance(CharacterStats _targetStats, int totleMagicalDamage)//法抗计算
    {
        totleMagicalDamage -= _targetStats.magicResistance.GetValue() + (_targetStats.intelligence.GetValue() * 3);
        totleMagicalDamage = Mathf.Clamp(totleMagicalDamage, 0, int.MaxValue);
        return totleMagicalDamage;
    }
    
   
   
    private static int CheckTargetArmor(CharacterStats _targetStats, int totleDamage)//防御计算
    {

        //被冰冻后,角色护甲减少
        if (_targetStats.isChilded)
            totleDamage -= Mathf.RoundToInt(_targetStats.armor.GetValue() * .8f);
        else
            totleDamage -= _targetStats.armor.GetValue();
        totleDamage = Mathf.Clamp(totleDamage, 0, int.MaxValue);
        return totleDamage;
    }
    public virtual void OnEvasion()
    {

    }//可继承成功闪避触发的函数
    private bool TargetCanAvoidAttack(CharacterStats _targetStats)//闪避计算
    {
        int totleEvation = _targetStats.evasion.GetValue() + _targetStats.agility.GetValue();

        //我被麻痹后
        //敌人的闪避率提升
        if (isShocked)
            totleEvation += 20;

        if (Random.Range(0, 100) < totleEvation)
        {
            _targetStats.OnEvasion();
            return true;
        }
        return false;
    }

    private bool CanCrit()//判断是否暴击
    {
        int totleCriticalChance = critChance.GetValue() + agility.GetValue();

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

        return false;
    }

    private int CalculateCriticalDamage(int _damage)//计算暴击后伤害
    {
        float totleCirticalPower = (critPower.GetValue() + strength.GetValue()) * .01f;

        float critDamage = _damage * totleCirticalPower;

        return Mathf.RoundToInt(critDamage);//返回舍入为最近整数的
    }
    public int GetMaxHealthValue()
    {

        return Health.GetValue() + vitality.GetValue() * 10;

    }//统计生命值函数

    public  Stat GetStats(StatType _statType)
    {
        if (_statType == StatType.strength) return strength;
        else if (_statType == StatType.agility) return agility;
        else if (_statType == StatType.intelligence) return intelligence;
        else if (_statType == StatType.vitality) return vitality;
        else if (_statType == StatType.damage) return damage;
        else if (_statType == StatType.critChance) return critChance;
        else if (_statType == StatType.critPower) return critPower;
        else if (_statType == StatType.Health) return Health;
        else if (_statType == StatType.armor) return armor;
        else if (_statType == StatType.evasion) return evasion;
        else if (_statType == StatType.magicResistance) return magicResistance;
        else if (_statType == StatType.fireDamage) return fireDamage;
        else if (_statType == StatType.iceDamage) return iceDamage;
        else if (_statType == StatType.lightingDamage) return lightingDamage;

        return null;
    }
    #endregion
}

Sword_Skill_Controller.cs
using System.Collections;
using System.Collections.Generic;
using Unity.Burst.CompilerServices;
using UnityEngine;

public class Sword_Skill_Controller : MonoBehaviour
{
    private float returnSpeed;
    private bool isReturning;

    private Animator anim;
    private Rigidbody2D rb;
    private CircleCollider2D cd;
    private Player player;

    private bool canRotate = true;

    private float freezeTimeDuration;
    [Header("Piece info")]
    [SerializeField] float pierceAmount;

    [Header("Bounce info")]
    private float bounceSpeed;//设置弹跳速度
    private bool isBouncing;//判断是否可以弹跳
    private int bounceAmount;//弹跳的次数
    public List<Transform> enemyTargets;//保存所有在剑范围内的敌人的列表
    private int targetIndex;//设置targetIndex作为敌人计数器

    [Header("Spin info")]
    private float maxTravelDistance;//最大攻击距离
    private float spinDuration;//持续时间
    private float spinTimer;//计时器
    private bool wasStopped;//是否停止
    private bool isSpinning;

    private float hitTimer;
    private float hitColldown;

    private float spinDirection;//用来不断推进剑在x轴上移动的值
   
    private void Awake()
    {
        anim = GetComponentInChildren<Animator>();
        rb = GetComponent<Rigidbody2D>();
        cd = GetComponent<CircleCollider2D>();
    }

    private void DestroyMe()
    {
        Destroy(gameObject);
    }
    public void ReturnSword()
    {
        rb.constraints = RigidbodyConstraints2D.FreezeAll;//修复剑只要不触碰到物体就无法收回的bug
        //rb.isKinematic = false;
        transform.parent = null;
        isReturning = true;
    }
    public void SetupSword(Vector2 _dir,float _gravityScale,Player _player,float _freezeTimeDuration,float _returnSpeed)
    {
        player = _player;
        rb.velocity = _dir;
        rb.gravityScale = _gravityScale;
        freezeTimeDuration = _freezeTimeDuration;
        returnSpeed = _returnSpeed;
        if(pierceAmount <= 0)
          anim.SetBool("Rotation", true); //其次在pierceAmount > 0时不启动旋转动画
        spinDirection = Mathf.Clamp(rb.velocity.x, -1, 1);用来不断推进剑在x轴上移动的值
        //Mathf.Clamp,当剑使向右飞的时候,direction为1,反之为-1
        //https://docs.unity3d.com/cn/current/ScriptReference/Mathf.Clamp.html
        Invoke("DestroyMe",7);//在一定时间后自动销毁剑
    }

    public void SetupBounce(bool _isBouncing,int _bounceAmount,float _bounceSpeed)
    {
        isBouncing = _isBouncing;
        bounceAmount = _bounceAmount;
        bounceSpeed = _bounceSpeed;
    }

    public void SetupPierce(int _pierceAomunt)
    {
        pierceAmount = _pierceAomunt;
    }

    public void SetupSpin(bool _isSpinning, float _maxTravelDistance,float _spinDuration,float _hitCooldown)
    {
        isSpinning = _isSpinning;
        maxTravelDistance = _maxTravelDistance;
        spinDuration = _spinDuration;
        hitColldown = _hitCooldown;
    }

    private void Update()
    {
        if (canRotate)
            transform.right = rb.velocity;//使武器随着速度而改变朝向

        if (isReturning)
        {
            transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);//从原来的位置返回到player的位置
                                                                                                                                  //并且随着时间增加而增加速度
            if (Vector2.Distance(transform.position, player.transform.position) < 1)//当剑与player的距离小于一定距离,清除剑
            {
                player.CatchTheSword();
            }
        }


        BounceLogic();
        SpinLogic();
    }

    private void SpinLogic()
    {
        if (isSpinning)//首先当isSpining为true才可进入
        {
            if (Vector2.Distance(player.transform.position, transform.position) > maxTravelDistance && !wasStopped)//当剑与角色到达最大攻击距离,stop为true,停止剑运动,给剑一个倒计时
            {
                StopWhenSpinning();
            }
            if (wasStopped)//当stop为true,倒计时过了,回归角色
            {
                spinTimer -= Time.deltaTime;                                                                    //spinDirection一直是1或者-1,但position是变化的
                transform.position = Vector2.MoveTowards(transform.position, new Vector2(transform.position.x + spinDirection, transform.position.y), 1.5f * Time.deltaTime);//让剑在x轴上移动的函数
                if (spinTimer < 0)
                {
                    isReturning = true;
                    isSpinning = false;
                }

                hitTimer -= Time.deltaTime;

                if (hitTimer < 0)
                {
                    hitTimer = hitColldown;
                    Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 1);
                    foreach (var hit in colliders)
                    {
                        if (hit.GetComponent<Enemy>() != null)
                        {
                            hit.GetComponent<Enemy>().DamageImpact();
                        }
                    }
                }
            }
        }
    }

    private void StopWhenSpinning()
    {
        wasStopped = true;
        rb.constraints = RigidbodyConstraints2D.FreezeAll;
        spinTimer = spinDuration;
    }

    private void BounceLogic()
    {
        if (isBouncing && enemyTargets.Count > 0)
        {

            transform.position = Vector2.MoveTowards(transform.position, enemyTargets[targetIndex].position, bounceSpeed * Time.deltaTime);
            //3.当可以弹跳且列表内数量大于0,调用ToWords,这将使剑能够跳到敌人身上
            if (Vector2.Distance(transform.position, enemyTargets[targetIndex].position) < .1f)
            {
                enemyTargets[targetIndex].GetComponent<Enemy>().DamageImpact();
                targetIndex++;
                bounceAmount--;//设置弹跳次数
                if (bounceAmount <= 0)
                {
                    isBouncing = false;
                    isReturning = true;
                }//这样会使当弹跳次数为0时,返回到角色手中
                if (targetIndex >= enemyTargets.Count)
                {
                    targetIndex = 0;
                }
            }//利用与目标距离的判断,使剑接近目标距离时切换到下一个目标。
             //且如果所有目标都跳完了,切回第一个
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)//传入碰到的物体的碰撞器
    {
        if (isReturning)
        {
            return;
        }//修复在返回时扔出时没有碰到任何物体,但返回时碰到了物体导致剑的一些性质发生改变的问题,及回来的时候调用了OnTriggerEnter2D

        if(collision.GetComponent<Enemy>()!=null)
        {
            Enemy enemy = collision.GetComponent<Enemy>();
            SwordSkillDamage(enemy);
        }


        SetupTargetsForBounce(collision);

        StuckInto(collision);
    }//打开IsTrigger时才可使用该函数

    private void SwordSkillDamage(Enemy enemy)
    {
        EnemyStats enemyStats = enemy.GetComponent<EnemyStats>();
        player.stats.DoDamage(enemyStats);

        if(player.skill.sword.timeStopUnlocked)
            enemy.FreezeTimerFor(freezeTimeDuration);

        if (player.skill.sword.vulnerableUnlocked)
            enemyStats.MakeVulnerableFor(freezeTimeDuration);//调用脆弱效果函数

        ItemData_Equipment equipment = Inventory.instance.GetEquipment(EquipmentType.Amulet);
        if (equipment != null)
        {
            equipment.Effect(enemy.transform);
        }
    }//造成伤害函数

    private void SetupTargetsForBounce(Collider2D collision)
    {
        if (collision.GetComponent<Enemy>() != null)//首先得碰到敌人,只有碰到敌人才会跳
        {
            if (isBouncing && enemyTargets.Count <= 0)
            {
                Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 10);
                foreach (var hit in colliders)
                {
                    if (hit.GetComponent<Enemy>() != null)
                    {
                        enemyTargets.Add(hit.transform);
                    }
                }
            }
        }
    }

    //https://docs.unity3d.com/cn/current/ScriptReference/Collider2D.OnTriggerEnter2D.html

    private void StuckInto(Collider2D collision)
    {
        if(pierceAmount > 0 && collision.GetComponent<Enemy>()!= null)//本质就是能穿过敌人,在amount>0时不执行能让剑卡在敌人里的语句就行
        {
            pierceAmount--;
            return;
        }

        if (isSpinning)
        {
            StopWhenSpinning();
            return;
        }//防止卡在敌人身上

        canRotate = false;
        cd.enabled = false;//相当于关闭碰撞后触发函数。//https://docs.unity3d.com/cn/current/ScriptReference/Collision2D-enabled.html

        rb.isKinematic = true;//开启物理学反应 https://docs.unity3d.com/cn/current/ScriptReference/Rigidbody2D-isKinematic.html
        rb.constraints = RigidbodyConstraints2D.FreezeAll;//冻结所有移动

        if (isBouncing&&enemyTargets.Count>0)//修复剑卡不到墙上的bug
            return;

        //终止对动画的改变和终止附在敌人上
        transform.parent = collision.transform;//设置sword的父组件为碰撞到的物体
        anim.SetBool("Rotation", false);
    }
    
}

Sword_Skill.cs
using UnityEngine;
using UnityEngine.UI;

public enum SwordType
{
    Regular,
    Bounce,
    Pierce,
    Spin
}

public class Sword_Skill : Skill
{
    public SwordType swordType = SwordType.Regular;//创建应一个enum列表,后面用来判断切换状态

    [Header("Skill Info")]
    [SerializeField] private UI_SkillTreeSlot swordUnlockButton;
    public bool swordUnlocked;
    [SerializeField] private GameObject swordPrefab;//sword预制体
    [SerializeField] private Vector2 launchForce;//发射力度
    [SerializeField] private float swordGravity;//发射体的重力
    [SerializeField] private float freezeTimeDuration;
    [SerializeField] private float returnSpeed;

    [Header("Bounce Info")]
    [SerializeField] private UI_SkillTreeSlot bounceUnlockButton;
    [SerializeField] private int bounceAmount;
    [SerializeField] private float bounceGravity;
    [SerializeField] private float bounceSpeed;

    [Header("Pierce info")]
    [SerializeField] private UI_SkillTreeSlot pierceUnlockButton;

    [SerializeField] private int pierceAmount;
    [SerializeField] private float pierceGravity;


    [Header("Spin info")]
    [SerializeField] private UI_SkillTreeSlot spinUnlockButton;
    [SerializeField] private float hitCooldown = .35f;//攻击冷却
    [SerializeField] private float maxTravelDistance = 7;//最大攻击距离
    [SerializeField] private float spinDuration = 2;//持续时间
    [SerializeField] private float spinGravity = 1;

    [Header("Passive skills")]
    [SerializeField] private UI_SkillTreeSlot timeStopUnlockButton;
    public bool timeStopUnlocked { get; private set; }
    [SerializeField] private UI_SkillTreeSlot vulnerableUnlockButton;
    public bool vulnerableUnlocked{ get; private set; }




    [Header("Aim dots")]
    [SerializeField] private int numberOfDots;//需要的点的数量
    [SerializeField] private float spaceBetweenDots;//相隔的距离
    [SerializeField] private GameObject dotPrefab;//dot预制体
    [SerializeField] private Transform dotsParent;//不是很懂



    private GameObject[] dots;//dot组
    private Vector2 finalDir;

    protected override void Start()
    {
        base.Start();

        GenerateDots();//生成点函数

        SetupGravity();

        swordUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockSword);
        timeStopUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockTimeStop);
        vulnerableUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockVulnerable);
        bounceUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockBounce);
        pierceUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockPierce);
        spinUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockSpin);
    }

    private void SetupGravity()//每个剑的状态都对应了不同的剑重力
    {
        if (swordType == SwordType.Pierce)
        {
            swordGravity = pierceGravity;
        }
        if (swordType == SwordType.Bounce)
        {
            swordGravity = bounceGravity;
        }
        if (swordType == SwordType.Spin)
        {
            swordGravity = spinGravity;
        }
    }

    protected override void Update()
    {
        base.Update();
        if (Input.GetKeyUp(KeyCode.Mouse1))
        {
            finalDir = new Vector2(AimDirection().normalized.x * launchForce.x, AimDirection().normalized.y * launchForce.y);
            //将位移量改为单位向量分别与力度的x,y相乘作为finalDir
        }

        if (Input.GetKey(KeyCode.Mouse1))
        {
            for (int i = 0; i < dots.Length; i++)
            {
                dots[i].transform.position = DotsPosition(i * spaceBetweenDots);//用循环为每个点以返回值赋值(传入值为每个点的顺序i*点间距
            }
        }
    }
    public void CreateSword()
    {
        GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);//创造实例,初始位置为此时player的位置
        Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();//获得Controller

        if (swordType == SwordType.Bounce)
        {
            newSwordScript.SetupBounce(true, bounceAmount, returnSpeed);
        }
        else if (swordType == SwordType.Pierce)
        {
            newSwordScript.SetupPierce(pierceAmount);
        }
        else if (swordType == SwordType.Spin)
        {
            newSwordScript.SetupSpin(true, maxTravelDistance, spinDuration, hitCooldown);
        }



        newSwordScript.SetupSword(finalDir, swordGravity, player, freezeTimeDuration, bounceSpeed);//调用Controller里的SetupSword函数,给予其速度和重力和player实例

        player.AssignNewSword(newSword);//调用在player中保存通过此方法创造出的sword实例
        DotsActive(false);//关闭点的显示
    }
    #region Unlock Region
    private void UnlockTimeStop()
    {
        if (timeStopUnlockButton.unlocked)
            timeStopUnlocked = true;
    }
    private void UnlockVulnerable()
    {
        if (vulnerableUnlockButton.unlocked)
            vulnerableUnlocked = true;
    }
    private void UnlockSword()
    {
        if (swordUnlockButton.unlocked)
        {
            swordUnlocked = true;
            swordType = SwordType.Regular;

        }

    }
    private void UnlockBounce()
    {
        if (bounceUnlockButton.unlocked)
            swordType = SwordType.Bounce;
    }
    private void UnlockPierce()
    {
        if (pierceUnlockButton.unlocked)
            swordType = SwordType.Pierce;

    }
    private void UnlockSpin()
    {
        if (spinUnlockButton.unlocked)
            swordType = SwordType.Spin;

    }
    #endregion

    #region Aim region
    public Vector2 AimDirection()
    {
        Vector2 playerPosition = player.transform.position;//拿到玩家此时的位置
        Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);//https://docs.unity3d.com/cn/current/ScriptReference/Camera.ScreenToWorldPoint.html
                                                                                    //大概就是返回屏幕上括号里的参数的位置,这里返回了鼠标的位置
                                                                                    //拿到此时鼠标的位置
        Vector2 direction = mousePosition - playerPosition;//获得距离的绝对向量

        return direction;//返回距离向量
    }

    public void DotsActive(bool _isActive)
    {
        for (int i = 0; i < dots.Length; i++)
        {
            dots[i].SetActive(_isActive);//设置每个点是否显示函数
        }
    }
    private void GenerateDots()//生成点函数
    {
        dots = new GameObject[numberOfDots];//为dot赋予实例数量
        for (int i = 0; i < numberOfDots; i++)
        {
            dots[i] = Instantiate(dotPrefab, player.transform.position, Quaternion.identity, dotsParent);//对象与世界轴或父轴完全对齐
            //https://docs.unity3d.com/cn/current/ScriptReference/Quaternion-identity.html
            dots[i].SetActive(false);//关闭dot
        }
    }

    private Vector2 DotsPosition(float t)//传入顺序相关的点间距
    {
        Vector2 position = (Vector2)player.transform.position +
            new Vector2
            (AimDirection().normalized.x * launchForce.x,
             AimDirection().normalized.y * launchForce.y) * t  //基本间距
             + .5f * (Physics2D.gravity * swordGravity) * (t * t)//重力影响
             ;
        //t是控制之间点间距的
        return position;//返回位置
    }//设置点间距函数
    #endregion

}
PlayerGroundState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//GroundState用于保证只有在Idle和Move这两个地面状态下才能调用某些函数,并且稍微减少一点代码量
public class PlayerGroundState : PlayerState
{
    public PlayerGroundState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
    {
    }

    public override void Enter()
    {
        base.Enter();
    }

    public override void Exit()
    {
        base.Exit();
    }
    public override void Update()
    {
        base.Update();
        if(Input.GetKeyDown(KeyCode.R))
        {
            stateMachine.ChangeState(player.blackhole);
        }
        if(Input.GetKeyDown(KeyCode.Mouse1)&&HasNoSword()&&player.skill.sword.swordUnlocked)//点击右键进入瞄准状态,当sword存在时,不能进入aim状态
        {
            stateMachine.ChangeState(player.aimSword);
        }
        if (Input.GetKeyDown(KeyCode.Q) && player.skill.parry.parryUnlocked)//摁Q进入反击状态
        {
            stateMachine.ChangeState(player.counterAttack);
        }
        if(Input.GetKeyDown(KeyCode.Mouse0))//p38 2.从ground进入攻击状态
        {
            stateMachine.ChangeState(player.primaryAttack);
        }
        if(player.IsGroundDetected()==false)
        {
            stateMachine.ChangeState(player.airState);
        }// 写这个是为了防止在空中直接切换为moveState了。

        if (Input.GetKeyDown(KeyCode.Space) && player.IsGroundDetected())
        {
            stateMachine.ChangeState(player.jumpState);
        }//空格切换为跳跃状态


    }
    private bool HasNoSword()//用这个函数同时控制了是否能进入aimSword和如果sword存在便使他回归player的功能
    {
        if(!player.sword)
        {
            return true;
        }
        player.sword.GetComponent<Sword_Skill_Controller>().ReturnSword();
        return false;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1594017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RTC的基本概念以及相关例程

实时时钟(RTC) 北京时间跟伦敦时间错8个小时 BKP简介 BKP本质上是RAM存储器&#xff0c;没有掉电不丢失的能力。 VBAT的作用就是&#xff0c;当VDD断电时&#xff0c;BKP会切换到VBAT供电&#xff0c;这样可以继续维持BKP里面的数据&#xff0c;如果VDD断电&#xff0c;VBAT也…

《猎灵online》游戏完整源码(源码+客户端+服务端+文档+工具),云盘下载

《猎灵》是一款由国内知名开发运营开发的大型3D魔幻网游&#xff0c;《猎灵》研发团队突破诸多瓶颈&#xff0c;首创“全自由无限制PK”&#xff0c;让玩家拒绝无意义等待&#xff0c;自由作战不受任何束缚&#xff0c;真正的实现想战就战&#xff0c;游戏创建了六界神魔乱斗的…

蓝桥杯嵌入式模板(cubemxkeil5)

LED 引脚PC8~PC15&#xff0c;默认高电平&#xff08;灭&#xff09;。 此外还要配置PD2为输出引脚&#xff08;控制LED锁存&#xff09; &#xff0c;默认低电平&#xff08;锁住&#xff09;&#xff01;&#xff01;&#xff01; #include "led.h"void led_disp…

五子棋:不会下五子棋也没关系,会用Java写五子棋就行

关注公号“微澜网络”获取完整源代码&#xff01; 效果展示&#xff1a; 目录 效果展示&#xff1a; 导语&#xff1a; 游戏介绍&#xff1a; 程序设计&#xff1a; 1.游戏规则和功能&#xff1a; 2.用户界面设计&#xff1a; 3.程序架构设计&#xff1a; 4.可扩展性和灵…

【IC验证】fork...join

1.fork...join 各线程并行执行&#xff0c;当耗时最长的线程执行完后&#xff0c;跳出该语句块。如果任何一个子线程无法结束&#xff0c;则整个fork...join将被挂起 2.fork...join_any 如果任何一个子线程完成&#xff0c;则程序允许执行fork...join_any块外面接下来的语句…

SVN的介绍

首先SVN是什么&#xff1a; Apache下的一个开源的项目Subversion&#xff0c;通常缩写为 SVN&#xff0c;是一个版本控制系统。 版本控制系统是一个软件&#xff0c;它可以伴随我们软件开发人员一起工作&#xff0c;让我们编写代码的完整的历史保存下来。 目前它的各个版本的…

华为ensp中aaa(3a)实现telnet远程连接认证配置命令

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月14日18点49分 AAA认证的全称是Authentication、Authorization、Accounting&#xff0c;中文意思是认证、授权、计费。 以下是详细解释 认证&#xff08;Authentic…

Kylin-Server-V10-SP3-General-Release-2303-X86_64

Kylin-Server-V10-SP3-General-Release-2303-X86_64 银河麒麟V10 银河蓝色麒麟比红麒麟养眼多了 Kylin-CSDN博客 Kylin IPv4 setting-CSDN博客

安全加速SCDN带的态势感知能为网站安全带来哪些帮助

随着安全加速SCDN被越来越多的用户使用&#xff0c;很多用户都不知道安全加速SCDN的态势感知是用于做什么的&#xff0c;德迅云安全今天就带大家来了解下什么是态势感知&#xff0c;态势感知顾名思义就是对未发生的事件进行预知&#xff0c;并提前进行防范措施的布置&#xff0…

Android安卓开发 - 开发基础(二)

App的工程结构 本节介绍App工程的基本结构及其常用配置&#xff0c;首先描述项目和模块的区别&#xff0c;以及工程内部各目录与配置 文件的用途说明&#xff1b;其次阐述两种级别的编译配置文件build.gradle…

用于扩展Qt本身的插件(上)

Qt自身插件 引言示例插件与应用插件的程序作为整体插件和应用插件的程序单独存在实现插件编写测试插件的程序应用插件运行结果引言 用于扩展qt自身的插件按照我的理解分为两种: 1. 直接扩展Qt自身,无需在QtCreator的设计器中加载; 2. 扩展Qt自身,同时需要在QtCreator的设计…

【SpringBoot整合系列】SpringBoot 实现大文件分片上传、断点续传及秒传

目录 功能介绍文件上传分片上传秒传断点续传 相关概念相关方法大文件上传流程前端切片处理逻辑后端处理切片的逻辑流程解析 后端代码实现功能目标1.建表SQL2.引入依赖3.实体类4.响应模板5.枚举类6.自定义异常7.工具类8.Controller层9.FileService10.LocalStorageService11.File…

设计模式代码实战-工厂模式

1、问题描述 小明家有两个工厂&#xff0c;一个用于生产圆形积木&#xff0c;一个用于生产方形积木&#xff0c;请你帮他设计一个积木工厂系统&#xff0c;记录积木生产的信息。 输入案例 3 Circle 1 Square 2 Circle 1 2、工厂模式 将产品的创建过程封装在⼀个⼯⼚类中&am…

变换时光:用MagicTime生成落叶纷飞的视频

简介 MagicTime: https://huggingface.co/spaces/BestWishYsh/MagicTime 是一款基于时间延迟视频生成模型的变体人工智能工具&#xff0c;它可以让您将静止图像转换为动态视频&#xff0c;赋予图像生命。 今天&#xff0c;我们将利用MagicTime来生成一片落叶纷飞的景象&#…

【从浅学到熟知Linux】程序地址空间分布与进程地址空间详谈(含虚拟地址到物理地址的映射)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 程序地址空间概览进程地址空间 程序地址空间概览 我们在执行一个C语言程序时&#xff0c;它包含代码、变量…

配置DHCP服务器实现为动态客户端和静态客户端分配不同网络参数

相关学习推荐&#xff1a;什么是DHCP?为什么要使用DHCP&#xff1f; 华为HCIP课程【视频教程】&#xff1a;华为HCIP必考题&#xff1a;DHCP协议原理与配置 组网需求 如图1所示&#xff0c;Router作为企业出口网关&#xff0c;PC和IP Phone为某办公区办公设备。为了方便统一管…

记一次Oracle DG备库实例宕分析

一、问题现象 同事反馈国外点在国内的XXX备库实例宕&#xff0c;尝试将该实例重启&#xff0c;结果重启报如下错误&#xff0c;未能正常启动该数据库。 Standby crash recovery failed to bring standby database to a consistent point because needed redo hasnt arrived yet…

全国贫困县DID数据(2008-2022年)

数据来源&#xff1a;国W院扶贫开发领导小组办公室 时间跨度&#xff1a;2008-2022年 数据范围&#xff1a;各县域 数据指标 年份 县域名称 所属地市 所属省份 县域代码 是否贫困县(是为1&#xff0c;否为0) 参考文献&#xff1a; [1]马雯嘉,吴茂祯.从全面脱贫到乡村振兴…

ChatGPT在线网页版

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

FL Studio808鼓音在哪 FL Studio怎么让音乐鼓点更有力 FL Studio教程

FL Studio808鼓音在哪&#xff1f;808是一款电鼓机的名称&#xff0c;它发出的声音也被称之为808鼓&#xff0c;通常我们可以安装鼓机插件来使用&#xff0c;但FL Studio中自带的也有808鼓的采样音频。FL Studio怎么让音乐鼓点更有力&#xff1f;让鼓点更有力要从EQ均衡器、压缩…