You've already forked taptap2024_GJ_chidouren
完成4种基础ai
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using Framework.Timer;
|
||||
using Framework.Utils.Extend;
|
||||
using Pathfinding;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using XFFSM;
|
||||
|
||||
namespace Game.Component
|
||||
@@ -20,18 +22,23 @@ namespace Game.Component
|
||||
[SerializeField] protected FSMController _fsmController;
|
||||
[SerializeField] protected AILerp _aiLerp;
|
||||
|
||||
[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 MoveSpeed = 3;
|
||||
|
||||
private FsmData _fsmData;
|
||||
private float _curAttackingTime;
|
||||
private float _curAttackCDTime;
|
||||
[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 FsmData _fsmData;
|
||||
private float _curAttackingTime;
|
||||
private float _curAttackCDTime;
|
||||
private float _curMoveSpeedOffset;
|
||||
private TimeHandler _speedTimeHandler;
|
||||
private bool _hasAtkState; //是否为攻击状态
|
||||
|
||||
//出生点
|
||||
[HideInInspector] public Vector2 CreatePos;
|
||||
@@ -40,7 +47,7 @@ namespace Game.Component
|
||||
public bool IsDebugDraw = true;
|
||||
private void OnDrawGizmos ()
|
||||
{
|
||||
this._aiLerp.speed = this.MoveSpeed;
|
||||
this._aiLerp.speed = this.CurMoveSpeed;
|
||||
|
||||
if (!IsDebugDraw)
|
||||
{
|
||||
@@ -56,21 +63,21 @@ namespace Game.Component
|
||||
|
||||
if (this.ActiveArea.y != 0 && this.ActiveArea.x != 0)
|
||||
{
|
||||
UnityEditor.Handles.color = new Color (0f, 1f, 0f, 0.5f);
|
||||
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.black);
|
||||
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.5f);
|
||||
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.black);
|
||||
Color.white);
|
||||
// 在圆顶部绘制文本“巡逻范围”
|
||||
UnityEditor.Handles.Label (CreatePos + new Vector2 (0f, WalkArea.y / 2), "巡逻范围");
|
||||
}
|
||||
@@ -78,7 +85,7 @@ namespace Game.Component
|
||||
if (TriggerDistance > 0)
|
||||
{
|
||||
//绘制一个半径为TriggerDistance的圆形
|
||||
UnityEditor.Handles.color = new Color (1f, 0f, 0f, 0.5f);
|
||||
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), "攻击范围");
|
||||
@@ -109,6 +116,35 @@ namespace Game.Component
|
||||
{
|
||||
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 ||
|
||||
@@ -124,13 +160,32 @@ namespace Game.Component
|
||||
|
||||
public bool HasRunaway { get; private set; } = false;
|
||||
|
||||
public float CurMoveSpeed => this._curMoveSpeedOffset * this.MaxMoveSpeed;
|
||||
|
||||
private void ResetState ()
|
||||
{
|
||||
this.UpdateSpeedState (false , false);
|
||||
_curAttackingTime = 0;
|
||||
_curAttackCDTime = 0;
|
||||
this._aiLerp.speed = this.MoveSpeed;
|
||||
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 ()
|
||||
{
|
||||
GameUpdateMgr.Instance.RemoveUpdater (DoUpdate);
|
||||
@@ -149,6 +204,7 @@ namespace Game.Component
|
||||
|
||||
private void DoUpdate ()
|
||||
{
|
||||
UpdateSpeedState (this._hasAtkState);
|
||||
if (_curAttackingTime > 0)
|
||||
{
|
||||
this._curAttackingTime -= Time.deltaTime;
|
||||
@@ -180,13 +236,13 @@ namespace Game.Component
|
||||
if (this._curAttackingTime > 0)
|
||||
{
|
||||
//追逐中使用追逐半径检测
|
||||
hasAttack = this.FollowDistance < 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;
|
||||
Vector2.Distance ( MapContent.Instance.PlayerPosition , this.transform.position) < this.TriggerDistance;
|
||||
}
|
||||
|
||||
if (hasAttack)
|
||||
@@ -195,12 +251,6 @@ namespace Game.Component
|
||||
}
|
||||
}
|
||||
|
||||
public void EndAttack ()
|
||||
{
|
||||
this._curAttackingTime = 0;
|
||||
this._curAttackCDTime = this.AttackCDDuration;
|
||||
}
|
||||
|
||||
public void CheckUpdateFsmData (bool isUpdate = false)
|
||||
{
|
||||
isUpdate |= _fsmData.HasAttack != this.HasAttack;
|
||||
@@ -219,6 +269,16 @@ namespace Game.Component
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public void EndAttack ()
|
||||
{
|
||||
this._hasAtkState = false;
|
||||
this._curAttackingTime = 0;
|
||||
this._curAttackCDTime = this.AttackCDDuration;
|
||||
}
|
||||
|
||||
private void Init ()
|
||||
{
|
||||
this._fsmData = new FsmData ()
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
public override void OnEnter ()
|
||||
{
|
||||
this.Entity.UpdateSpeedState (true);
|
||||
}
|
||||
|
||||
public override void OnUpdate ()
|
||||
|
||||
25
Assets/Scripts/Game/Component/EnemyFSM_AI/FollowDuration.cs
Normal file
25
Assets/Scripts/Game/Component/EnemyFSM_AI/FollowDuration.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Game.Component.EnemyFSM_AI
|
||||
{
|
||||
public class FollowDuration : EnemyFSMState
|
||||
{
|
||||
private AttackTimer _attackTimer;
|
||||
|
||||
public override void OnEnter ()
|
||||
{
|
||||
this.Entity.UpdateSpeedState (true);
|
||||
this._attackTimer = this.Entity.GetComponent<AttackTimer> ();
|
||||
this._attackTimer.StartAttack (this.Entity.EndAttack);
|
||||
}
|
||||
|
||||
public override void OnUpdate ()
|
||||
{
|
||||
if (this._attackTimer.IsRunning)
|
||||
{
|
||||
Debug.Log ("开始追逐!");
|
||||
SetAiTarget (MapContent.Instance.PlayerPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ca3a44f24814bd190225eccca7d63f0
|
||||
timeCreated: 1729081419
|
||||
@@ -6,7 +6,7 @@ namespace Game.Component.EnemyFSM_AI
|
||||
{
|
||||
public override void OnEnter ()
|
||||
{
|
||||
|
||||
this.Entity.UpdateSpeedState (true);
|
||||
}
|
||||
|
||||
public override void OnUpdate ()
|
||||
|
||||
25
Assets/Scripts/Game/Component/EnemyFSM_AI/PathWalk.cs
Normal file
25
Assets/Scripts/Game/Component/EnemyFSM_AI/PathWalk.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Game.Component.FsmMonoData;
|
||||
|
||||
namespace Game.Component.EnemyFSM_AI
|
||||
{
|
||||
public class PathWalk : EnemyFSMState
|
||||
{
|
||||
private WalkPathGroup _walkPathGroup;
|
||||
|
||||
public override void OnEnter ()
|
||||
{
|
||||
this.Entity.UpdateSpeedState (false);
|
||||
this._walkPathGroup = this.Entity.GetComponent<WalkPathGroup> ();
|
||||
this._walkPathGroup.InitTarget (this.Entity.transform.position);
|
||||
this.SetAiTarget (this._walkPathGroup.CurTarget);
|
||||
}
|
||||
|
||||
public override void OnUpdate ()
|
||||
{
|
||||
if (this._walkPathGroup.CheckNextNode (this.Entity.transform.position))
|
||||
{
|
||||
this.SetAiTarget (this._walkPathGroup.CurTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f98a710f96914e2e8585939e12bff6d8
|
||||
timeCreated: 1729096274
|
||||
@@ -4,6 +4,10 @@ namespace Game.Component.EnemyFSM_AI
|
||||
{
|
||||
public class Sleep : EnemyFSMState
|
||||
{
|
||||
|
||||
public override void OnEnter ()
|
||||
{
|
||||
SetAiTarget (this.Entity.CreatePos);
|
||||
//此处可设置角色动画为沉睡状态
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Game/Component/FsmMonoData.meta
Normal file
3
Assets/Scripts/Game/Component/FsmMonoData.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15cf63ee2da54dfca225484973ba5219
|
||||
timeCreated: 1729082256
|
||||
33
Assets/Scripts/Game/Component/FsmMonoData/AttackTimer.cs
Normal file
33
Assets/Scripts/Game/Component/FsmMonoData/AttackTimer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Framework.Timer;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
public class AttackTimer : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Header ("追逐时长")] private float AttackRealTime;
|
||||
private TimeHandler _timeHandler;
|
||||
|
||||
[ProgressBar(0 , "Curprogress")]
|
||||
public float Curprogress => this._timeHandler?.CurProgress ?? 0;
|
||||
|
||||
public bool IsRunning => this._timeHandler?.IsPlaying ?? false;
|
||||
|
||||
|
||||
public void StartAttack (Action callback)
|
||||
{
|
||||
this._timeHandler?.Kill ();
|
||||
this._timeHandler = GameUpdateMgr.Instance.CreateTimer (this.AttackRealTime , callback);
|
||||
}
|
||||
|
||||
public void StopAttack ()
|
||||
{
|
||||
this._timeHandler?.Kill ();
|
||||
this._timeHandler = null;
|
||||
}
|
||||
|
||||
private void OnDisable ()
|
||||
{
|
||||
StopAttack ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaef8af63e5b481a835fd19a9ea60e15
|
||||
timeCreated: 1729082604
|
||||
49
Assets/Scripts/Game/Component/FsmMonoData/WalkPathGroup.cs
Normal file
49
Assets/Scripts/Game/Component/FsmMonoData/WalkPathGroup.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Game.Component.Map;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Game.Component.FsmMonoData
|
||||
{
|
||||
public class WalkPathGroup : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private MovePathGroup PathGroup;
|
||||
|
||||
private int _curIndex;
|
||||
|
||||
public Vector2 CurTarget { get; private set; }
|
||||
|
||||
public void InitTarget (Vector2 actorPos)
|
||||
{
|
||||
//根据角色当前位置,寻找最近的节点
|
||||
_curIndex = 0;
|
||||
CurTarget = this.PathGroup.PathList[0].position;
|
||||
for (int i = 1; i < this.PathGroup.PathList.Count; i++)
|
||||
{
|
||||
if (Vector2.Distance (actorPos , this.PathGroup.PathList[i].position) < Vector2.Distance (actorPos , CurTarget))
|
||||
{
|
||||
_curIndex = i;
|
||||
CurTarget = this.PathGroup.PathList[i].position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckNextNode (Vector2 actorPos)
|
||||
{
|
||||
if (Vector2.Distance (actorPos , CurTarget) < 0.1f)
|
||||
{
|
||||
NextNode ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void NextNode ()
|
||||
{
|
||||
_curIndex++;
|
||||
if (this._curIndex >= this.PathGroup.PathList.Count)
|
||||
{
|
||||
_curIndex = 0;
|
||||
}
|
||||
CurTarget = this.PathGroup.PathList[_curIndex].position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 954117e49b324df0a8e9fbe6556f9578
|
||||
timeCreated: 1729095580
|
||||
3
Assets/Scripts/Game/Component/Map.meta
Normal file
3
Assets/Scripts/Game/Component/Map.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7537b88cf96d486b9ef0ccb9d26d9a49
|
||||
timeCreated: 1729094150
|
||||
90
Assets/Scripts/Game/Component/Map/MovePathGroup.cs
Normal file
90
Assets/Scripts/Game/Component/Map/MovePathGroup.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Game.Component.Map
|
||||
{
|
||||
public class MovePathGroup : MonoBehaviour
|
||||
{
|
||||
private List<Vector3> _pathList;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
public List<Transform> PathList;
|
||||
|
||||
private void OnDrawGizmos ()
|
||||
{
|
||||
if (PathList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//根据PathList绘制节点,并按照顺序连接线段
|
||||
for (int i = PathList.Count - 1; i >= 0 ; i--)
|
||||
{
|
||||
if (PathList[i] == null)
|
||||
{
|
||||
PathList.RemoveAt (i);
|
||||
continue;
|
||||
}
|
||||
//每个节点绘制一个园
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere (PathList[i].position, 0.1f);
|
||||
if (this.PathList.Count > 1)
|
||||
{
|
||||
if (i < PathList.Count - 1)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine (PathList[i].position, PathList[i + 1].position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine (PathList[i].position, PathList[0].position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateNodeVector ();
|
||||
}
|
||||
|
||||
[Button ("刷新路径")]
|
||||
private void RefreshPath ()
|
||||
{
|
||||
this.PathList.Clear ();
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
this.PathList.Add (transform.GetChild (i));
|
||||
}
|
||||
UpdateNodeVector ();
|
||||
}
|
||||
|
||||
private void UpdateNodeVector ()
|
||||
{
|
||||
this._pathList = new List<Vector3>();
|
||||
foreach (Transform t in PathList)
|
||||
{
|
||||
_pathList.Add (t.position);
|
||||
}
|
||||
}
|
||||
|
||||
[Button ("添加路径节点")]
|
||||
private void AddPathNode ()
|
||||
{
|
||||
//创建一个空的游戏对象(编辑器中)
|
||||
GameObject go = new GameObject ("PathNode");
|
||||
UnityEditor.Selection.activeGameObject = go;
|
||||
if (PathList != null && PathList.Count > 0)
|
||||
{
|
||||
go.transform.position = PathList[PathList.Count - 1].position + Vector3.up;
|
||||
}
|
||||
else
|
||||
{
|
||||
go.transform.position = transform.position;
|
||||
}
|
||||
go.transform.SetParent (this.transform);
|
||||
PathList.Add (go.transform);
|
||||
UpdateNodeVector ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Game/Component/Map/MovePathGroup.cs.meta
Normal file
3
Assets/Scripts/Game/Component/Map/MovePathGroup.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 912374266a4144cfb5a61aea8cde632a
|
||||
timeCreated: 1729094165
|
||||
@@ -17,8 +17,8 @@ namespace Game.Component
|
||||
Quaternion targetRotation = Quaternion.FromToRotation (Vector3.up , forward.normalized);
|
||||
transform.rotation = Quaternion.Lerp (transform.rotation , targetRotation , 0.25f);
|
||||
// Debug.DrawLine (transform.position , transform.position + forward , Color.magenta);
|
||||
// this.transform.Translate ( Vector3.up * speedOffset * speed * Time.deltaTime);
|
||||
this.rigidbody2D.MovePosition (t.position + ((Vector3)vector * speed * Time.deltaTime));
|
||||
this.transform.Translate ( Vector3.up * speedOffset * speed * Time.deltaTime);
|
||||
// this.rigidbody2D.MovePosition (t.position + ((Vector3)vector * speed * Time.deltaTime));
|
||||
// Vector3 v = target.position - transform.position;
|
||||
// v.z = 0; // 确保向量在2D平面上
|
||||
// float angle = Vector3.SignedAngle(Vector3.up, v, Vector3.forward);
|
||||
|
||||
Reference in New Issue
Block a user