using Simulator.UI; using Simulator.Model; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UVC.Data.Core; namespace Simulator.Data { public class SourceComponent : ComponentBase { public SourceDataClass sourceData = new SourceDataClass(); SourceModelData sourceModelData = new SourceModelData(); public List entitySocket; List possessEntities = new List(); public int count = 0; public int index = 0; [SerializeField] BubbleUI bubbleUIPrefab; BubbleUI currentBubble; public void SetComponent(SourceDataClass sourceData) { this.sourceData = sourceData; data = sourceData; onComponentClicked += FindAnyObjectByType().SetProertyWindow; FitCollider(); } void IncreaseCount() { count++; if (count >= entitySocket.Count) { //index++; count = 0; } } void PlaceNext(Entity entity) { if (entitySocket == null || entitySocket.Count == 0) { Debug.LogWarning("[SourceComponent] entitySocket이 비어있습니다. 배치를 건너뜁니다."); return; } // 현재 소켓이 비어있으면 다음 유효 소켓을 찾는다(안전장치) int guard = 0; while ((entitySocket[count] == null) && guard < entitySocket.Count) { IncreaseCount(); guard++; } if (entitySocket[count] == null) { Debug.LogWarning("[SourceComponent] 사용 가능한 소켓이 없습니다. 배치를 건너뜁니다."); return; } // 부모/로컬 트랜스폼 설정 entity.transform.SetParent(entitySocket[count].transform, worldPositionStays: false); // Z방향으로 적층(원래 로직 유지) float stepZ = EntityManager.Instance.ObjectSize.z; entity.transform.localPosition = new Vector3(0f, 0f, stepZ * index); entity.transform.localRotation = Quaternion.identity; if (!possessEntities.Contains(entity)) possessEntities.Add(entity); IncreaseCount(); SetBubble($"{possessEntities.Count}/{sourceModelData.total_entity}"); } public override void GetModelData(DataObject modelData) { sourceModelData.total_entity = modelData.GetDataObject("data").GetInt("total_entity") ?? 0; var datas = modelData.GetDataObject("data"); var entity_Ids = datas.GetDataArray("entity_ids"); foreach (var entity in entity_Ids) { var id = entity.GetString("entity_id"); var name = entity.GetString("prefab_name"); PlaceNext(EntityManager.Instance.SpawnEntity(id, this, name)); } /* var entityIds = datas["entity_ids"].ConvertTo>(); if (entityIds == null || entityIds.Count == 0) { Debug.LogWarning("[SourceComponent] entity_ids가 비어있습니다."); return; } // 여러 개를 한 번에 스폰 var entities = EntityManager.Instance.SpawnEntites(entityIds, this); if (entities == null || entities.Count == 0) { return; } // 배치 foreach (var e in entities) { if (e == null) continue; PlaceNext(e); } */ } public override void DecreaseEntity(Entity entity) { possessEntities.Remove(entity); SetBubble($"{possessEntities.Count}/{sourceModelData.total_entity}"); ReflowEntities(); } public void ReflowEntities() { // 위치 인덱스 초기화 count = 0; index = 0; // 🔑 핵심: 스냅샷을 만든 뒤 원본을 비우고, 스냅샷 기반으로 재배치 // 이렇게 하면 순회 중 리스트 수정으로 인한 예외가 발생하지 않습니다. var snapshot = new List(possessEntities.Count); foreach (var e in possessEntities) { if (e != null) snapshot.Add(e); } possessEntities.Clear(); foreach (var e in snapshot) { PlaceNext(e); // 여기서 다시 possessEntities에 안전하게 채워짐 } } public void SetBubble(string text) { if (currentBubble == null) { // 생성 currentBubble = Instantiate(bubbleUIPrefab, FindAnyObjectByType().transform); currentBubble.target = transform; currentBubble.worldOffset = new Vector3(0, 2.0f, 0); // 필요에 따라 조절 currentBubble.GetComponent().SetAsFirstSibling(); } // 텍스트 갱신 currentBubble.SetMessage(text); } public override void getpath() { onComponentClicked?.Invoke(componentType, sourceData); } } }