You've already forked taptap2024_GJ_chidouren
331 lines
12 KiB
C#
331 lines
12 KiB
C#
using System;
|
||
using Framework.Timer;
|
||
using Framework.Utils.Extend;
|
||
using Pathfinding;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using XFFSM;
|
||
|
||
namespace Game.Component
|
||
{
|
||
public class EnemyEntity : MonoBehaviour
|
||
{
|
||
[Serializable]
|
||
public class FsmData
|
||
{
|
||
public bool HasAttack;
|
||
public bool HasSafeArea;
|
||
public bool HasRunaway;
|
||
public bool HasActive = true;
|
||
}
|
||
|
||
[SerializeField] protected FSMController _fsmController;
|
||
[SerializeField] protected AILerp _aiLerp;
|
||
public EnemyAnimState AnimState;
|
||
|
||
[Header ("最大活动区域")] public Vector2 ActiveArea = new Vector2 (10 , 5);
|
||
[Header ("随机巡逻区域")] public Vector2 WalkArea = new Vector2 (5 , 5);
|
||
[Header ("攻击检测半径")] public float TriggerDistance = -1f;
|
||
[Header ("追逐检测半径")] public float FollowDistance = -1f;
|
||
[Header ("攻击最小持续时间")] public float AttackStateDuration = 5f; //攻击状态最小持续时间
|
||
[Header ("仇恨丢失冷却时间")] public float AttackCDDuration = 2f;
|
||
[Header ("单位移动速度")] public float MaxMoveSpeed = 3;
|
||
[Header ("常规移动速度系数"), Range (0, 1)] public float WalkSpeedOffset = 1;
|
||
[Header ("攻击移动速度系数"), Range (0, 1)] public float AttackSpeedOffset = 1;
|
||
[Header ("速度变化系数&插值t")] public float SpeedChangeT = 0.5f;
|
||
|
||
private float _expSpeedOffset; //额外速度系数
|
||
private TimeHandler _expTimeHandler; //额外速度计时器
|
||
|
||
private FsmData _fsmData;
|
||
private float _curAttackingTime;
|
||
private float _curAttackCDTime;
|
||
private float _curMoveSpeedOffset;
|
||
private TimeHandler _speedTimeHandler;
|
||
private bool _hasAtkState; //是否为攻击状态
|
||
|
||
//出生点
|
||
[HideInInspector] public Vector2 CreatePos;
|
||
|
||
#if UNITY_EDITOR
|
||
public bool IsDebugDraw = true;
|
||
private void OnDrawGizmos ()
|
||
{
|
||
this._aiLerp.speed = this.CurMoveSpeed;
|
||
|
||
if (!IsDebugDraw)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var enemyTransform = this.transform.position;
|
||
//基于CreatePos为中心点,绘制一个矩形,宽高为ActiveArea, 用于表示此单位的活动区域
|
||
if (!UnityEditor.EditorApplication.isPlaying)
|
||
{
|
||
this.CreatePos = enemyTransform;
|
||
}
|
||
|
||
if (this.ActiveArea.y != 0 && this.ActiveArea.x != 0)
|
||
{
|
||
UnityEditor.Handles.color = new Color (0f, 1f, 0f, 0.35f);
|
||
UnityEditor.Handles.DrawSolidRectangleWithOutline (
|
||
new Rect (CreatePos.x - ActiveArea.x / 2, CreatePos.y - ActiveArea.y / 2, ActiveArea.x, ActiveArea.y),
|
||
new Color (0f, 1f, 0f, 0.25f),
|
||
Color.white);
|
||
// 在圆顶部绘制文本“活动范围”
|
||
UnityEditor.Handles.Label (CreatePos + new Vector2 (0f, ActiveArea.y / 2), "活动范围");
|
||
}
|
||
|
||
if ( this.WalkArea.y != 0 && this.WalkArea.x != 0)
|
||
{
|
||
UnityEditor.Handles.color = new Color (0f, 0f, 1f, 0.35f);
|
||
UnityEditor.Handles.DrawSolidRectangleWithOutline (
|
||
new Rect (CreatePos.x - this.WalkArea.x / 2, CreatePos.y - WalkArea.y / 2, WalkArea.x, WalkArea.y), new Color (0f, 0f, 1f, 0.25f),
|
||
Color.white);
|
||
// 在圆顶部绘制文本“巡逻范围”
|
||
UnityEditor.Handles.Label (CreatePos + new Vector2 (0f, WalkArea.y / 2), "巡逻范围");
|
||
}
|
||
|
||
if (TriggerDistance > 0)
|
||
{
|
||
//绘制一个半径为TriggerDistance的圆形
|
||
UnityEditor.Handles.color = new Color (1f, 0f, 0f, 0.35f);
|
||
UnityEditor.Handles.DrawSolidDisc (enemyTransform, Vector3.back, TriggerDistance);
|
||
//在圆顶部绘制文本“攻击范围”
|
||
UnityEditor.Handles.Label (enemyTransform + new Vector3 (0f, TriggerDistance, 0f), "攻击范围");
|
||
}
|
||
|
||
if (this.FollowDistance > 0 && this._curAttackingTime > 0)
|
||
{
|
||
//绘制一个半径为FollowDistance的圆形
|
||
UnityEditor.Handles.color = new Color (0.62f, 0f, 1f, 0.25f);
|
||
UnityEditor.Handles.DrawSolidDisc (enemyTransform, Vector3.back, FollowDistance);
|
||
//在圆顶部绘制文本“追逐范围”
|
||
UnityEditor.Handles.Label (enemyTransform + new Vector3 (0f, FollowDistance, 0f), "追逐范围");
|
||
}
|
||
|
||
if (UnityEditor.EditorApplication.isPlaying && this._fsmController.Initialized)
|
||
{
|
||
var currentStateInfo = this._fsmController.GetCurrentStateInfo (0);
|
||
//绘制文本
|
||
var text = "行动状态: " + currentStateInfo.data.DisplayName;
|
||
UnityEditor.Handles.Label (enemyTransform, text);
|
||
}
|
||
}
|
||
#endif
|
||
public AILerp AiLerp => this._aiLerp;
|
||
public bool HasAttack => this._curAttackingTime > 0;
|
||
|
||
public bool HasSafeArea
|
||
{
|
||
get
|
||
{
|
||
if (this.ActiveArea.x.Compare (-1) && this.ActiveArea.y.Compare (-1))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
//特殊情况 ,如果值为-1 ,表示不限制
|
||
//x如果为-1
|
||
if (this.ActiveArea.x.Compare (-1))
|
||
{
|
||
if (transform.position.y < this.CreatePos.y - this.ActiveArea.y / 2 ||
|
||
transform.position.y > this.CreatePos.y + this.ActiveArea.y / 2)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
//y如果为-1
|
||
if (this.ActiveArea.y.Compare (-1))
|
||
{
|
||
if (transform.position.x < this.CreatePos.x - this.ActiveArea.x / 2 ||
|
||
transform.position.x > this.CreatePos.x + this.ActiveArea.x / 2)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
//检测当前位置是否在活动范围内, 如果不在活动范围内,直接返回false
|
||
if (transform.position.x < this.CreatePos.x - this.ActiveArea.x / 2 ||
|
||
transform.position.x > this.CreatePos.x + this.ActiveArea.x / 2 ||
|
||
transform.position.y < this.CreatePos.y - this.ActiveArea.y / 2 ||
|
||
transform.position.y > this.CreatePos.y + this.ActiveArea.y / 2)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
#if UNITY_EDITOR
|
||
public bool HasRunaway => MapContent.Instance?.IsRunaway ?? false;
|
||
|
||
public float CurMoveSpeed => (MapContent.Instance?.MoveGlobalOffset ?? 0) * this._curMoveSpeedOffset * (this.MaxMoveSpeed *
|
||
(1 +
|
||
( this._expTimeHandler?.IsPlaying ?? false
|
||
? (1 - this._expTimeHandler.CurProgress) * this._expSpeedOffset
|
||
: 0f)
|
||
)
|
||
);
|
||
#else
|
||
public bool HasRunaway => MapContent.Instance.IsRunaway ?? false;
|
||
|
||
public float CurMoveSpeed => (MapContent.Instance.MoveGlobalOffset) * this._curMoveSpeedOffset * (this.MaxMoveSpeed *
|
||
(1 +
|
||
( this._expTimeHandler?.IsPlaying ?? false
|
||
? (1 - this._expTimeHandler.CurProgress) * this._expSpeedOffset
|
||
: 0f)
|
||
)
|
||
);
|
||
#endif
|
||
|
||
|
||
public void ResetState ()
|
||
{
|
||
this.transform.position = this.CreatePos;
|
||
this.AnimState.SetState (EnemyAnimStateType.Idle);
|
||
EndAttack ();
|
||
this.UpdateSpeedState (false , false);
|
||
_curAttackingTime = 0;
|
||
_curAttackCDTime = 0;
|
||
this._aiLerp.speed = this.CurMoveSpeed;
|
||
}
|
||
|
||
public void UpdateSpeedState (bool hasAtk , bool anim = true)
|
||
{
|
||
this._hasAtkState = hasAtk;
|
||
var targetSpeedOffset = this._hasAtkState ? this.AttackSpeedOffset : this.WalkSpeedOffset;
|
||
if (anim)
|
||
{
|
||
this._curMoveSpeedOffset = Mathf.Lerp (this._curMoveSpeedOffset , targetSpeedOffset , this.SpeedChangeT);
|
||
}
|
||
else
|
||
{
|
||
this._curMoveSpeedOffset = targetSpeedOffset;
|
||
}
|
||
}
|
||
|
||
#region 生命周期
|
||
|
||
private void OnDisable ()
|
||
{
|
||
this._expTimeHandler?.Kill ();
|
||
GameUpdateMgr.Instance.RemoveUpdater (DoUpdate);
|
||
}
|
||
|
||
private void OnEnable ()
|
||
{
|
||
if (!this._fsmController.Initialized)
|
||
{
|
||
Init ();
|
||
}
|
||
|
||
GameUpdateMgr.Instance.AddUpdater (DoUpdate);
|
||
ResetState ();
|
||
}
|
||
|
||
private void DoUpdate ()
|
||
{
|
||
UpdateSpeedState (this._hasAtkState);
|
||
if (_curAttackingTime > 0)
|
||
{
|
||
this._curAttackingTime -= Time.deltaTime;
|
||
|
||
if (this._curAttackingTime <= 0)
|
||
{
|
||
EndAttack ();
|
||
}
|
||
}
|
||
|
||
if (this._curAttackCDTime > 0)
|
||
{
|
||
_curAttackCDTime -= Time.deltaTime;
|
||
}
|
||
|
||
AttackCheck ();
|
||
CheckUpdateFsmData ();
|
||
this._fsmController.DoUpdate ();
|
||
}
|
||
|
||
private void AttackCheck ()
|
||
{
|
||
//处于攻击cd时不作攻击检测
|
||
if (_curAttackCDTime > 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var hasAttack = false;
|
||
if (this._curAttackingTime > 0)
|
||
{
|
||
//追逐中使用追逐半径检测
|
||
hasAttack = this.FollowDistance < 0 ||
|
||
Vector2.Distance ( MapContent.Instance.PlayerPosition , this.transform.position) < this.FollowDistance;
|
||
}
|
||
else
|
||
{
|
||
hasAttack = this.TriggerDistance < 0 ||
|
||
Vector2.Distance ( MapContent.Instance.PlayerPosition , this.transform.position) < this.TriggerDistance;
|
||
}
|
||
|
||
if (hasAttack)
|
||
{
|
||
this._curAttackingTime = this.AttackStateDuration;
|
||
}
|
||
}
|
||
|
||
public void CheckUpdateFsmData (bool isUpdate = false)
|
||
{
|
||
this._aiLerp.speed = this.CurMoveSpeed;
|
||
isUpdate |= _fsmData.HasAttack != this.HasAttack;
|
||
isUpdate |= _fsmData.HasSafeArea != this.HasSafeArea;
|
||
isUpdate |= _fsmData.HasRunaway != this.HasRunaway;
|
||
isUpdate |= _fsmData.HasActive != MapContent.Instance.IsActiveGame;
|
||
|
||
this._fsmData.HasAttack = HasAttack;
|
||
this._fsmData.HasSafeArea = HasSafeArea;
|
||
this._fsmData.HasRunaway = HasRunaway;
|
||
this._fsmData.HasActive = MapContent.Instance.IsActiveGame;
|
||
|
||
if (isUpdate)
|
||
{
|
||
this._fsmController.SetBool ("hasAttack", this._fsmData.HasAttack);
|
||
this._fsmController.SetBool ("hasSafeArea", this._fsmData.HasSafeArea);
|
||
this._fsmController.SetBool ("hasRunaway", this._fsmData.HasRunaway);
|
||
this._fsmController.SetBool ("hasActive", this._fsmData.HasActive);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
public void AddSpeedBuff (float duration , float offset)
|
||
{
|
||
this._expSpeedOffset = offset;
|
||
this._expTimeHandler?.Kill ();
|
||
this._expTimeHandler = GameUpdateMgr.Instance.CreateTimer (duration , null);
|
||
}
|
||
|
||
public void EndAttack ()
|
||
{
|
||
this._hasAtkState = false;
|
||
this._curAttackingTime = 0;
|
||
this._curAttackCDTime = this.AttackCDDuration;
|
||
}
|
||
|
||
private void Init ()
|
||
{
|
||
this._fsmData = new FsmData ()
|
||
{
|
||
HasActive = true
|
||
};
|
||
this.CreatePos = this.transform.position;
|
||
this._fsmController.Init (this);
|
||
}
|
||
}
|
||
} |