完成4种基础ai

This commit is contained in:
2024-10-17 00:46:27 +08:00
parent 4920837ed7
commit cdd391ca36
74 changed files with 1094 additions and 4449 deletions

View File

@@ -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 ()

View File

@@ -4,6 +4,7 @@
{
public override void OnEnter ()
{
this.Entity.UpdateSpeedState (true);
}
public override void OnUpdate ()

View 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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4ca3a44f24814bd190225eccca7d63f0
timeCreated: 1729081419

View File

@@ -6,7 +6,7 @@ namespace Game.Component.EnemyFSM_AI
{
public override void OnEnter ()
{
this.Entity.UpdateSpeedState (true);
}
public override void OnUpdate ()

View 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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f98a710f96914e2e8585939e12bff6d8
timeCreated: 1729096274

View File

@@ -4,6 +4,10 @@ namespace Game.Component.EnemyFSM_AI
{
public class Sleep : EnemyFSMState
{
public override void OnEnter ()
{
SetAiTarget (this.Entity.CreatePos);
//此处可设置角色动画为沉睡状态
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 15cf63ee2da54dfca225484973ba5219
timeCreated: 1729082256

View 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 ();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eaef8af63e5b481a835fd19a9ea60e15
timeCreated: 1729082604

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 954117e49b324df0a8e9fbe6556f9578
timeCreated: 1729095580

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7537b88cf96d486b9ef0ccb9d26d9a49
timeCreated: 1729094150

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 912374266a4144cfb5a61aea8cde632a
timeCreated: 1729094165

View File

@@ -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);