Files
Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/AI/AIZombieService.cs
2025-06-24 23:49:13 +08:00

115 lines
3.8 KiB
C#

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Animancer;
using BITFALL;
using BITFALL.Bullet;
using BITKit;
using BITKit.Entities;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Net.BITKit.Quadtree;
using Net.Project.B.Faction;
using Net.Project.B.Health;
using Net.Project.B.Melee;
using Project.B.CharacterController;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;
namespace Net.Project.B.AI
{
public class AIZombieService:IDisposable
{
private readonly IHealthService _healthService;
private readonly IMeleeService _meleeService;
private readonly ILogger<AIZombieService> _logger;
private readonly IEntitiesService _entitiesService;
private readonly ITicker _ticker;
private readonly ConcurrentDictionary<int,IntervalUpdate> _attackIntervals = new();
private readonly ScriptableAIAnimations _animations;
private readonly MeleeData _meleeData = new()
{
Damage = 10,
IgnoreTags = { "zombie" },
MaxTargetCount = 1,
Radius = 1.2f,
};
public AIZombieService(ITicker ticker, IEntitiesService entitiesService, ILogger<AIZombieService> logger, IMeleeService meleeService, IHealthService healthService)
{
_ticker = ticker;
_entitiesService = entitiesService;
_logger = logger;
_meleeService = meleeService;
_healthService = healthService;
_ticker.Add(OnTick);
_animations = _entitiesService.QueryComponents<ScriptableAIAnimations>() is {Length:>0} s ? s[0] :null;
}
private void OnTick(float obj)
{
foreach (var (blackBoard,characterController, entity, zombie, navMeshAgent, animancerComponent) in _entitiesService
.QueryComponents<AIBlackBoard,ICharacterController, IEntity, AIZombie, NavMeshAgent, AnimancerComponent>())
{
if (!navMeshAgent.isOnNavMesh) continue;
if (characterController.AllowMovement.Allow is false) continue;
if(blackBoard is not {Target:not null , AlertValue: 1})continue;
blackBoard.Target.ServiceProvider.QueryComponents(out ICharacterController humanCc);
var playerPos = humanCc.ViewPosition;
var zombiePos = characterController.Position;
navMeshAgent.SetDestination(playerPos);
navMeshAgent.radius = Mathf.Clamp(Vector3.Distance(playerPos, zombiePos) * 0.1f, 0.64f, 1.6f);
if(_attackIntervals.GetOrCreate(entity.Id).AllowUpdate)continue;
var distance = math.distance(playerPos.xz, zombiePos.xz);
if (distance < navMeshAgent.stoppingDistance*2)
{
Attack();
_meleeData.Initiator = entity.Id;
_meleeData.StartPosition = characterController.ViewPosition;
_meleeService.Add(_meleeData);
}
continue;
async void Attack()
{
using var _ = characterController.AllowMovement.GetDisableHandle();
var state = animancerComponent.Layers[1].Play(_animations.AttachClip, 0.2f);
state.Time = 0;
await state;
if (animancerComponent)
state.Stop();
}
}
}
public void Dispose()
{
_ticker.Remove(OnTick);
}
}
}