195 lines
5.2 KiB
C#
195 lines
5.2 KiB
C#
|
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>();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|