Unity类银河恶魔城学习记录1-13PlayerAttack-Improve P40

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考

此代码仅为较上一P有所改变的代码

https://www.bilibili.com/video/BV1cM4y1p7RF/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click

Player.cs
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Player : MonoBehaviour
{
    [Header("Attack Details")]
    public Vector2[] attackMovement;//每个攻击时获得的速度组
    public bool isBusy{ get; private set; }//防止在攻击间隔中进入move
    //
    [Header("Move Info")]
    public float moveSpeed;//定义速度,与xInput相乘控制速度的大小
    public float jumpForce;
    [Header("Dash Info")]
    [SerializeField] private float dashCooldown;
    private float dashUsageTimer;//为dash设置冷却时间,在一定时间内不能连续使用
    public float dashSpeed;//冲刺速度
    public float dashDuration;//持续时间
    public float dashDir { get; private set; }
    [Header("Collision Info")]
    [SerializeField] private Transform groundCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置    
    [SerializeField] private float groundCheckDistance;
    [SerializeField] private Transform wallCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置    
    [SerializeField] private float wallCheckDistance;
    [SerializeField] private LayerMask whatIsGround;//LayerMask类,与Raycast配合,https://docs.unity3d.com/cn/current/ScriptReference/Physics.Raycast.html

    public int facingDir { get; private set; } = 1;
    private bool facingRight = true;//判断是否朝右
    #region 定义Unity组件
    public Animator anim { get; private set; }//这样才能配合着拿到自己身上的animator的控制权
    public Rigidbody2D rb { get; private set; }//配合拿到身上的Rigidbody2D组件控制权
    #endregion
    #region 定义States
    public PlayerStateMachine stateMachine { get; private set; }
    public PlayerIdleState idleState { get; private set; }
    public PlayerMoveState moveState { get; private set; }
    public PlayerJumpState jumpState { get; private set; }
    public PlayerAirState airState { get; private set; }
    public PlayerDashState dashState { get; private set; }
    public PlayerWallSlideState wallSlide { get; private set; }
    public PlayerWallJumpState wallJump { get; private set; }


    public PlayerPrimaryAttack primaryAttack { get; private set; }
    #endregion
    private void Awake()
    {
        stateMachine = new PlayerStateMachine();
        //通过构造函数,在构造时传递信息
        idleState = new PlayerIdleState(this, stateMachine, "Idle");
        moveState = new PlayerMoveState(this, stateMachine, "Move");
        jumpState = new PlayerJumpState(this, stateMachine, "Jump");
        airState = new PlayerAirState(this, stateMachine, "Jump");
        dashState = new PlayerDashState(this, stateMachine, "Dash");
        wallSlide = new PlayerWallSlideState(this, stateMachine, "WallSlide");
        wallJump = new PlayerWallJumpState(this, stateMachine, "Jump");//wallJump也是Jump动画


        primaryAttack = new PlayerPrimaryAttack(this, stateMachine, "Attack");
        //this 就是 Player这个类本身
    }//Awake初始化所以State,为所有State传入各自独有的参数,及animBool,以判断是否调用此动画(与animatoin配合完成)
    private void Start()
    {
        anim = GetComponentInChildren<Animator>();//拿到自己身上的animator的控制权
        rb = GetComponent<Rigidbody2D>();
        stateMachine.Initialize(idleState);

    }

    private void Update()//在mano中update会自动刷新但其他没有mano的不会故,需要在这个updata中调用其他脚本中的函数stateMachine.currentState.update以实现 //stateMachine中的update

    {
        stateMachine.currentState.Update();//反复调用CurrentState的Update函数
        CheckForDashInput();
        
    }

    public IEnumerator BusyFor(float _seconds)//https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
    {
        isBusy = true;
        Debug.Log("IsBusy");
        yield return new WaitForSeconds(_seconds);
        isBusy = false;
        Debug.Log("NotBusy");
    }//p39 4.防止在攻击间隔中进入move,通过设置busy值,在使用某些状态时,使其为busy为true,抑制其进入其他state
     //IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
    public void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();
    //从当前状态拿到AnimationTrigger进行调用的函数

    public void CheckForDashInput()
    {

        dashUsageTimer -= Time.deltaTime;//给dash上冷却时间
        if (IsWallDetected())
        {
            return;
        }//修复在wallslide可以dash的BUG
        if (Input.GetKeyDown(KeyCode.LeftShift) && dashUsageTimer < 0)
        {

            dashUsageTimer = dashCooldown;
            dashDir = Input.GetAxisRaw("Horizontal");//设置一个值,可以将dash的方向改为你想要的方向而不是你的朝向
            if (dashDir == 0)
            {
                dashDir = facingDir;//只有当玩家没有控制方向时才使用默认朝向
            }
            stateMachine.ChangeState(dashState);
        }

    }//将Dash切换设置成一个函数,使其在所以情况下都能使用
    #region 速度函数Velocity
    public void ZeroVelocity()
    {
        rb.velocity = new Vector2(0, 0);
    }//设置速度为0函数
    
    public void SetVelocity(float _xVelocity, float _yVelocity)
    {
        rb.velocity = new Vector2(_xVelocity, _yVelocity);//将rb的velocity属性设置为对应的想要的二维向量。因为2D游戏的速度就是二维向量
        FlipController(_xVelocity);//在其他设置速度的时候调用翻转控制器
    }//控制速度的函数,此函数在其他State中可能会使用,但仅能通过player.SeVelocity调用
     #endregion
    #region 碰撞函数Collision
    public bool IsGroundDetected()
    {
        return Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
    }//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics.Raycast.html
    //xxxxxxxx()   => xxxxxxxx  == xxxxxxxxxx() return xxxxxxxxx;
    public bool IsWallDetected()
    {
        return Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
    }//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics.Raycast.html
    //xxxxxxxx()   => xxxxxxxx  == xxxxxxxxxx() return xxxxxxxxx;
    private void OnDrawGizmos()
    {
        Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。
        Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。
    }//画线函数
    #endregion
    #region 翻转函数Flip
    public void Flip()
    {
        facingDir = facingDir * -1;
        facingRight = !facingRight;
        transform.Rotate(0, 180, 0);//旋转函数,transform不需要额外定义,因为他是自带的
    }//翻转函数

    public void FlipController(float _x)//目前设置x,目的时能在空中时也能转身
    {
        if (_x > 0 && !facingRight)//当速度大于0且没有朝右时,翻转
        {
            Flip();
        }
        else if (_x < 0 && facingRight)
        {
            Flip();
        }
    }
    #endregion
}
PlayerIdleState.cs
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerIdleState : PlayerGroundState//继承函数,获得PlayerState里的所有函数和参数
{
    public PlayerIdleState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
    {
    }//构造函数,用于传递信息。
     //当外补New出对象时,New出的对象里传入参数

    public override void Enter()
    {

        base.Enter();
        player.ZeroVelocity();//因为速度是没在wallJump里改,所以进入idle写死速度为0
    }

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

    public override void Update()
    {
        base.Update();
        //-------------------------------
        if (player.IsWallDetected())
        {
            if (xInput != 0 && xInput != player.facingDir)
            {
                stateMachine.ChangeState(player.moveState);//在这里我们能使用Player里的东西,主要是因为我们通过构造函数传过来Player实体,如果不传,则无法使用已经存在了的Player实体。
            }

        }
        else
        {
            if (xInput != 0 && !player.isBusy)//设置BusyBool值,在一定时间内使其无法从idle-》move
            {
                stateMachine.ChangeState(player.moveState);
            }
        }
        //--------------------------------
        //以上代码为自我改良版,改良了在碰墙时,idle和move会交替卡住的状况


    }
}
PlayerPrimaryAttack.cs
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerPrimaryAttack : PlayerState
{
    //p38 2.从ground进入

    private int comboCounter;

    private float lastTimeAttacked;//距离上一次攻击的时间
    private float comboWindow = 2;//可以间隔的时间
    public PlayerPrimaryAttack(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
    {
    }

    public override void Enter()
    {
        base.Enter();
        if(comboCounter >2||Time.time>comboWindow+lastTimeAttacked)//当计数器超过2和间隔时间大于window时,进入第一个攻击动作
        {
            comboCounter = 0;
        }
        Debug.Log(comboCounter);

        player.anim.SetInteger("ComboCounter", comboCounter);//设置animtor里的comboCounter

        player.SetVelocity(player.attackMovement[comboCounter].x * player.facingDir, player.attackMovement[comboCounter].y);//给角色初速度,让角色在攻击触发时移动一点

        stateTimer = .1f;
    }

    public override void Exit()
    {
        base.Exit();
        player.StartCoroutine("BusyFor", .15f);
        comboCounter++;
        lastTimeAttacked = Time.time;
    }

    public override void Update()
    {
        base.Update();
        if(stateTimer<0)
        {
            player.ZeroVelocity();
        }//1.修改移动时攻击时后可以移动的BUG
        //2.但给了点时间模拟惯性可以动一点
        if (triggerCalled)
        {
            stateMachine.ChangeState(player.idleState);
        }
    }
}
相关推荐
erxij4 分钟前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
HC1825808583221 分钟前
“倒时差”用英语怎么说?生活英语口语学习柯桥外语培训
学习·生活
学习路上_write25 分钟前
FPGA/Verilog,Quartus环境下if-else语句和case语句RT视图对比/学习记录
单片机·嵌入式硬件·qt·学习·fpga开发·github·硬件工程
非概念31 分钟前
stm32学习笔记----51单片机和stm32单片机的区别
笔记·stm32·单片机·学习·51单片机
wyh要好好学习1 小时前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech1 小时前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#
咩咩觉主2 小时前
尽量通俗易懂地概述.Net && U nity跨语言/跨平台相关知识
unity·c#·.net·.netcore
无敌最俊朗@2 小时前
stm32学习之路——八种GPIO口工作模式
c语言·stm32·单片机·学习
yngsqq2 小时前
035集——BOUNDARY获取图形外轮廓(CAD—C#二次开发入门)
开发语言·javascript·c#
EterNity_TiMe_2 小时前
【论文复现】STM32设计的物联网智能鱼缸
stm32·单片机·嵌入式硬件·物联网·学习·性能优化