using UnityEngine; using System; using System.Collections.Generic; using UnityEngine.Events; using Octopus.Simulator.Networks; using Newtonsoft.Json; using Octopus.Simulator; #region class [Serializable] public class MoveDataClass_start { public string move_id; public string move_label; public string type; public float move_time; public int capacity; public int products_per_batch; public List input_queues; public string output_queue; public string defect_queue; public string required_resource; public float return_time; public int min_batch_size; public float loading_delay; public float unloading_delay; } [Serializable] public class MoveDataClass_loading { public string move_id; public string move_label; public int loaded_count; public List source_queues; public List source_stores; public float loading_delay; } [Serializable] public class MoveDataClass_Request_Resource { public string move_id; public string move_label; } [Serializable] public class MoveDataClass_Resource_Acquired { public string resource; } [Serializable] public class MoveDataClass_Speed_Factor_Applied { public float original; public float adjusted; } [Serializable] public class MoveDataClass_Move_Moving { public float time; public float loading_delay; public float unloading_delay; public MoveMovingInput input; public MoveMovingOutput output; } [Serializable] public class MoveMovingInput { public List queues; public List stores; } [Serializable] public class MoveMovingOutput { public string queue; public string store; public string defect_queue; public string defect_store; } [Serializable] public class MoveDataClass_Arrived { public int count; } [Serializable] public class MoveDataClass_Unloading_Defect { public string product_id; public string output_queue; public int output_queue_length; public float unloading_delay; } [Serializable] public class MoveDataClass_Unloading_Defect_Store { public string product_id; public string store_name; public int store_length; public float unloading_delay; } [Serializable] public class MoveDataClass_Unloading { public string product_id; public string output_queue; public int output_queue_length; public float unloading_delay; } [Serializable] public class MoveDataClass_Unloading_Store { public string product_id; public string store_name; public int store_queue_length; public float unloading_delay; } [Serializable] public class MoveDataClass_returning { public float return_time; } [Serializable] public class MoveDataClassStatistics { public statisticsMove statistics; } [Serializable] public class statisticsMove { public int total_moved; } public enum UnLoadingState { store, queue, defectStore, defectQueue } #endregion public class SimulationModelMove : SimulationModel { [Header("Transport")] public List listProducts = new List(); public Transform productPos; public Vector3 productDistance = new Vector3(0, 0.321f, 0); int productCount = 0; int currentCount = 0; int totalMoved = 0; bool isFirstTransport = true; Transform origin = null; public Transform destination = null; public float arrivalTime = 0; public float elapsedTime = 0; Vector3 originalPos; Vector3 prevPos; void Update() { if (!simulationUI.isplaying) return; UpdateProductStacking(); UpdateMovement(); } private void UpdateProductStacking() { if (productCount != listProducts.Count) { productCount = listProducts.Count; // 단순하게 모두 같은 offset으로 배치 (필요 시 i 기반 offset으로 확장) foreach (var gb in listProducts) { if (gb != null) { gb.transform.localPosition = productDistance; } } } } private void UpdateMovement() { if (destination == null) return; elapsedTime += Time.deltaTime * WebParameters.speed; if (arrivalTime <= 0f) return; float t = elapsedTime / arrivalTime; if (t > 1f) { transform.position = destination.position; transform.forward = destination.forward; destination = null; } else { Vector3 newPos = Vector3.Lerp(originalPos, destination.position, t); transform.position = newPos; Vector3 dir = newPos - prevPos; dir.y = 0; if (dir.sqrMagnitude > 0.0001f) { dir.Normalize(); transform.forward = dir; } prevPos = newPos; } } public override void GetData(string data) { var wrapclass = JsonConvert.DeserializeObject(data); if (wrapclass == null || string.IsNullOrEmpty(wrapclass._event)) return; switch (wrapclass._event) { case "move_loading": LoadingCargo(wrapclass.data.ToString()); break; case "move_moving": Moving(wrapclass.data.ToString()); break; case "move_unloading": Unloading(wrapclass.data.ToString(), UnLoadingState.queue); break; case "move_unloading_store": Unloading(wrapclass.data.ToString(), UnLoadingState.store); break; case "move_unloading_defect": Unloading(wrapclass.data.ToString(), UnLoadingState.defectQueue); break; case "move_unloading_defect_store": Unloading(wrapclass.data.ToString(), UnLoadingState.defectStore); break; case "move_arrived": Arrive(wrapclass.data.ToString()); break; case "move_speed_factor_applied": SpeedFactorApply(wrapclass.data.ToString()); break; case "move_returning": Returning(wrapclass.data.ToString()); break; case "move_statistics_update": totalMoved = JsonConvert.DeserializeObject(wrapclass.data.ToString()).statistics.total_moved; SetBubble(totalMoved); break; } } private void LoadingCargo(string data) { var loading = JsonConvert.DeserializeObject(data); if (loading == null) return; currentCount = loading.loaded_count; SetBubble(currentCount); // 처음 운송이면 출발 위치 세팅 if (isFirstTransport) { string sourceId = GetFirstNonEmpty(loading.source_queues, loading.source_stores); if (!string.IsNullOrEmpty(sourceId)) { var sourceModel = DataManager.I.GetModel(sourceId) as SimulationModelStore; if (sourceModel != null) { transform.position = sourceModel.GetTransporterPosition()?.position ?? transform.position; } } isFirstTransport = false; } AddProducts(loading.loaded_count); } private void Moving(string data) { var moveData = JsonConvert.DeserializeObject(data); if (moveData == null) return; elapsedTime = 0f; arrivalTime = moveData.time; originalPos = transform.position; prevPos = transform.position; string inputId = GetFirstNonEmpty(moveData.input?.queues, moveData.input?.stores); if (!string.IsNullOrEmpty(inputId)) { var model = DataManager.I.GetModel(inputId) as SimulationModelStore; if (model != null) origin = model.GetTransporterPosition(); } if (!string.IsNullOrEmpty(moveData.output?.queue)) { var model = DataManager.I.GetModel(moveData.output.queue) as SimulationModelStore; if (model != null) destination = model.GetTransporterPosition(); } else if (!string.IsNullOrEmpty(moveData.output?.store)) { var model = DataManager.I.GetModel(moveData.output.store) as SimulationModelStore; if (model != null) destination = model.GetTransporterPosition(); } } private string GetFirstNonEmpty(List listA, List listB) { if (listA != null && listA.Count > 0) return listA[0]; if (listB != null && listB.Count > 0) return listB[0]; return string.Empty; } private void AddProducts(int count) { for (int i = 0; i < count; i++) { var product = ProductManager.Instance.SpawnProduct(); if (product == null) continue; product.GetComponent().SetParent(this.nodeID); product.transform.parent = productPos; product.transform.localPosition = Vector3.zero; product.transform.localRotation = Quaternion.identity; listProducts.Add(product); } } private void Unloading(string data, UnLoadingState state) { string targetID = string.Empty; switch (state) { case UnLoadingState.store: var store = JsonConvert.DeserializeObject(data); targetID = store?.store_name ?? string.Empty; break; case UnLoadingState.queue: var queue = JsonConvert.DeserializeObject(data); targetID = queue?.output_queue ?? string.Empty; break; case UnLoadingState.defectStore: var defectStore = JsonConvert.DeserializeObject(data); targetID = defectStore?.store_name ?? string.Empty; break; case UnLoadingState.defectQueue: var defectQueue = JsonConvert.DeserializeObject(data); targetID = defectQueue?.output_queue ?? string.Empty; break; } if (string.IsNullOrEmpty(targetID)) return; var model = DataManager.I.GetModel(targetID); if (model == null) { return; } var storeModel = model.GetComponent(); if (listProducts.Count > 0) { var product = listProducts[0]; // storeModel?.StoreProduct(product); // 필요 시 활성화 listProducts.RemoveAt(0); Destroy(product); } else { Debug.LogWarning("Trying to unload from empty transporter : " + nodeID); } } private void Arrive(string data) { var arrived = JsonConvert.DeserializeObject(data); if (arrived == null) return; currentCount -= arrived.count; SetBubble(currentCount); } private void SpeedFactorApply(string data) { var speedData = JsonConvert.DeserializeObject(data); if (speedData == null) return; float originalTime = speedData.original; float adjustedTime = speedData.adjusted; arrivalTime += adjustedTime - originalTime; elapsedTime = 0f; originalPos = transform.position; prevPos = transform.position; } private void Returning(string data) { var ret = JsonConvert.DeserializeObject(data); if (ret == null) return; elapsedTime = 0f; arrivalTime = ret.return_time; originalPos = transform.position; prevPos = transform.position; if (origin != null) destination = origin; } public override void SetBubble(object data) { string msg = $"{currentCount}/{totalMoved}"; if (currentBubble == null) { // 생성 currentBubble = Instantiate(bubbleUIPrefab, FindAnyObjectByType().transform); currentBubble.target = DataBubbleSocket; currentBubble.worldOffset = new Vector3(0, 2.0f, 0); // 필요에 따라 조절 currentBubble.GetComponent().SetAsFirstSibling(); } // 텍스트 갱신 currentBubble.SetMessage(msg); List datas = new List { currentCount.ToString(), totalMoved.ToString() }; currentBubble.SetDetail(datas, logicType.move, LogicUIManager.instance.GetItemLabelByID(nodeID)); } }