Files
XRLib/Assets/Scripts/Simulator/Components/Conveyor/ConveyorComponent.cs
2026-02-23 17:06:38 +09:00

273 lines
8.2 KiB
C#

using Gpm.Ui.Sample;
using sc.modeling.splines.runtime;
using Simulator.Data;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
using UVC.Data.Core;
public enum conveyorType
{
partial,
overall
}
public class ConveyorComponent : ComponentBase
{
public List<ConveyorTarget> ConveyorQueue = new List<ConveyorTarget>();
public Stack<ConveyorTarget> WaitingQueue = new Stack<ConveyorTarget>();
public List<SplineMesher> mesher = new List<SplineMesher>();
SplineContainer spline;
float duration = 3f;
public conveyorType type = conveyorType.overall;
public int segmentCount = 10;
public float borderEpsilon = 1e-4f;
bool[] occupancy;
ConveyorPath conveyorPath;
public event Action<ConveyorPath> onConveyorClicked;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
spline = GetComponent<SplineContainer>();
occupancy = new bool[Mathf.Max(1, segmentCount)];
EntityManager.Instance.OnEntityDestroyed += UnSetEntity;
EntityManager.Instance.onEntityGet += UnSetEntity;
}
// Update is called once per frame
void Update()
{
/*
if (Input.GetKeyDown(KeyCode.G))
{
GameObject target = GameObject.CreatePrimitive(PrimitiveType.Cube);
var cTraget = target.AddComponent<ConveyorTarget>();
cTraget.Progress = 0f;
ConveyorQueue.Add(cTraget);
}
if (Input.GetKeyDown(KeyCode.S))
{
// 출구에서 한 개 배출
if (WaitingQueue.Count > 0)
{
var target = WaitingQueue.Pop();
Destroy(target.gameObject);
}
}
if (type == conveyorType.overall)
{
if (WaitingQueue.Count < 1)
{
SetProgress(Time.deltaTime / duration);
MoveOnConveyor();
}
}
else // partial
{
SetProgress(Time.deltaTime / duration);
MoveOnConveyor();
}
*/
SetProgress(Time.deltaTime / duration);
MoveOnConveyor();
}
public void SetComponent(ConveyorPath conveyorData)
{
this.conveyorPath = conveyorData;
onConveyorClicked += FindAnyObjectByType<ConveyorProperty>().SetProertyWindow;
var box=gameObject.AddComponent<BoxCollider>();
box.isTrigger = true;
}
void SetConveyorEntity(Entity entity)
{
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
cTarget.transform.SetParent(this.transform, worldPositionStays: false);
cTarget.transform.localPosition = Vector3.zero;
cTarget.transform.localRotation = Quaternion.identity;
cTarget.Progress = 0f;
ConveyorQueue.Add(cTarget);
}
public void SetModelData(DataObject datas)
{
var data = datas.GetDataObject("data");
var entityId = data.GetString("entity_id_original");
var timing = data.GetDataObject("timing");
var durationdata = timing.GetDataObject("duration");
duration = (float)durationdata.GetFloat("real_seconds");
SetEntity(entityId);
}
public override void SetEntity(string key, string name = "")
{
var entity = EntityManager.Instance.GetEntity(key, this,name);
SetConveyorEntity(entity);
}
public void UnSetEntity(Entity entity)
{
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
if (ConveyorQueue.Contains(cTarget))
{
ConveyorQueue.Remove(cTarget);
}
}
public void UnSetModelData(DataObject datas)
{
var data = datas.GetDataObject("data");
var entityId = data.GetString("entity_id_original");
var entity = EntityManager.Instance.GetEntity(entityId,this);
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
if (ConveyorQueue.Contains(cTarget))
{
ConveyorQueue.Remove(cTarget);
}
}
void SetProgress(float progress)
{
if (ConveyorQueue.Count == 0) return;
if (type == conveyorType.overall)
{
foreach (var target in ConveyorQueue)
{
target.Progress += progress;
if (target.Progress > 1f) target.Progress = 1f;
}
}
else
{
PartialAdvance(progress);
}
}
void MoveOnConveyor()
{
if (spline == null || spline.Splines.Count == 0) return;
Spline s = spline.Splines[0];
for (int i = ConveyorQueue.Count - 1; i >= 0; i--)
{
var target = ConveyorQueue[i];
float t = Mathf.Clamp01(target.Progress);
// 1) 스플라인 "로컬" 위치/탄젠트/업 벡터
Vector3 pLocal = SplineUtility.EvaluatePosition(s, t);
Vector3 tanLocal = SplineUtility.EvaluateTangent(s, t);
if (tanLocal.sqrMagnitude < 1e-8f) tanLocal = Vector3.forward;
tanLocal.Normalize();
// Up 벡터는 splines 패키지 버전에 따라 API가 다를 수 있어 안전하게 처리
// 가능하면 EvaluateUpVector 사용, 없으면 Vector3.up 사용
Vector3 upLocal = Vector3.up;
#if UNITY_SPLINES
// 일부 버전에서 제공
// upLocal = SplineUtility.EvaluateUpVector(s, t);
#endif
upLocal.Normalize();
Vector3 rightLocal = Vector3.Cross(upLocal, tanLocal).normalized;
// Up이 탄젠트와 평행해지는 특이점 방지
if (rightLocal.sqrMagnitude < 1e-8f)
rightLocal = Vector3.Cross(Vector3.up, tanLocal).normalized;
// 2) "스플라인 프레임" 기준 오프셋
Vector3 offsetLocal =
rightLocal * 0.0f +
upLocal * 0.0f +
tanLocal * 0.0f;
Vector3 finalLocalPos = pLocal + offsetLocal;
// 3) 화물은 컨베이어 자식으로 두고 localPosition으로 넣기
target.transform.localPosition = finalLocalPos;
if (target.Progress >= 1f)
{
ConveyorQueue.RemoveAt(i);
WaitingQueue.Push(target);
}
}
}
void PartialAdvance(float delta)
{
ConveyorQueue.Sort((a, b) => b.Progress.CompareTo(a.Progress));
int cellCount = Mathf.Max(1, segmentCount);
float cell = 1f / cellCount;
if (occupancy == null || occupancy.Length != cellCount)
occupancy = new bool[cellCount];
System.Array.Clear(occupancy, 0, occupancy.Length);
if (WaitingQueue.Count > 0)
occupancy[cellCount - 1] = true;
foreach (var t in ConveyorQueue)
{
float desired = Mathf.Min(1f, t.Progress + delta);
int currCell = CellIndex(t.Progress, cellCount);
int wantCell = CellIndex(desired, cellCount);
int allowedCell = currCell;
for (int c = currCell + 1; c <= wantCell && c < cellCount; c++)
{
if (occupancy[c]) break;
allowedCell = c;
}
float maxProg = Mathf.Min(desired, CellEnd(allowedCell, cellCount) - borderEpsilon);
if (allowedCell == cellCount - 1)
maxProg = Mathf.Min(desired, 1f);
t.Progress = Mathf.Clamp01(maxProg);
int newCell = CellIndex(t.Progress, cellCount);
occupancy[newCell] = true;
}
}
int CellIndex(float progress, int cellCount)
{
float cell = 1f / cellCount;
int idx = Mathf.FloorToInt(progress / cell);
return Mathf.Clamp(idx, 0, cellCount - 1);
}
float CellEnd(int cellIndex, int cellCount)
{
float cell = 1f / cellCount;
return (cellIndex + 1) * cell;
}
void UpdateKnot(BezierKnot knot, int index, int splineIndex)
{
spline.Splines[splineIndex].SetKnot(index, knot);
}
void UpdateMesh()
{
foreach (var m in mesher)
{
m.Rebuild();
}
}
public override void getpath()
{
onConveyorClicked?.Invoke(conveyorPath);
}
}