Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
Enemy.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Enemy : Entity
{
[SerializeField] protected LayerMask whatIsPlayer;
[Header("Move Info")]
public float moveSpeed;
public float idleTime;
[Header("Attack Info")]
public float attackDistance;
public float attackCooldown;//攻击冷却
[HideInInspector]public float lastTimeAttacked;//最后一次攻击的时间
#region 类
public EnemyStateMachine stateMachine;
#endregion
protected override void Awake()
{
base.Awake();
stateMachine = new EnemyStateMachine();
}
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 AnimationFinishTrigger() => stateMachine.currentState.AnimationFinishTrigger();//动画完成时调用的函数,与Player相同
public virtual RaycastHit2D IsPlayerDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, 50, 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));
}
}
Enemy_Skeleton.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; }
#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);
}
protected override void Start()
{
base.Start();
stateMachine.Initialize(idleState);
}
protected override void Update()
{
base.Update();
}
}
EnemyState.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);
}
//动画完成时调用的函数,与Player相同
public virtual void AnimationFinishTrigger()
{
triggerCalled = true;
}
}
SkeletonBattleState.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class SkeletonBattleState : EnemyState
{
private Transform player;//用于给Player定位,好判断怎么跟上他
private Enemy_Skeleton enemy;
private int moveDir;
public SkeletonBattleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy ) : base(_enemyBase, _stateMachine, _animBoolName)
{
enemy = _enemy;
}
public override void Enter()
{
base.Enter();
player = GameObject.Find("Player").transform;//全局找Player位置
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
//退出此状态的方式
if(enemy.IsPlayerDetected().distance < enemy.attackDistance)//当距离小于攻击距离,变为攻击状态
{
if(CanAttack())
stateMachine.ChangeState(enemy.attackState);
}
//下面为移动方向设置
if(player.position.x > enemy.transform.position.x)//在右,向右移动
{
moveDir = 1;
}
else if(player.position.x<enemy.transform.position.x)//在左,向左移动
{
moveDir = -1;
}
enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);
}
private bool CanAttack()
{
if(Time.time > enemy.lastTimeAttacked + enemy.attackCooldown)
{
enemy.lastTimeAttacked = Time.time;
return true;
}
Debug.Log("Attack is on cooldown");
return false;
}
}
SkeletonAttackState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SkeletonAttackState : EnemyState
{
private Enemy_Skeleton enemy;
public SkeletonAttackState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
enemy = _enemy;
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
enemy.lastTimeAttacked = Time.time;//记录攻击的最后时间以便于判断是否能够进入攻击状态
}
public override void Update()
{
base.Update();
enemy.SetZeroVelocity();//使速度为0
if(triggerCalled)
{
stateMachine.ChangeState(enemy.battleState);
}//给退出条件,在动画完成之后
}
}
Enemy_SkeletonAnimationTrigger.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy_SkeletonAnimationTriggers : MonoBehaviour
{
private Enemy_Skeleton enemy => GetComponentInParent<Enemy_Skeleton>();//拿到enemy实体
private void AnimationTrigger()
{
enemy.AnimationFinishTrigger();//调用实体上的函数,使triggerCalled为true;
}
}