Files
Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/WorldNodeService/UnitySubwayService.cs

196 lines
7.6 KiB
C#
Raw Normal View History

2025-06-24 23:49:13 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit;
using BITKit.Entities;
using Cysharp.Threading.Tasks;
using Dreamteck.Splines;
using Net.Project.B.Interaction;
using Unity.Mathematics;
using UnityEngine;
namespace Net.Project.B.WorldNode
{
public class UnitySubwayService:IDisposable
{
private readonly IWorldInteractionService _interactionService;
private readonly IEntitiesService _entitiesService;
private readonly IFixedTicker _ticker;
private readonly Dictionary<int, int> _prevPlatform = new();
private readonly Dictionary<int, SplineComputer> _splineComputers = new();
private readonly Dictionary<int, float> _distances = new();
private readonly Dictionary<int, float> _lengths = new();
private readonly Dictionary<int, float> _vehicleLength=new();
private readonly HashSet<int> _isBusy = new();
public UnitySubwayService(IEntitiesService entitiesService, IFixedTicker ticker, IWorldInteractionService interactionService)
{
_entitiesService = entitiesService;
_ticker = ticker;
_interactionService = interactionService;
_ticker.Add(OnTick);
_entitiesService.OnAdd += OnAdd;
}
private void OnAdd(IEntity obj)
{
if (obj.ServiceProvider.QueryComponents(out UnitySubwayNode subwayNode, out Transform transform))
{
var colliders = transform.GetComponentsInChildren<Collider>();
foreach (var x in colliders)
{
foreach (var y in colliders)
{
Physics.IgnoreCollision(x, y, true);
}
}
var spline = _splineComputers[obj.Id] = subwayNode.Spline.GetComponent<SplineComputer>();
var sample = spline.Project(transform.position);
var distance = Mathf.Lerp(0, _lengths[obj.Id]=spline.CalculateLength(),(float)sample.percent);
subwayNode.Subway.isKinematic = true;
subwayNode.Subway.interpolation = RigidbodyInterpolation.Interpolate;
_distances[obj.Id] = distance;
if(subwayNode.FrontWheel && subwayNode.BackWheel)
{
var localFrontPosition = subwayNode.FrontWheel.localPosition;
var localBackPosition = subwayNode.BackWheel.localPosition;
_vehicleLength[obj.Id] = Vector3.Distance(localFrontPosition, localBackPosition);
}
}
}
private void OnTick(float obj)
{
var gc = GC.GetTotalMemory(false);
var stopNodes = _entitiesService.QueryComponents<IEntity,UnitySubwayPlatformNode, Transform>();
foreach (var (entity, transform, subwayNode) in _entitiesService
.QueryComponents<IEntity, Transform, UnitySubwayNode>())
{
if (_isBusy.Contains(entity.Id)) continue;
var spline = _splineComputers[entity.Id];
foreach (var (platformEntity, platformNode, platformTransform) in stopNodes)
{
var sample = spline.Project(platformTransform.position);
var subwayPosition = transform.position;
if (Vector3.Distance(sample.position, subwayPosition) < 1)
{
if (_prevPlatform.TryGetValue(entity.Id, out var prevPlatform) is false ||
prevPlatform != platformEntity.Id)
{
subwayNode.Subway.isKinematic = true;
_prevPlatform[entity.Id] = platformEntity.Id;
Arrived(entity.Id, platformNode.StopTime);
break;
}
}
var distance = _distances[entity.Id];
var length = _lengths[entity.Id];
distance = (distance + subwayNode.Speed * obj) % length;
_distances[entity.Id] = distance;
var percentage = distance<=0 ? 0 : distance / length;
sample= spline.Evaluate(percentage,SplineComputer.EvaluateMode.Calculate);
if (subwayNode.Offset.sqrMagnitude > 0.1f)
{
sample.position += sample.rotation * subwayNode.Offset;
}
if (_vehicleLength.TryGetValue(entity.Id, out var vehicleLength))
{
var sampleFront =spline.Evaluate(GetPercent(distance + vehicleLength / 2)) ;
var sampleBack =spline.Evaluate(GetPercent(distance - vehicleLength / 2)) ;
var up = Vector3.Cross(sample.forward, sample.right);
var rotation = quaternion.LookRotation(sampleFront.position - sampleBack.position, up);
subwayNode.Subway.MoveRotation(rotation);
var newPosition = Vector3.Lerp(sampleBack.position, sampleFront.position, 0.5f);
subwayNode.Subway.MovePosition(newPosition);
//_distances[entity.Id] = (float)spline.Project(newPosition).percent * length;
}
else
{
subwayNode.Subway.MovePosition(sample.position); //sample.position);
subwayNode.Subway.MoveRotation(sample.rotation);
}
continue;
float GetPercent(float d)
{
d %= length;
return d<=0 ? 0 : d / length;
}
}
}
var after = GC.GetTotalMemory(false);
if (gc != after)
{
// Debug.Log($"GC消耗:{gc}=>{after}");
}
}
public void Dispose()
{
_ticker.Remove(OnTick);
}
private async void Arrived(int id, float stopTime)
{
if (_entitiesService.TryGetEntity(id, out var entity) is false) return;
if (entity.ServiceProvider.QueryComponents(out Transform transform,out UnitySubwayNode subwayNode) is false) return;
Door();
try
{
_isBusy.Add(entity.Id);
await UniTask.Delay(TimeSpan.FromSeconds(stopTime),cancellationToken: entity.CancellationToken);
Door();
await UniTask.Delay(TimeSpan.FromSeconds(4),cancellationToken: entity.CancellationToken);
}
catch (OperationCanceledException)
{
}
_isBusy.Remove(entity.Id);
return;
void Door()
{
foreach (var x in transform.GetComponentsInChildren<IEntity>())
{
if (x.ServiceProvider.QueryComponents(out UnityDoorNode doorNode,
out IWorldInteractable interactable))
{
_interactionService.Interaction(this, interactable, WorldInteractionProcess.System);
}
}
}
}
}
}