【Unity2D好项目分享】用全是好活制作横版卷轴射击游戏⑤制作得分系统以及能量的使用。

学习目标:

前面学习了制作了生命值和能量值系统,今天来是试着做一下得分系统,以及将我们的能量使用分为两部分。


得分系统:

  为了方便以后制作暂停菜单,我们需要将生命条和能量条以及得分系统放在同一个Canvas上,先把三者创建的Canvas组件移除掉,给一个空对象创建Canvas,别忘了设置他们的锚点。


 

 

 再给它创建一个Image,使用纵向布局,再创建两个Text

 

 

 

创建一个ScoreManager对象以及同名脚本方便我们管理分数

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreManager : PersistentSingleTon<ScoreManager>
{
    int score;
    int currentScore;

    [SerializeField] Vector3 scoreTextScale = new Vector3(1.2f, 1.2f, 1f);
    public void ResetScore()
    {
        score = 0;
        currentScore = 0;
        ScoreDisplay.UpdateText(score);
    }
    public void AddScore(int scorePoint)
    {
        currentScore += scorePoint;
        StartCoroutine(nameof(AddScoreCoroutine));
    }

    IEnumerator AddScoreCoroutine()
    {
        ScoreDisplay.ScaleText(scoreTextScale);
        while(currentScore > score)
        {
            score += 1;
            ScoreDisplay.UpdateText(score);

            yield return null;
        }
        ScoreDisplay.ScaleText(Vector3.one);
    }

    
}

给我们的ScoreText新创建一个脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreDisplay : MonoBehaviour
{
    //得分UI的显示与更新

    static Text scoreText;

    private void Awake()//用于脚本自身初始化
    {
        scoreText = GetComponent<Text>();
    }
    private void Start() //初始化工作晚于Awake运行,留给其他脚本进行初始化和调用其他脚本的函数
    {
        ScoreManager.Instance.ResetScore();
    }

    public static void UpdateText(int score)
    {
        scoreText.text = score.ToString();
    }
    public static void ScaleText(Vector3 targetScale)
    {
        scoreText.transform.localScale = targetScale;
    }
}

当然我们要给我们的Enemy绑定这个脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : Character
{
    [SerializeField] int deathEnergyBouns = 3;
    [SerializeField] int scorePoint = 100;
    public override void Die()
    {
        ScoreManager.Instance.AddScore(scorePoint);
        PlayerEnergy.Instance.Obtain(deathEnergyBouns);
        EnemyManager.Instance.RemoveList(gameObject);
        base.Die();
    }
}

这样得分系统就算是做完了。

制作能量使用第①部分闪避:

首先还得是在Plyaer上添加一个动作表

 在PlayerInput脚本中

public event UnityAction onDodge = delegate { };
public void OnDodge(InputAction.CallbackContext context)
    {
        if(context.phase == InputActionPhase.Performed)
        {
            onDodge.Invoke();
        }
    }

我们可以为玩家添加闪避动作,通过关闭碰撞体即可实现,先检查一遍PlayerEnergy脚本。

using System.Collections;
using UnityEngine;

public class PlayerEnergy : Singleton<PlayerEnergy>
{
    [SerializeField] float overdriveInterval = 0.1f;
    [SerializeField] EnergyBar energyBar;
    public const int MAX = 100;
    public const int PERCENT = 1;
    int energy;


    protected override void Awake()
    {
        waitForOverdriveInterval = new WaitForSeconds(overdriveInterval);
        base.Awake();
    }
    private void OnEnable()
    {
        PlayerOverdirve.on += PlayerOverdriveOn;
        PlayerOverdirve.off += PlayerOverdriveOff;
    }

    private void OnDisable()
    {
        PlayerOverdirve.on -= PlayerOverdriveOn;
        PlayerOverdirve.off -= PlayerOverdriveOff;
    }
    void Start()
    {
        energyBar.Initialize(energy, MAX);
        Obtain(100);
    }

    public void Obtain(int value) //获得能量
    {
        if (energy == MAX || !gameObject.activeSelf || !avaliable) return;

        energy = Mathf.Clamp(energy + value, 0, MAX);
        energyBar.UpdateStates(energy, MAX); //更新能量条
    }

    public void Use(int value)
    {
        energy -= value;
        energyBar.UpdateStates(energy, MAX);

        if(energy == 0 && !avaliable)
        {
            PlayerOverdirve.off.Invoke();
        }
    }

    public bool IsEnough(int value) => energy >= value; //判断能量是否能使用


}

点开Player脚本为它创建闪避Dodge相关的模块

[Header("--- DODGE ---")]
    [SerializeField] int dodgeEnergyCost = 25;
    [SerializeField] float maxRoll = 360f;
    [SerializeField] float rollSpeed = 360f;
    [SerializeField] Vector3 dodgeScale = new Vector3(0.5f, 0.5f, 0.5f);

    [SerializeField] AudioData dodgeSFX;

    bool isDodging = false;

    float currentRoll;
    float dodgeDuration;
private void Awake()
    {
        rigi2D = GetComponent<Rigidbody2D>();
        collider = GetComponent<Collider2D>();

        var size = transform.GetChild(0).GetComponent<Renderer>().bounds.size;
        paddingX = size.x / 2;
        paddingY = size.y / 2;


        dodgeDuration = maxRoll / rollSpeed;
        rigi2D.gravityScale = 0f;

        waitForFireInterval = new WaitForSeconds(fireInterval);
        waitForOverdriveFireInterval = new WaitForSeconds(fireInterval /= overdriveFireFactor);
        waitHealthGeneratedTime = new WaitForSeconds(healthGenerateTime);
        waitDeceletationTime = new WaitForSeconds(decelarationTime);
    }
 protected override void OnEnable()
    {
        //增加委托/订阅事件
        base.OnEnable();
        input.onMove += Move;
        input.onStopMove += StopMove;
        input.onFire += Fire;
        input.onStopFire += StopFire;
        input.onDodge += Dodge;
}
private void OnDisable()
    {
        //取消委托
        input.onMove -= Move;
        input.onStopMove -= StopMove;
        input.onFire -= Fire;
        input.onStopFire -= StopFire;
        input.onDodge -= Dodge;
}

 void Dodge()
    {
        if (isDodging || !PlayerEnergy.Instance.IsEnough(dodgeEnergyCost)) return;
        StartCoroutine(nameof(DodgeCoroutine));
    }

    IEnumerator DodgeCoroutine()
    {
        isDodging = true;
        AudioManager.Instance.PlayRandomSFX(dodgeSFX);
        //消耗能量
        PlayerEnergy.Instance.Use(dodgeEnergyCost);
        //无敌状态
        collider.isTrigger = true;

        currentRoll = 0f;
        TimeController.Instance.BulletTime(slowMotionDuration, slowMotionDuration);

        var scale = transform.localScale;
        while(currentRoll < maxRoll)
        {
            currentRoll += rollSpeed * Time.deltaTime;
            //让玩家绕着X轴旋转
            transform.rotation = Quaternion.AngleAxis(currentRoll, Vector3.right);

            BezierCurve.QuadraticPoint(Vector3.one, Vector3.one,dodgeScale,currentRoll / maxRoll);
           

            yield return null;
        }

        collider.isTrigger = false;
        isDodging = false;
}

里面还涉及复制减速的TimeController

再创建一个同名脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TimeController : Singleton<TimeController>
{
    [SerializeField, Range(0, 1)] float bulletTimeScale = 0.1f;

    float defaultFixedDeltaTime;
    float t;


    protected override void Awake()
    {
        base.Awake();
        defaultFixedDeltaTime = Time.fixedDeltaTime;
    }
    public void BulletTime(float duration)
    {
        Time.timeScale = bulletTimeScale;
        StartCoroutine(SlowOutCoroutine(duration));
    }

    public void BulletTime(float inDuration,float outDuration)
    {
        StartCoroutine(SlowInAndOutCoroutine(inDuration, outDuration));
    }

    public void BulletTime(float inDuration,float keepingDuration,float outDuration)
    {
        StartCoroutine(SlowInKeepAndOutCoroutine(inDuration,keepingDuration,outDuration));
    }

    public void SlowIn(float duration)
    {
        StartCoroutine(SlowInCoroutine(duration));
    }

    public void ShowOut(float duration)
    {
        StartCoroutine(SlowOutCoroutine(duration));
    }

    IEnumerator SlowInKeepAndOutCoroutine(float inDuration,float keepingDuration,float outDuration)
    {
        yield return StartCoroutine(SlowInCoroutine(inDuration));
        yield return new WaitForSecondsRealtime(keepingDuration);

        StartCoroutine(SlowOutCoroutine(outDuration));
    }

    IEnumerator SlowInAndOutCoroutine(float inDuration,float outDuration)
    {
        yield return StartCoroutine(SlowInCoroutine(inDuration));

        StartCoroutine(SlowOutCoroutine(outDuration));
    } 

    IEnumerator SlowInCoroutine(float duration)
    {
        t = 0f;
        while (t < 1f)
        {
            t += Time.unscaledDeltaTime / duration;
            Time.timeScale = Mathf.Lerp(1f, bulletTimeScale, t) ;
            Time.fixedDeltaTime = defaultFixedDeltaTime * Time.timeScale;

            yield return null;
        }
    }
    IEnumerator SlowOutCoroutine(float duration)
    {
        t = 0f;
        while (t < 1f)
        {
            t += Time.unscaledDeltaTime / duration;
            Time.timeScale = Mathf.Lerp(bulletTimeScale, 1f, t);
            Time.fixedDeltaTime = defaultFixedDeltaTime * Time.timeScale;

            yield return null;
        }
    }
    
}

以及还有一个 BezierCurve的脚本用于计算二次贝塞尔曲线的,

using UnityEngine;

public class BezierCurve
{
    /// <summary>
    /// Return a point of quadratic Bezier curve.
    /// 返回二次贝塞尔曲线上的点。
    /// </summary>
    public static Vector3 QuadraticPoint(Vector3 startPoint, Vector3 endPoint, Vector3 controlPoint, float by)
    {
        // * Math Formula Method
        // float oneMinusT = 1f - t;

        // return oneMinusT * oneMinusT * startPoint + t * t * endPoint + 2 * oneMinusT * t * controlPoint;
        
        return Vector3.Lerp(
            Vector3.Lerp(startPoint, controlPoint, by), 
            Vector3.Lerp(controlPoint, endPoint, by), 
            by);
    }

    /// <summary>
    /// Return a point of cubic Bezier curve.
    /// 返回三次贝塞尔曲线上的点。
    /// </summary>
    public static Vector3 CubicPoint(Vector3 startPoint, Vector3 endPoint, Vector3 controlPointStart, Vector3 controlPointEnd, float t)
    {
        // * Method 01
        // float oneMinusT = 1f - t;

        // return oneMinusT * oneMinusT * oneMinusT * startPoint + 
        //     t * t * t * endPoint +
        //     3 * t * oneMinusT * oneMinusT * controlPointStart + 
        //     3 * t * t * oneMinusT * controlPointEnd;

        // * Method 02
        // var AB = Vector3.Lerp(startPoint, controlPointStart, t);
        // var BC = Vector3.Lerp(controlPointStart, controlPointEnd, t);
        // var CD = Vector3.Lerp(controlPointEnd, endPoint, t);
        // var AB2BC = Vector3.Lerp(AB, BC, t);
        // var BC2CD = Vector3.Lerp(BC, CD, t);

        // return Vector3.Lerp(AB2BC, BC2CD, t);
 
        // * Method 03
        // return Vector3.Lerp(
        //         Vector3.Lerp(
        //             Vector3.Lerp(startPoint, controlPointStart, t), 
        //             Vector3.Lerp(controlPointStart, controlPointEnd, t), 
        //             t), 
        //         Vector3.Lerp(
        //             Vector3.Lerp(controlPointStart, controlPointEnd, t), 
        //             Vector3.Lerp(controlPointEnd, endPoint, t), t), 
        //             t);

        // * Method 04
        // var AB = Vector3.Lerp(startPoint, controlPointStart, t);
        // var BC = Vector3.Lerp(controlPointStart, controlPointEnd, t);
        // var CD = Vector3.Lerp(controlPointEnd, endPoint, t);

        // return QuadraticBezierCurvePoint(AB, CD, BC, t);

        // * Method 05
        return QuadraticPoint(
            Vector3.Lerp(startPoint, controlPointStart, t), 
            Vector3.Lerp(controlPointEnd, endPoint, t),
            Vector3.Lerp(controlPointStart, controlPointEnd, t), t);
    }
}

 将默认需要使用的能量设置为25。

制作能量使用第②部分闪避:

 再制作一个动作表绑定为鼠标中键

  public event UnityAction onOverDirve = delegate { };
public void OnOverdrive(InputAction.CallbackContext context)
    {
        if(context.phase == InputActionPhase.Performed)
        {
            onOverDirve.Invoke();
        }
    }

 然后我们再扩展PlayerEnergy脚本

using System.Collections;
using UnityEngine;

public class PlayerEnergy : Singleton<PlayerEnergy>
{
    [SerializeField] float overdriveInterval = 0.1f;
    [SerializeField] EnergyBar energyBar;
    public const int MAX = 100;
    public const int PERCENT = 1;
    int energy;

    bool avaliable = true; //当处于能量爆发时为false

    WaitForSeconds waitForOverdriveInterval;

    protected override void Awake()
    {
        waitForOverdriveInterval = new WaitForSeconds(overdriveInterval);
        base.Awake();
    }
    private void OnEnable()
    {
        PlayerOverdirve.on += PlayerOverdriveOn;
        PlayerOverdirve.off += PlayerOverdriveOff;
    }

    private void OnDisable()
    {
        PlayerOverdirve.on -= PlayerOverdriveOn;
        PlayerOverdirve.off -= PlayerOverdriveOff;
    }
    void Start()
    {
        energyBar.Initialize(energy, MAX);
        Obtain(75);
    }

    public void Obtain(int value) //获得能量
    {
        if (energy == MAX || !gameObject.activeSelf || !avaliable) return;

        energy = Mathf.Clamp(energy + value, 0, MAX);
        energyBar.UpdateStates(energy, MAX); //更新能量条
    }

    public void Use(int value)
    {
        energy -= value;
        energyBar.UpdateStates(energy, MAX);

        if(energy == 0 && !avaliable)
        {
            PlayerOverdirve.off.Invoke();
        }
    }

    public bool IsEnough(int value) => energy >= value; //判断能量是否能使用

    void PlayerOverdriveOn()
    {
        avaliable = false;
        StartCoroutine(nameof(KeepUsingCoroutine));
    }

    void PlayerOverdriveOff()
    {
        avaliable = true;
        StopCoroutine(nameof(KeepUsingCoroutine));
    }

    IEnumerator KeepUsingCoroutine()
    {
        while(gameObject.activeSelf && energy > 0)
        {
            yield return waitForOverdriveInterval;

            Use(PERCENT);
        }
    }
}

然后再为我们的Player创建一个叫PlayerOverDrive的脚本,

这里不用event事件是方便委托在其它类函数中被调用。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class PlayerOverdirve : MonoBehaviour
{
    //在其它类中被调用
    public static UnityAction on = delegate { }; //委托能量的开启
    public static UnityAction off = delegate { }; //委托能量的开启

    [SerializeField] GameObject triggerVFX;
    [SerializeField] GameObject engineVFXNormal;
    [SerializeField] GameObject engineVFXOverdrive;

    [SerializeField] AudioData onSFX;
    [SerializeField] AudioData offSFX;
    private void Awake()
    {
        on += On;
        off += Off;
    }
    private void OnDisable()
    {
        on -= On;
        off -= Off;
    }

    void On()
    {
        triggerVFX.SetActive(true);
        engineVFXNormal.SetActive(false);
        engineVFXOverdrive.SetActive(true);
        AudioManager.Instance.PlayRandomSFX(onSFX);
    }
    void Off()
    {
        engineVFXNormal.SetActive(true);
        engineVFXOverdrive.SetActive(false);
        AudioManager.Instance.PlayRandomSFX(offSFX);
    }
}

在Player脚本中添加相关Overdrive相关的脚本

这里将完整的Player脚本拖出来,大家可以复制粘贴然后自行看看Overdrive相关的脚本

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Player : Character
{
    [SerializeField] StateBar_HUD stateBar_HUD;

    [SerializeField] bool genearatedHealth = true;
    [SerializeField] float healthGenerateTime;
    [SerializeField,Range(0f,1f)] float healthRegeneatePercent;

    [Header("--- INPUT ---")]
    [SerializeField] PlayerInput input;

    [Header("--- MOVE ---")]
    [SerializeField] float accelarationTime = 3f;
    [SerializeField] float decelarationTime = 3f;
    [SerializeField] float moveSpeed = 10f;
    [SerializeField] float moveRotatinAngle = 50f;

    float paddingX;
    float paddingY;

    [SerializeField] float fireInterval= 0.2f;

    float t;
    readonly float slowMotionDuration = 1f;

    
    Vector2 previousVelocity;
    Quaternion previousRotation;

    [Header("--- FIRE ---")]
    [SerializeField] float keepingDuration = 5f;
    [SerializeField] GameObject projectTile1;
    [SerializeField] GameObject projectTile2;
    [SerializeField] GameObject projectTile3;
    [SerializeField] GameObject projectTileOverdirve;

    [SerializeField] AudioData projectileLaunchSFX;

    [SerializeField, Range(0, 2)] int weaponPower = 0;

    [SerializeField] Transform muzlleMiddle;
    [SerializeField] Transform muzzleTop;
    [SerializeField] Transform muzzleBottom;

    Rigidbody2D rigi2D;

    new Collider2D collider;

    [Header("--- COROUTINE ---")]

    Coroutine moveCoroutine;
    Coroutine healthRegenerateCoroutine;

    WaitForSeconds waitDeceletationTime;
    WaitForSeconds waitForFireInterval;
    WaitForSeconds waitForOverdriveFireInterval;
    WaitForSeconds waitHealthGeneratedTime;

    [Header("--- DODGE ---")]
    [SerializeField] int dodgeEnergyCost = 25;
    [SerializeField] float maxRoll = 360f;
    [SerializeField] float rollSpeed = 360f;
    [SerializeField] Vector3 dodgeScale = new Vector3(0.5f, 0.5f, 0.5f);

    [SerializeField] AudioData dodgeSFX;

    bool isDodging = false;

    float currentRoll;
    float dodgeDuration;

    [Header("--- OVERDRIVE ---")]
    [SerializeField] int overdriveDodgeFactor = 2;
    [SerializeField] float overdriveFireFactor = 1.2f;
    [SerializeField] float overdriveSpeedFactor = 1.2f;


    bool isOverdirving = false;

    private void Awake()
    {
        rigi2D = GetComponent<Rigidbody2D>();
        collider = GetComponent<Collider2D>();

        var size = transform.GetChild(0).GetComponent<Renderer>().bounds.size;
        paddingX = size.x / 2;
        paddingY = size.y / 2;


        dodgeDuration = maxRoll / rollSpeed;
        rigi2D.gravityScale = 0f;

        waitForFireInterval = new WaitForSeconds(fireInterval);
        waitForOverdriveFireInterval = new WaitForSeconds(fireInterval /= overdriveFireFactor);
        waitHealthGeneratedTime = new WaitForSeconds(healthGenerateTime);
        waitDeceletationTime = new WaitForSeconds(decelarationTime);
    }
    protected override void OnEnable()
    {
        //增加委托/订阅事件
        base.OnEnable();
        input.onMove += Move;
        input.onStopMove += StopMove;
        input.onFire += Fire;
        input.onStopFire += StopFire;
        input.onDodge += Dodge;
        input.onOverDirve += Overdrive;

        PlayerOverdirve.on += OverdriveOn;
        PlayerOverdirve.off += OverdriveOff;

    }
    private void OnDisable()
    {
        //取消委托
        input.onMove -= Move;
        input.onStopMove -= StopMove;
        input.onFire -= Fire;
        input.onStopFire -= StopFire;
        input.onDodge -= Dodge;
        input.onOverDirve -= Overdrive;

        PlayerOverdirve.on -= OverdriveOn;
        PlayerOverdirve.off -= OverdriveOff;
    }
    void Start()
    {
        rigi2D.gravityScale = 0f;

        waitForFireInterval = new WaitForSeconds(fireInterval);
        waitHealthGeneratedTime = new WaitForSeconds(healthGenerateTime);

        stateBar_HUD.Initialize(health, maxHealth);

        input.EnableGamePlayInput(); //激活动作表
    }


    public override void TakeDamage(float damage)
    {
        base.TakeDamage(damage);
        stateBar_HUD.UpdateStates(health, maxHealth);
        TimeController.Instance.BulletTime(slowMotionDuration);

        if (gameObject.activeSelf)
        {
            if(healthRegenerateCoroutine != null)
            {
                StopCoroutine(healthRegenerateCoroutine);
            }

            healthRegenerateCoroutine = StartCoroutine(HealthPercentageCoroutine(waitHealthGeneratedTime, healthRegeneatePercent));

        }
    }

    public override void RestoreHealth(float value)
    {
        base.RestoreHealth(value);
        stateBar_HUD.UpdateStates(health, maxHealth);
    }
    public override void Die()
    {
        stateBar_HUD.UpdateStates(0f, maxHealth);
        base.Die();
    }

    #region WALK
    void Move(Vector2 moveInput) //就是你输入信号的二维值
    {
        //Vector2 moveAmount = moveInput * moveSpeed;
        //rigi2D.velocity = moveAmount;
        if (moveCoroutine != null)
        {
            StopCoroutine(moveCoroutine);
        }

        Quaternion moveRotation = Quaternion.AngleAxis(moveRotatinAngle * moveInput.y,Vector3.right); //right即红色的X轴,初始旋转角度
        moveCoroutine=StartCoroutine(MoveCoroutine(accelarationTime,(moveInput.normalized * moveSpeed),moveRotation));
        StopCoroutine(nameof(DeceletationCoroutine));
        StartCoroutine(nameof(MoveRangeLimitationCoroutine));

    }
    void StopMove()
    {
        //rigi2D.velocity = Vector2.zero;
        if (moveCoroutine != null)
        {
            StopCoroutine(moveCoroutine);
        }
        moveCoroutine = StartCoroutine(MoveCoroutine(decelarationTime,Vector2.zero, Quaternion.identity));
        StartCoroutine(nameof(DeceletationCoroutine));
    }

    IEnumerator MoveCoroutine(float time,Vector2 moveVelocity,Quaternion moveRotation)
    {
        t = 0f;
        previousVelocity = rigi2D.velocity;
        previousRotation = transform.rotation;

        while(t< 1f)
        {
            t += Time.fixedDeltaTime / time;

            rigi2D.velocity = Vector2.Lerp(previousVelocity, moveVelocity,t);
            transform.rotation = Quaternion.Lerp(previousRotation, moveRotation,t);

            yield return new WaitForFixedUpdate();
        }
    }
    
    IEnumerator MoveRangeLimitationCoroutine()
    {
        while (true)
        {
            transform.position = ViewPort.Instance.PlayerMoveablePosition(transform.position,paddingX,paddingY);

            yield return null;
        }
    }

    IEnumerator DeceletationCoroutine()
    {
        yield return waitDeceletationTime;

        StopCoroutine(nameof(MoveRangeLimitationCoroutine));
    }
    #endregion

    #region FIRE

    void Fire()
    {
        StartCoroutine(nameof(FireCoroutine));
    }

    void StopFire()
    {
        StopCoroutine(nameof(FireCoroutine));
    }

    IEnumerator FireCoroutine()
    {
        while (true)
        {
            

            switch (weaponPower)
            {
                case 0:
                    PoolManager.Release(isOverdirving?projectTileOverdirve:projectTile1, muzlleMiddle.position, Quaternion.identity);
                    break;
                case 1:
                    PoolManager.Release(isOverdirving ? projectTileOverdirve : projectTile1, muzzleTop.position, Quaternion.identity);
                    PoolManager.Release(isOverdirving ? projectTileOverdirve : projectTile1, muzzleBottom.position, Quaternion.identity);
                    break;
                case 2:
                    PoolManager.Release(isOverdirving ? projectTileOverdirve : projectTile1, muzzleTop.position, Quaternion.identity);
                    PoolManager.Release(isOverdirving ? projectTileOverdirve : projectTile2, muzlleMiddle.position, Quaternion.identity);
                    PoolManager.Release(isOverdirving ? projectTileOverdirve : projectTile3, muzzleBottom.position, Quaternion.identity);
                    break;
                default:break;
            }

            AudioManager.Instance.PlayRandomSFX(projectileLaunchSFX);

            yield return isOverdirving? waitForOverdriveFireInterval : waitForFireInterval;
        }
    }

    #endregion

    #region DODGE
    void Dodge()
    {
        if (isDodging || !PlayerEnergy.Instance.IsEnough(dodgeEnergyCost)) return;
        StartCoroutine(nameof(DodgeCoroutine));
    }

    IEnumerator DodgeCoroutine()
    {
        isDodging = true;
        AudioManager.Instance.PlayRandomSFX(dodgeSFX);
        //消耗能量
        PlayerEnergy.Instance.Use(dodgeEnergyCost);
        //无敌状态
        collider.isTrigger = true;

        currentRoll = 0f;
        TimeController.Instance.BulletTime(slowMotionDuration, slowMotionDuration);

        var scale = transform.localScale;
        while(currentRoll < maxRoll)
        {
            currentRoll += rollSpeed * Time.deltaTime;
            //让玩家绕着X轴旋转
            transform.rotation = Quaternion.AngleAxis(currentRoll, Vector3.right);

            BezierCurve.QuadraticPoint(Vector3.one, Vector3.one,dodgeScale,currentRoll / maxRoll);
            //if(currentRoll < maxRoll / 2f)
            //{
            //    scale.x = Mathf.Clamp(scale.x - Time.deltaTime / dodgeDuration, dodgeScale.x, 1f);
            //    scale.y = Mathf.Clamp(scale.y - Time.deltaTime / dodgeDuration, dodgeScale.y, 1f);
            //    scale.z = Mathf.Clamp(scale.z - Time.deltaTime / dodgeDuration, dodgeScale.z, 1f);
            //}
            //else
            //{
            //    scale.x = Mathf.Clamp(scale.x + Time.deltaTime / dodgeDuration, dodgeScale.x, 1f);
            //    scale.y = Mathf.Clamp(scale.y + Time.deltaTime / dodgeDuration, dodgeScale.y, 1f);
            //    scale.z = Mathf.Clamp(scale.z + Time.deltaTime / dodgeDuration, dodgeScale.z, 1f);
            //}
            //transform.localScale = scale;

            yield return null;
        }

        collider.isTrigger = false;
        isDodging = false;
    }
    #endregion

    #region OVERDRIVE

    void Overdrive()
    {
        if (!PlayerEnergy.Instance.IsEnough(PlayerEnergy.MAX)) return;

        PlayerOverdirve.on.Invoke();
    }

    void OverdriveOn()
    {
        isOverdirving = true;
        moveSpeed *= overdriveFireFactor;
        dodgeEnergyCost *= overdriveDodgeFactor;
        TimeController.Instance.BulletTime(slowMotionDuration,keepingDuration,slowMotionDuration);

    }

    void OverdriveOff()
    {
        isOverdirving = false;
        moveSpeed /= overdriveFireFactor;
        dodgeEnergyCost /= overdriveDodgeFactor;
    }

    #endregion
}

然后设置相应的参数。 


学习产出:

 

 

猜你喜欢

转载自blog.csdn.net/dangoxiba/article/details/124825190
今日推荐