Unity类银河恶魔城学习记录10-4 p92 Death of entity源代码

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

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

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

public class PlayerStat : CharacterStats
{
    private Player player;
    

    

    protected override void Start()
    {
        player = GetComponent<Player>();
        base.Start();
    }
    public override void DoDamage(CharacterStats _targetStats)
    {
        base.DoDamage(_targetStats);

    }
    protected override void TakeDamage(int _damage)
    {
        base.TakeDamage(_damage);
        player.DamageEffect();
    }
    protected override void Die()
    {
        base.Die();

        player.Die();
    }
}

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

public class EnemyStat : CharacterStats
{
    private Enemy enemy;
    public override void DoDamage(CharacterStats _targetStats)
    {
        base.DoDamage(_targetStats);
    }

    protected override void Die()
    {
        base.Die();
        enemy.Die();
    }

    protected override void Start()
    {
        enemy = GetComponent<Enemy>();
        base.Start();
    }

    protected override void TakeDamage(int _damage)
    {
        base.TakeDamage(_damage);

        enemy.DamageEffect();
    }
}
Entity.cs
 using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Entity : MonoBehaviour
{
    [Header("Knockback info")]
    [SerializeField] protected Vector2 knockbackDirection;//被击打后的速度信息
    [SerializeField] protected float knockbackDuration;//被击打的时间
    protected bool isKnocked;//此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况

    [Header("Collision Info")]
    public Transform attackCheck;//transform类,代表的时物体的位置,用来控制攻击检测的位置
    public float attackCheckRadius;//检测半径
    [SerializeField] protected Transform groundCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置    
    [SerializeField] protected float groundCheckDistance;
    [SerializeField] protected Transform wallCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置    
    [SerializeField] protected float wallCheckDistance;
    [SerializeField] protected LayerMask whatIsGround;//LayerMask类,与Raycast配合,https://docs.unity3d.com/cn/current/ScriptReference/Physics.Raycast.html

    #region 定义Unity组件
    public SpriteRenderer sr { get; private set; }
    public Animator anim { get; private set; }//这样才能配合着拿到自己身上的animator的控制权
    public Rigidbody2D rb { get; private set; }//配合拿到身上的Rigidbody2D组件控制权
    public EntityFX fx { get; private set; }//拿到EntityFX
    public CharacterStats stats { get; private set; }
    public CapsuleCollider2D cd { get; private set; }
    #endregion
    public int facingDir { get; private set; } = 1;
    protected bool facingRight = true;//判断是否朝右

    protected virtual void Awake()
    {
        
    }
    protected virtual void Start()
    {
        anim = GetComponentInChildren<Animator>();//拿到自己子组件身上的animator的控制权
        sr = GetComponentInChildren<SpriteRenderer>();
        fx = GetComponent<EntityFX>();拿到的组件上的EntityFX控制权
        rb = GetComponent<Rigidbody2D>();
        stats = GetComponent<CharacterStats>();
        cd = GetComponent<CapsuleCollider2D>();
    }
    protected virtual void Update()
    {

    }

    protected virtual void Exit()
    {

    }
    public virtual void DamageEffect()
    {

        fx.StartCoroutine("FlashFX");//IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
                                     //https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
        StartCoroutine("HitKnockback");//调用被击打后产生后退效果的函数
        Debug.Log(gameObject.name+"was damaged");
    }
    
    protected virtual IEnumerator HitKnockback()
    {
        isKnocked = true;//此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况
        rb.velocity = new Vector2(knockbackDirection.x * -facingDir, knockbackDirection.y);
        yield return new WaitForSeconds(knockbackDuration);
        isKnocked = false;
    }
    //被击打后产生后退效果的函数
    #region 速度函数Velocity
    public virtual void SetZeroVelocity()
    {
        if(isKnocked)
        {
            return;
        }
        rb.velocity = new Vector2(0, 0);
    }//设置速度为0函数

    public virtual void SetVelocity(float _xVelocity, float _yVelocity)
    {
        if(isKnocked)
            return;此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况

        rb.velocity = new Vector2(_xVelocity, _yVelocity);//将rb的velocity属性设置为对应的想要的二维向量。因为2D游戏的速度就是二维向量
        FlipController(_xVelocity);//在其他设置速度的时候调用翻转控制器
    }//控制速度的函数,此函数在其他State中可能会使用,但仅能通过player.SeVelocity调用
    #endregion
    #region 翻转函数Flip
    public virtual void Flip()
    {
        facingDir = facingDir * -1;
        facingRight = !facingRight;
        transform.Rotate(0, 180, 0);//旋转函数,transform不需要额外定义,因为他是自带的
    }//翻转函数

    public virtual void FlipController(float _x)//目前设置x,目的时能在空中时也能转身
    {
        if (_x > 0 && !facingRight)//当速度大于0且没有朝右时,翻转
        {
            Flip();
        }
        else if (_x < 0 && facingRight)
        {
            Flip();
        }
    }
    #endregion
    #region 碰撞函数Collision
    public virtual bool IsGroundDetected()
    {
        return Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
    }//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics2D.Raycast.html
    //xxxxxxxx()   => xxxxxxxx  == xxxxxxxxxx() return xxxxxxxxx;
    public virtual bool IsWallDetected()
    {
        return Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
    }//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics2D.Raycast.html
    //xxxxxxxx()   => xxxxxxxx  == xxxxxxxxxx() return xxxxxxxxx;
    protected virtual 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(后面的) 的线。
        Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Gizmos.DrawWireSphere.html
        //绘制具有中心和半径的线框球体。
    }//画图函数
    #endregion
    public void MakeTransprent(bool isClear)
    {
        if (isClear)
            sr.color = Color.clear;
        else
            sr.color = Color.white;
    }

    public virtual void Die()
    {

    }
}
Player.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Player : Entity
{
    [Header("Attack Details")]
    public Vector2[] attackMovement;//每个攻击时获得的速度组
    public float counterAttackDuration = .2f;
    


    public bool isBusy{ get; private set; }//防止在攻击间隔中进入move
    //
    [Header("Move Info")]
    public float moveSpeed;//定义速度,与xInput相乘控制速度的大小
    public float jumpForce;
    public float swordReturnImpact;//在player里设置swordReturnImpact作为击退的参数

    [Header("Dash Info")]
    [SerializeField] private float dashCooldown;
    private float dashUsageTimer;//为dash设置冷却时间,在一定时间内不能连续使用
    public float dashSpeed;//冲刺速度
    public float dashDuration;//持续时间
    public float dashDir { get; private set; }


    
    
    #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 PlayerDeadState deadState { get; private set; }
    


    public PlayerPrimaryAttackState primaryAttack { get; private set; }
    public PlayerCounterAttackState counterAttack { get; private set; }

    public PlayerAimSwordState aimSword { get; private set; }
    public PlayerCatchSwordState catchSword { get; private set; }
    public PlayerBlackholeState blackhole { get; private set; }


    public SkillManager skill { get; private set; }
    public GameObject sword{ get; private set; }//声明sword


   
    #endregion
    protected override void Awake()
    {

        base.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动画
        deadState = new PlayerDeadState(this, stateMachine, "Die");


        primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
        counterAttack = new PlayerCounterAttackState(this, stateMachine, "CounterAttack");

        aimSword = new PlayerAimSwordState(this,stateMachine, "AimSword");
        catchSword = new PlayerCatchSwordState(this, stateMachine, "CatchSword");
        blackhole = new PlayerBlackholeState(this, stateMachine, "Jump");

        //this 就是 Player这个类本身
    }//Awake初始化所以State,为所有State传入各自独有的参数,及animBool,以判断是否调用此动画(与animatoin配合完成)
    protected override void Start()
    {
        base.Start();
        stateMachine.Initialize(idleState);
        skill = SkillManager.instance;

    }

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

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

        if(Input.GetKeyDown(KeyCode.F))
        {
            skill.crystal.CanUseSkill();
        }
        
    }
    public void AssignNewSword(GameObject _newSword)//保持创造的sword实例的函数
    {
        sword = _newSword;
    }

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

    public void CheckForDashInput()
    {

        
        if (IsWallDetected())
        {
            return;
        }//修复在wallslide可以dash的BUG
        if (Input.GetKeyDown(KeyCode.LeftShift) && skill.dash.CanUseSkill())//将DashTimer<0 的判断 改成DashSkill里的判断
        {

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

    }//将Dash切换设置成一个函数,使其在所以情况下都能使用


    public override void Die()
    {
        base.Die();

        stateMachine.ChangeState(deadState);
    }
}
PlayerDeadState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerDeadState : PlayerState
{
    public PlayerDeadState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
    {
    }

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

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

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

    public override void Update()
    {
        base.Update();

        player.SetZeroVelocity();
    }
}

Enemy.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Enemy : Entity
{
    [SerializeField] protected LayerMask whatIsPlayer;
    [Header("Stun Info")]
    public float stunnedDuration;//stunned持续时间
    public Vector2 stunnedDirection;//stunned改变后的速度
    protected bool canBeStunned;//判断是否可以被反击
    [SerializeField] protected GameObject counterImage;//一个代表着是否可以被反击的信号

    [Header("Move Info")]
    public float moveSpeed;
    public float idleTime;
    public float battleTime;//多久能从battle状态中退出来
    private float defaultMoveSpeed;

    [Header("Attack Info")]
    public float attackDistance;
    public float attackCooldown;//攻击冷却
    [HideInInspector] public float lastTimeAttacked;//最后一次攻击的时间
    

    #region 类
    public EnemyStateMachine stateMachine { get; private set; }

    public string lastingAnimBoolName{ get; private set; }

    public virtual void AssignLastAnimName(string _animBoolName)
    {
        lastingAnimBoolName = _animBoolName;
    }
    #endregion

    protected override void Awake()
    {
        base.Awake();
        stateMachine = new EnemyStateMachine();
        defaultMoveSpeed = moveSpeed;

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

    }


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

        stateMachine.currentState.Update();

        //Debug.Log(IsPlayerDetected().collider.gameObject.name + "I see");//这串代码会报错,可能使版本的物体,因为在没有找到Player的时候物体是空的,NULL,你想让他在控制台上显示就报错了
    }

    public virtual void FreezeTime(bool _timeFrozen)
    {
        if(_timeFrozen)
        {
            moveSpeed = 0;
            anim.speed = 0;
        }
        else
        {
            moveSpeed = defaultMoveSpeed;
            anim.speed = 1;
        }
    }

    protected virtual IEnumerator FreezeTimeFor(float _seconds)
    {
        FreezeTime(true);

        yield return new WaitForSeconds(_seconds);

        FreezeTime(false);
    }
    #region Counter Attack Window
    public virtual void OpenCounterAttackWindow()//打开可以反击的信号的函数
    {
        canBeStunned = true;
        counterImage.SetActive(true);
    }
    public virtual void CloseCounterAttackWindow()//关闭可以反击的信号的函数
    {
        canBeStunned = false;
        counterImage.SetActive(false);
    }
    #endregion


    
    public virtual bool CanBeStunned()//这里的主要目的是完成在被反击过后能立刻关闭提示窗口
    {
        if (canBeStunned)
        {
            CloseCounterAttackWindow();
            return true;
        }
        return false;

    }
    public virtual void AnimationFinishTrigger() => stateMachine.currentState.AnimationFinishTrigger();//动画完成时调用的函数,与Player相同
    public virtual RaycastHit2D IsPlayerDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, 7, whatIsPlayer);//用于从射线投射获取信息的结构。
    //该函数的返回值可以变,可以只返回bool,也可以是碰到的结构

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

        Gizmos.color = Color.yellow;//把线改成黄色
        Gizmos.DrawLine(transform.position, new Vector3(transform.position.x + attackDistance * facingDir, transform.position.y));//用来判别是否进入attackState的线
    }


}

Enemy_Skeleton.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyState
{

    protected Enemy enemyBase;

    protected EnemyStateMachine stateMachine;

    protected Rigidbody2D rb;//小简化

    protected bool triggerCalled;

    private string animBoolName;

    protected float stateTimer;


    public EnemyState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName)
    {
        this.enemyBase = _enemyBase;
        this.stateMachine = _stateMachine;
        this.animBoolName = _animBoolName;
    }
    public virtual void Enter()
    {
        triggerCalled = false;
        enemyBase.anim.SetBool(animBoolName, true);
        rb = enemyBase.rb;//小简化
    }
    public virtual void Update()
    {
        stateTimer -= Time.deltaTime;
    }
    public virtual void Exit()
    {
        enemyBase.anim.SetBool(animBoolName, false);

        enemyBase.AssignLastAnimName(animBoolName);
    }

    //动画完成时调用的函数,与Player相同
    public virtual void AnimationFinishTrigger()
    {
        triggerCalled = true;
    }
}

EnemyState.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditorInternal;
using UnityEngine;

public class Enemy_Skeleton : Enemy
{
    #region 类State
    public SkeletonIdleState idleState { get; private set; }
    public SkeletonMoveState moveState { get; private set; }
    public SkeletonBattleState battleState { get; private set; }
    public SkeletonAttackState attackState { get; private set; }
    public SkeletonStunnedState stunnedState { get; private set; }
    public SkeletonDeadState deadState { get; private set; }
    #endregion
    protected override void Awake()
    {
        base.Awake();

        idleState = new SkeletonIdleState(this, stateMachine, "Idle", this);
        moveState = new SkeletonMoveState(this,stateMachine, "Move", this);
        battleState = new SkeletonBattleState(this, stateMachine, "Move", this);
        attackState = new SkeletonAttackState(this, stateMachine, "Attack", this);
        stunnedState = new SkeletonStunnedState(this, stateMachine, "Stunned", this);
        deadState = new SkeletonDeadState(this, stateMachine, "Idle", this);

    }

    protected override void Start()
    {
        base.Start();
        stateMachine.Initialize(idleState);
    }

    protected override void Update()
    {
        base.Update();
        
        
    }

    public override bool CanBeStunned()
    {
        if (base.CanBeStunned())//在这里重写主要是因为只有在Skeleton里面才能调用stunnedState
        {
            stateMachine.ChangeState(stunnedState);
            return true;
        }
        return false;
    }
    //重写后不仅能达成在完成被反击后关闭提示窗口,还能转换为stunnedState
    public override void Die()
    {
        base.Die();
        stateMachine.ChangeState(deadState);
    }
}
SkeletonDeadState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkeletonDeadState : EnemyState
{
    private Enemy_Skeleton enemy;
    public SkeletonDeadState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
    {
        this.enemy = _enemy;
    }

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

        enemy.anim.SetBool(enemy.lastingAnimBoolName, true);
        enemy.anim.speed = 0;
        enemy.cd.enabled = false;

        stateTimer = .1f;
    }

    public override void Update()
    {
        base.Update();

        if (stateTimer > 0)
            rb.velocity = new Vector2(0, 10);
    }
}

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-16 22:06:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-16 22:06:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-16 22:06:04       82 阅读
  4. Python语言-面向对象

    2024-03-16 22:06:04       91 阅读

热门阅读

  1. Linux-网络基础

    2024-03-16 22:06:04       42 阅读
  2. Redis 慢查询是什么?如何配置使用

    2024-03-16 22:06:04       40 阅读
  3. 通过服务器配置cros来解决跨域问题

    2024-03-16 22:06:04       42 阅读
  4. ARTS Week 21

    2024-03-16 22:06:04       42 阅读
  5. 【记录】Git A项目作为产品,B项目继承A项目

    2024-03-16 22:06:04       38 阅读
  6. 如何将csv文件转换为markdown表格

    2024-03-16 22:06:04       38 阅读
  7. 什么是去中心化,如何去中心化

    2024-03-16 22:06:04       34 阅读
  8. 安装elasticsearch、kibana

    2024-03-16 22:06:04       44 阅读
  9. 架构师六大派Solid

    2024-03-16 22:06:04       40 阅读