Entity.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Entity : MonoBehaviour
{
[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 Animator anim { get; private set; }//这样才能配合着拿到自己身上的animator的控制权
public Rigidbody2D rb { get; private set; }//配合拿到身上的Rigidbody2D组件控制权
#endregion
public int facingDir { get; private set; } = 1;
protected bool facingRight = true;//判断是否朝右
protected virtual void Awake()
{
anim = GetComponentInChildren<Animator>();//拿到自己身上的animator的控制权
rb = GetComponent<Rigidbody2D>();
}
protected virtual void Start()
{
}
protected virtual void Update()
{
}
public virtual void Damage()
{
Debug.Log(gameObject.name+"was damaged");
}
#region 速度函数Velocity
public virtual void SetZeroVelocity()
{
rb.velocity = new Vector2(0, 0);
}//设置速度为0函数
public virtual void SetVelocity(float _xVelocity, float _yVelocity)
{
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
}
PlayerAnimationTrigger.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerAnimationTriggers : MonoBehaviour
{
private Player player => GetComponentInParent<Player>();//获得夫组件上的实际存在的Player组件
private void AnimationTrigger()
{
player.AnimationTrigger();
}
private void AttackTrigger()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position, player.attackCheckRadius);//创建一个碰撞器组,保存所有圈所碰到的碰撞器
//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Physics2D.OverlapCircleAll.html
foreach(var hit in colliders)//https://blog.csdn.net/m0_52358030/article/details/121722077
{
if(hit.GetComponent<Enemy>()!=null)
{
hit.GetComponent<Enemy>().Damage();
}
}
}
}
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;
}
private void AttackTrigger()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(enemy.attackCheck.position, enemy.attackCheckRadius);//创建一个碰撞器组,保存所有圈所碰到的碰撞器
//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Physics2D.OverlapCircleAll.html
foreach (var hit in colliders)//https://blog.csdn.net/m0_52358030/article/details/121722077
{
if (hit.GetComponent<Player>() != null)
{
hit.GetComponent<Player>().Damage();
}
}
}
}
SkeletonBattleState.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
//从ground进来的
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())
{
stateTimer = enemy.battleTime;
if (enemy.IsPlayerDetected().distance < enemy.attackDistance)//当距离小于攻击距离,变为攻击状态
{
if (CanAttack())
stateMachine.ChangeState(enemy.attackState);
}
}
else//当没有看见player后,才会根据没有看到的时间来使其退出battle状态
{
if(stateTimer < 0||Vector2.Distance(player.transform.position,enemy.transform.position)>7)//根据距离来判断是否结束battle状态
{
stateMachine.ChangeState(enemy.idleState);
}
}
//下面为移动方向设置
if(player.position.x > enemy.transform.position.x)//在右,向右移动
{
moveDir = 1;
}
else if(player.position.x<enemy.transform.position.x)//在左,向左移动
{
moveDir = -1;
}
if(Vector2.Distance(player.transform.position,enemy.transform.position)>1)
enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);
else
{
enemy.SetZeroVelocity();
}//我自己设置了一个敌人接近一定距离就停下来的设置,防止出现敌人乱晃的情况
}
private bool CanAttack()
{
if(Time.time > enemy.lastTimeAttacked + enemy.attackCooldown)
{
enemy.lastTimeAttacked = Time.time;
return true;
}
return false;
}
}