BITFALL/Assets/Artists/Scripts/Entities/AI/AIRuntimeStates.cs

260 lines
7.0 KiB
C#
Raw Normal View History

2024-04-16 04:39:26 +08:00
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;
2024-04-19 00:40:34 +08:00
using BITKit.Sensors;
2024-04-16 04:39:26 +08:00
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();
2024-04-19 00:40:34 +08:00
protected readonly HashSet<int> ignoreNoises = new();
2024-04-16 04:39:26 +08:00
protected readonly CacheList<IEntity> detectedEnemies = new();
2024-04-19 00:40:34 +08:00
protected readonly CacheList<IEntity> hearEnemies = new();
2024-04-16 04:39:26 +08:00
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;
2024-04-19 00:40:34 +08:00
if (IsEnemy(entity) is false)
{
foreach (var _collider in entity.As<MonoBehaviour>().GetComponents<Collider>())
{
Service.RangeSensor.Ignores.Add(_collider.GetInstanceID());
; }
continue;
}
2024-04-16 04:39:26 +08:00
detectedEnemies.Add(entity);
}
2024-04-19 00:40:34 +08:00
hearEnemies.Clear();
foreach (var x in Service.AudioSensor.Get())
{
if (x.TryGetComponent<IEntity>(out var entity) is false) continue;
if (IsEnemy(entity) is false)
{
foreach (var _collider in entity.As<MonoBehaviour>().GetComponents<Collider>())
{
Service.AudioSensor.Ignores.Add(_collider.GetInstanceID());
}
continue;
}
hearEnemies.Add(entity);
}
2024-04-16 04:39:26 +08:00
}
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);
}
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>();
2024-04-19 00:40:34 +08:00
return;
}
foreach (var entity in hearEnemies)
{
Service.CombatTarget = entity;
Service.TransitionState<Alert>();
return;
}
foreach (var x in Service.AudioSensor.Noises)
{
if(ignoreNoises.Add(x.Id) is false)continue;
var distance = Vector3.Distance(Service.transform.position, x.Position);
//Debug.Log($"{x.Radius}:{distance}");
if (distance <= x.Radius && NavMesh.SamplePosition(x.Position,out var hit,1,NavMesh.AllAreas))
{
Service.Agent.SetDestination(hit.position);
}
return;
2024-04-16 04:39:26 +08:00
}
}
}
[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)
{
2024-04-19 00:40:34 +08:00
foreach (var entity in hearEnemies)
{
Service.Agent.SetDestination(entity.As<MonoBehaviour>().transform.position);
return;
}
2024-04-16 04:39:26 +08:00
detectedWeight -= deltaTime;
}
else
{
foreach (var entity in detectedEnemies)
{
var distance= Vector3.Distance(Service.transform.position, entity.As<MonoBehaviour>().transform.position);
2024-04-19 00:40:34 +08:00
entity.TryGetComponent<ISensorTarget>(out var sensorTarget);
2024-04-16 04:39:26 +08:00
if(distance<=immediatelyDetectDistance)
{
Service.CombatTarget = entity;
Service.TransitionState<Combat>();
2024-04-19 00:40:34 +08:00
sensorTarget?.Detected(1,Service.RangeSensor,Service.transform);
2024-04-16 04:39:26 +08:00
return;
}
detectedWeight += deltaTime;
2024-04-19 00:40:34 +08:00
sensorTarget?.Detected(detectedWeight,Service.RangeSensor,Service.transform);
2024-04-16 04:39:26 +08:00
if (detectedWeight >= 1)
{
Service.CombatTarget = entity;
Service.TransitionState<Combat>();
return;
}
}
2024-04-19 00:40:34 +08:00
2024-04-16 04:39:26 +08:00
}
RemainingAlertTime -= deltaTime;
if(RemainingAlertTime<=0)
{
Service.TransitionState<Idle>();
return;
}
}
}
2024-04-19 00:40:34 +08:00
2024-04-16 04:39:26 +08:00
[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);
2024-04-19 00:40:34 +08:00
[SerializeField, ReadOnly] private float currentLostTargetTime;
2024-04-16 04:39:26 +08:00
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
2024-04-19 00:40:34 +08:00
Service.RangeSensor.DetectedHeightWeight = 1;
2024-04-16 04:39:26 +08:00
currentLostTargetTime = lostTargetTime;
reTargetInterval.Reset();
2024-04-19 00:40:34 +08:00
Service.CombatTarget.TryGetComponent<AITarget>(out _currentTarget);
Service.Agent.isStopped = false;
2024-04-16 04:39:26 +08:00
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
2024-04-19 00:40:34 +08:00
Service.RangeSensor.DetectedHeightWeight = 0.5f;
2024-04-16 04:39:26 +08:00
Service.RangeSensor.transform.localRotation = Quaternion.identity;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
2024-04-19 00:40:34 +08:00
var position = Service.CombatTarget.As<MonoBehaviour>().transform.position;
var direction = position - Service.transform.position;
direction = Vector3.ProjectOnPlane(direction, Vector3.up);
if (direction.magnitude > 0.01)
2024-04-16 04:39:26 +08:00
{
2024-04-19 00:40:34 +08:00
Service.RangeSensor.transform.rotation = Quaternion.LookRotation(direction);
2024-04-16 04:39:26 +08:00
}
2024-04-19 00:40:34 +08:00
if (NavMesh.SamplePosition(position, out var hit, 1, NavMesh.AllAreas))
2024-04-16 04:39:26 +08:00
{
2024-04-19 00:40:34 +08:00
Service.Agent.SetDestination(hit.position);
2024-04-16 04:39:26 +08:00
}
2024-04-19 00:40:34 +08:00
2024-04-16 04:39:26 +08:00
foreach (var entity in detectedEnemies)
{
currentLostTargetTime = lostTargetTime;
2024-04-19 00:40:34 +08:00
if (reTargetInterval.AllowUpdate)
{
Service.CombatTarget = entity;
Service.CombatTarget.TryGetComponent<AITarget>(out _currentTarget);
}
2024-04-16 04:39:26 +08:00
return;
}
2024-04-19 00:40:34 +08:00
foreach (var entity in hearEnemies)
{
currentLostTargetTime = lostTargetTime;
}
currentLostTargetTime -= deltaTime;
if (currentLostTargetTime <= 0)
2024-04-16 04:39:26 +08:00
{
Service.TransitionState<Alert>();
}
}
}
}