This commit is contained in:
CortexCore
2024-04-16 04:39:26 +08:00
parent 09037a8958
commit aacf09b2b2
28 changed files with 2571 additions and 550 deletions

View File

@@ -11,7 +11,8 @@
"GUID:1235ca61e7f433b408ed5a68767e7123",
"GUID:508392158bd966c4d9c21e19661a441d",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:2dbcdde5f5df6a343a36c62f12bd6fae"
"GUID:2dbcdde5f5df6a343a36c62f12bd6fae",
"GUID:705b66b7892e7524f912fd152f5d5251"
],
"includePlatforms": [],
"excludePlatforms": [],

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

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

View File

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

View File

@@ -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
{