思路:
从敌人的位置发射一道射线或者一片区域来对玩家实体进行检测,如果检测倒玩家,则进行追击进攻
利用BoxCast()
即可实现
BoxCast()官方文档:https://docs.unity3d.com/cn/2022.3/ScriptReference/Physics2D.BoxCast.html
创建检测区域
由于BoxCast
需要众多参数,所以在Enemy中创建相关的全局变量
[Header("检测")]
// 因为中心点在脚底,所以加一个偏移量
public Vector2 centerOffset;
// 检测大小
public Vector2 checkSize;
// 检测距离
public float checkDistance;
// 图层
public LayerMask attackLayer;
创建FoundPlayer()
方法,在内部调用BaxCast()
方法
public bool FoundPlayer()
{
return Physics2D.BoxCast(transform.position + (Vector3)centerOffset, checkSize, 0, faceDirect, checkDistance, attackLayer);
}
创建OnDrawGizmosSelected()方法,用于在Unity图形界面中能够绘制出检测距离(checkDistance)的位置
private void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(transform.position + (Vector3)centerOffset + new Vector3(checkDistance * -transform.localScale.x, 0), 0.2f);
}
切换敌人状态
由于需要给Enemy赋予一个状态,同时也需要知道该状态的变量,再赋值给currentState。因此,在Enemy类中,编写一个切换的方法。不过,在此之前,需要创建一个枚举类(Enum),来表明当前的状态。
在Scripts下创建Utils文件夹,在文件夹内创建StateEnum
C#文件
在内部创建三种状态
public enum NPCState
{
Patrol, Chase, Skill
}
在Enemy中创建SwitchState()
方法,形参写为刚才创建的NPCState
public void SwitchState(NPCState state)
{
}
此方法内部实现,通过传入的NPCState
,转换为已经创建的三种状态变量
var newState = state switch
{
NPCState.Patrol => patrolState,
NPCState.Chase => chaseState,
_ => null
};
currentState.OnExit();
currentState = newState;
currentState.OnEnter(this);
_ => null
相当于switch语句中的default
。在切换状态时,需要调用OnExit()方法,关闭当前状态,再赋值新状态并调用OnEnter()
在BoarPatrolState类中,LogicUpdate()继续编写切换追击状态的代码。加一个判断,判断如果发现了玩家,则切换状态
if (currentEnemy.FoundPlayer())
{
currentEnemy.SwitchState(NPCState.Chase);
}
编写追击代码
在Enemy文件夹下,创建BoarChaseState C#脚本,用于编写追击逻辑
继承BaseState,并在OnEnter()中将currentEnemy进行赋值
public class BoarChaseState : BaseState
{
public override void OnEnter(Enemy enemy)
{
currentEnemy = enemy;
Debug.Log("chase");
}
public override void LogicUpdate()
{
}
public override void PhysicsUpdate()
{
}
public override void OnExit()
{
}
}
与此同时,在Boar类中,添加一行代码用于初始化BoarChaseState对象
protected override void Awake()
{
base.Awake();
patrolState = new BoarPatrolState();
chaseState = new BoarChaseState();
}
回到BoarChaseState类,在OnEnter()中,修改当前的速度为追击速度,并开启追击动画
public override void OnEnter(Enemy enemy)
{
currentEnemy = enemy;
currentEnemy.currentSpeed = currentEnemy.chaseSpeed;
currentEnemy.animator.SetBool("run", true);
}
在LogicUpdate()方法中,使野猪追击撞墙时不进行等待立即转身
public override void LogicUpdate()
{
// 追击时,撞墙不等待并进行转身
if (!currentEnemy.physicsCheck.isGround || (currentEnemy.physicsCheck.touchLeftWall && currentEnemy.faceDirect.x < 0) || (currentEnemy.physicsCheck.touchRightWall && currentEnemy.faceDirect.x > 0))
{
currentEnemy.transform.localScale = new Vector3(currentEnemy.faceDirect.x, 1, 1);
}
}
丢失目标后切换状态
丢失目标后,进行倒计时,在一定时间内没有发现玩家则切换回巡逻状态
在Enemy中,创建两个全局变量
// 丢失目标的时间
public float lostTime;
// 丢失目标的计时器
public float lostTimeCounter;
在TimeCounter()方法内,进行一个是否发现玩家的判断,如果没有发现则扣除倒计时
if(!FoundPlayer() && lostTimeCounter > 0)
{
lostTimeCounter -= Time.deltaTime;
} else if (FoundPlayer())
{
lostTimeCounter = lostTime;
}
接下来在BoarChaseState类LogicUpdate()中,加一个判断。如果丢失目标的时间计时结束,则切换回巡逻状态
public override void LogicUpdate()
{
// 丢失目标计时结束切换回巡逻状态
if(currentEnemy.lostTimeCounter <= 0)
{
currentEnemy.SwitchState(NPCState.Patrol);
}
// 追击时,撞墙不等待并进行转身
if (!currentEnemy.physicsCheck.isGround || (currentEnemy.physicsCheck.touchLeftWall && currentEnemy.faceDirect.x < 0) || (currentEnemy.physicsCheck.touchRightWall && currentEnemy.faceDirect.x > 0))
{
currentEnemy.transform.localScale = new Vector3(currentEnemy.faceDirect.x, 1, 1);
}
}
解决正面追击玩家时,玩家攻击不造成击退位移的问题
由于野猪追击玩家时的冲力抵消了玩家的击退位移的力,所以才会导致当前的问题
在Enemy类OnTakeDamage()方法中,添加一行代码
// 受伤时,无论x轴方向当前敌人的力有多少,都会造成击退
rb.velocity = new Vector2(0, rb.velocity.y);
标签:Enemy,void,追击,2D,Unity,currentEnemy,override,public
From: https://www.cnblogs.com/xinlindeyu/p/17932810.html