1
This commit is contained in:
@@ -11,7 +11,8 @@
|
||||
"GUID:1235ca61e7f433b408ed5a68767e7123",
|
||||
"GUID:508392158bd966c4d9c21e19661a441d",
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||
"GUID:2dbcdde5f5df6a343a36c62f12bd6fae"
|
||||
"GUID:2dbcdde5f5df6a343a36c62f12bd6fae",
|
||||
"GUID:705b66b7892e7524f912fd152f5d5251"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
194
Assets/Artists/Scripts/Entities/AI/AIRuntimeStates.cs
Normal file
194
Assets/Artists/Scripts/Entities/AI/AIRuntimeStates.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Odbc;
|
||||
using System.Linq;
|
||||
using BITKit;
|
||||
using BITKit.AI.States;
|
||||
using BITKit.Entities;
|
||||
using BITKit.StateMachine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace BITFALL.AI.States
|
||||
{
|
||||
public abstract class AIRuntimeState : AIState
|
||||
{
|
||||
[Inject,HideInInspector] public AIService Service;
|
||||
private readonly ConcurrentDictionary<ulong, bool> _friends = new();
|
||||
private readonly ConcurrentDictionary<ulong, bool> _enemies = new();
|
||||
protected readonly CacheList<IEntity> detectedEnemies = new();
|
||||
public bool IsFriend(IEntity target) => _friends.GetOrAdd(target.Id, IsFriendInternal);
|
||||
public bool IsEnemy(IEntity target) => _enemies.GetOrAdd(target.Id, IsEnemyInternal);
|
||||
public override void OnStateUpdate(float deltaTime)
|
||||
{
|
||||
base.OnStateUpdate(deltaTime);
|
||||
detectedEnemies.Clear();
|
||||
foreach (var x in Service.RangeSensor.Get())
|
||||
{
|
||||
if (x.TryGetComponent<IEntity>(out var entity) is false) continue;
|
||||
if (IsEnemy(entity) is false)continue;
|
||||
detectedEnemies.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsEnemyInternal(ulong id)
|
||||
{
|
||||
var target = UnityEntitiesService.Get(id);
|
||||
var tags = target.As<MonoBehaviour>().GetComponent<ITag>().GetTags();
|
||||
foreach (var x in Service.EnemyTag.GetTags())
|
||||
{
|
||||
if (tags.Contains(x)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsFriendInternal(ulong id)
|
||||
{
|
||||
var target = UnityEntitiesService.Get(id);
|
||||
var tags = target.As<MonoBehaviour>().GetComponent<ITag>().GetTags();
|
||||
foreach (var x in Service.FriendTag.GetTags())
|
||||
{
|
||||
if (tags.Contains(x)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[CustomType(typeof(AI_Idle))]
|
||||
public sealed class Idle : AIRuntimeState, AI_Idle
|
||||
{
|
||||
public override void OnStateEntry(IState old)
|
||||
{
|
||||
base.OnStateEntry(old);
|
||||
Service.Agent.isStopped = true;
|
||||
}
|
||||
|
||||
public override void OnStateUpdate(float deltaTime)
|
||||
{
|
||||
base.OnStateUpdate(deltaTime);
|
||||
//foreach (var x in Service.AudioSensor.Get().Union(Service.RangeSensor.Get()))
|
||||
foreach (var entity in detectedEnemies)
|
||||
{
|
||||
Service.CombatTarget = entity;
|
||||
Service.TransitionState<Alert>();
|
||||
}
|
||||
}
|
||||
}
|
||||
[Serializable]
|
||||
[CustomType(typeof(AI_Idle))]
|
||||
public sealed class Alert : AIRuntimeState, AI_Alert
|
||||
{
|
||||
public int AlertLevel { get; set; }
|
||||
public float RemainingAlertTime { get; set; }
|
||||
[SerializeField] private int initialAlertTime;
|
||||
[SerializeField] private int immediatelyDetectDistance = 5;
|
||||
[SerializeField,ReadOnly]private float detectedWeight;
|
||||
public override void OnStateEntry(IState old)
|
||||
{
|
||||
base.OnStateEntry(old);
|
||||
RemainingAlertTime = initialAlertTime;
|
||||
detectedWeight = old switch
|
||||
{
|
||||
AI_Combat=>0.5f,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
public override void OnStateUpdate(float deltaTime)
|
||||
{
|
||||
base.OnStateUpdate(deltaTime);
|
||||
|
||||
if (detectedEnemies.Count is 0)
|
||||
{
|
||||
detectedWeight -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var entity in detectedEnemies)
|
||||
{
|
||||
var distance= Vector3.Distance(Service.transform.position, entity.As<MonoBehaviour>().transform.position);
|
||||
if(distance<=immediatelyDetectDistance)
|
||||
{
|
||||
Service.CombatTarget = entity;
|
||||
Service.TransitionState<Combat>();
|
||||
return;
|
||||
}
|
||||
detectedWeight += deltaTime;
|
||||
if (detectedWeight >= 1)
|
||||
{
|
||||
Service.CombatTarget = entity;
|
||||
Service.TransitionState<Combat>();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemainingAlertTime -= deltaTime;
|
||||
|
||||
if(RemainingAlertTime<=0)
|
||||
{
|
||||
Service.TransitionState<Idle>();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[Serializable]
|
||||
[CustomType(typeof(AI_Idle))]
|
||||
public sealed class Combat : AIRuntimeState, AI_Combat
|
||||
{
|
||||
public AITarget CurrentTarget => _currentTarget;
|
||||
private AITarget _currentTarget;
|
||||
[SerializeField] private float lostTargetTime;
|
||||
private readonly IntervalUpdate reTargetInterval = new(2);
|
||||
[SerializeField,ReadOnly]private float currentLostTargetTime;
|
||||
public override void OnStateEntry(IState old)
|
||||
{
|
||||
base.OnStateEntry(old);
|
||||
currentLostTargetTime = lostTargetTime;
|
||||
reTargetInterval.Reset();
|
||||
Service.CombatTarget.TryGetComponent<AITarget>(out _currentTarget);
|
||||
Service.Agent.isStopped = false;
|
||||
}
|
||||
|
||||
public override void OnStateExit(IState old, IState newState)
|
||||
{
|
||||
base.OnStateExit(old, newState);
|
||||
Service.RangeSensor.transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
public override void OnStateUpdate(float deltaTime)
|
||||
{
|
||||
base.OnStateUpdate(deltaTime);
|
||||
Service.RangeSensor.transform.rotation = Service.transform.rotation;
|
||||
if (reTargetInterval.AllowUpdate)
|
||||
{
|
||||
Service.CombatTarget = null;
|
||||
}
|
||||
if (Service.CombatTarget is not null)
|
||||
{
|
||||
var position = Service.CombatTarget.As<MonoBehaviour>().transform.position;
|
||||
if (NavMesh.SamplePosition(position, out var hit, 1, NavMesh.AllAreas))
|
||||
{
|
||||
Service.Agent.SetDestination(hit.position);
|
||||
}
|
||||
}
|
||||
foreach (var entity in detectedEnemies)
|
||||
{
|
||||
currentLostTargetTime = lostTargetTime;
|
||||
Service.CombatTarget = entity;
|
||||
Service.CombatTarget.TryGetComponent<AITarget>(out _currentTarget);
|
||||
return;
|
||||
}
|
||||
currentLostTargetTime-=deltaTime;
|
||||
if(currentLostTargetTime<=0)
|
||||
{
|
||||
Service.TransitionState<Alert>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
52
Assets/Artists/Scripts/Entities/AI/AIService.cs
Normal file
52
Assets/Artists/Scripts/Entities/AI/AIService.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BITFALL.AI.States;
|
||||
using BITKit;
|
||||
using BITKit.AI.States;
|
||||
using BITKit.Entities;
|
||||
using BITKit.Sensors;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UnityEngine.PlayerLoop;
|
||||
|
||||
namespace BITFALL.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// AI服务
|
||||
/// </summary>
|
||||
[CustomType(typeof(AIService))]
|
||||
public class AIService : StateBasedBehavior<AI_Base>
|
||||
{
|
||||
[SerializeReference,SubclassSelector] private ITicker ticker;
|
||||
[SerializeField] private NavMeshAgent agent;
|
||||
[SerializeField] private RangeSensor rangeSensor;
|
||||
[SerializeField] private AudioSensor audioSensor;
|
||||
[SerializeField] private Tag selfTag;
|
||||
[SerializeField] private Tag friendTag;
|
||||
[SerializeField] private Tag enemyTag;
|
||||
|
||||
public IEntity CombatTarget { get; set; }
|
||||
|
||||
public RangeSensor RangeSensor => rangeSensor;
|
||||
public AudioSensor AudioSensor => audioSensor;
|
||||
public NavMeshAgent Agent => agent;
|
||||
public ITag SelfTag => selfTag;
|
||||
public ITag FriendTag => friendTag;
|
||||
public ITag EnemyTag => enemyTag;
|
||||
public override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
ticker.Add(OnTick);
|
||||
TransitionState<Idle>();
|
||||
}
|
||||
private void OnTick(float obj)
|
||||
{
|
||||
UpdateState(obj);
|
||||
}
|
||||
public override void OnDestroyComponent()
|
||||
{
|
||||
base.OnDestroyComponent();
|
||||
ticker.Remove(OnTick);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "BITFALL.Entities.AI.Runtime",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:709caf8d7fb6ef24bbba0ab9962a3ad0",
|
||||
"GUID:7efac18f239530141802fb139776f333",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:705b66b7892e7524f912fd152f5d5251",
|
||||
"GUID:8c4dd21966739024fbd72155091d199e",
|
||||
"GUID:508392158bd966c4d9c21e19661a441d"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
@@ -6,9 +6,8 @@ using UnityEngine;
|
||||
using BITKit;
|
||||
using BITKit.Animations;
|
||||
using BITKit.Entities;
|
||||
using BITKit.Physics;
|
||||
|
||||
namespace BITFALL.Entites
|
||||
namespace BITFALL.Entities
|
||||
{
|
||||
public class EntityProxyCharacter : EntityBehavior
|
||||
{
|
||||
|
Reference in New Issue
Block a user