using sc.modeling.splines.runtime; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Splines; using UVC.Data.Core; public enum conveyorType { partial, overall } public class ConveyorComponent : ComponentBase { public List ConveyorQueue = new List(); public Stack WaitingQueue = new Stack(); public List mesher = new List(); SplineContainer spline; float duration = 3f; // === 추가: 슬롯 기반 제어 === public conveyorType type = conveyorType.overall; public int segmentCount = 10; // 칸 개수 public float borderEpsilon = 1e-4f; // 경계 떨림 방지 bool[] occupancy; // 프레임 점유 캐시 // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { spline = GetComponent(); occupancy = new bool[Mathf.Max(1, segmentCount)]; EntityManager.Instance.OnEntityDestroyed += UnSetEntity; } // Update is called once per frame void Update() { /* if (Input.GetKeyDown(KeyCode.G)) { GameObject target = GameObject.CreatePrimitive(PrimitiveType.Cube); var cTraget = target.AddComponent(); 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(); } void SetConveyorEntity(Entity entity) { var cTarget = entity.gameObject.GetComponent(); cTarget.transform.SetParent(this.transform, worldPositionStays: false); cTarget.Progress = 0f; ConveyorQueue.Add(cTarget); } public void SetModelData(DataObject datas) { var data = datas.GetDataObject("data"); var entityId = data.GetString("entity_id_original"); var entity = EntityManager.Instance.GetEntity(entityId,this); if (entity == null) { Debug.Log(entityId); Debug.Log(entity); } duration = (float)data.GetFloat("travel_time"); SetConveyorEntity(entity); } public void UnSetEntity(Entity entity) { var cTarget = entity.gameObject.GetComponent(); 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(); 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() { Spline s = spline.Splines[0]; for (int i = ConveyorQueue.Count - 1; i >= 0; i--) { var target = ConveyorQueue[i]; Vector3 localPos = SplineUtility.EvaluatePosition(s, target.Progress); target.transform.position = localPos; target.transform.position += new Vector3(0, 1f, -0.275f); // 끝(1.0) 도달 → WaitingQueue로 이동(배출 대기) 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); // 마지막 칸이면 1.0까지 허용(도달 시 MoveOnConveyor에서 대기열로 이동) 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(); } } }