초기재고설정
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
public readonly struct AsrsRect
|
||||
{
|
||||
public readonly int MinX, MinY, MaxX, MaxY; // inclusive
|
||||
|
||||
public AsrsRect(int minX, int minY, int maxX, int maxY)
|
||||
{
|
||||
MinX = minX; MinY = minY; MaxX = maxX; MaxY = maxY;
|
||||
}
|
||||
|
||||
public static AsrsRect FromTwoPoints(Vector2Int a, Vector2Int b)
|
||||
{
|
||||
int minX = Mathf.Min(a.x, b.x);
|
||||
int maxX = Mathf.Max(a.x, b.x);
|
||||
int minY = Mathf.Min(a.y, b.y);
|
||||
int maxY = Mathf.Max(a.y, b.y);
|
||||
return new AsrsRect(minX, minY, maxX, maxY);
|
||||
}
|
||||
|
||||
public bool Contains(int x, int y)
|
||||
=> x >= MinX && x <= MaxX && y >= MinY && y <= MaxY;
|
||||
|
||||
public bool EqualsTo(in AsrsRect other)
|
||||
=> MinX == other.MinX && MinY == other.MinY && MaxX == other.MaxX && MaxY == other.MaxY;
|
||||
|
||||
public override string ToString() => $"[{MinX},{MinY}]~[{MaxX},{MaxY}]";
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0726b8ac6e21d324db156de3b669450d
|
||||
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
using Simulator.Data;
|
||||
|
||||
public static class AsrsPositionUtil
|
||||
{
|
||||
// 서버/모델이 float로 주더라도 실제 그리드 좌표는 정수여야 하므로 RoundToInt를 사용합니다.
|
||||
public static Vector3Int ToGridInt(Position p)
|
||||
{
|
||||
return new Vector3Int(
|
||||
Mathf.RoundToInt(p.x),
|
||||
Mathf.RoundToInt(p.y),
|
||||
Mathf.RoundToInt(p.z)
|
||||
);
|
||||
}
|
||||
|
||||
public static Position ToPosition(Vector3Int v)
|
||||
{
|
||||
return new Position { x = v.x, y = v.y, z = v.z };
|
||||
}
|
||||
|
||||
public static (Vector3Int from, Vector3Int to) Normalize(Vector3Int from, Vector3Int to)
|
||||
{
|
||||
int minX = Mathf.Min(from.x, to.x);
|
||||
int maxX = Mathf.Max(from.x, to.x);
|
||||
int minY = Mathf.Min(from.y, to.y);
|
||||
int maxY = Mathf.Max(from.y, to.y);
|
||||
int minZ = Mathf.Min(from.z, to.z);
|
||||
int maxZ = Mathf.Max(from.z, to.z);
|
||||
|
||||
return (new Vector3Int(minX, minY, minZ), new Vector3Int(maxX, maxY, maxZ));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd4cb797d03dff245942d366be37448d
|
||||
@@ -0,0 +1,56 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public sealed class GridCellView : MonoBehaviour,
|
||||
IPointerEnterHandler, IPointerClickHandler
|
||||
{
|
||||
[Header("UI")]
|
||||
[SerializeField] private Image background;
|
||||
[SerializeField] private TMP_Text label;
|
||||
[SerializeField] private GameObject plusIcon; // 미리보기 선택
|
||||
[SerializeField] private GameObject setIcon; // 확정 설정
|
||||
|
||||
public int RackIndex { get; private set; } // 1..z
|
||||
public int X { get; private set; } // 1..layout.x
|
||||
public int Y { get; private set; } // 1..layout.y
|
||||
|
||||
private GridView owner;
|
||||
|
||||
public void Init(GridView owner, int rackIndex, int x, int y)
|
||||
{
|
||||
this.owner = owner;
|
||||
RackIndex = rackIndex;
|
||||
X = x;
|
||||
Y = y;
|
||||
|
||||
if (label != null)
|
||||
label.text = "1";
|
||||
|
||||
SetSelecting(false);
|
||||
SetOccupied(false);
|
||||
}
|
||||
|
||||
public void SetSelecting(bool on)
|
||||
{
|
||||
if (plusIcon != null) plusIcon.SetActive(on);
|
||||
}
|
||||
|
||||
public void SetOccupied(bool on)
|
||||
{
|
||||
if (setIcon != null) setIcon.SetActive(on);
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (owner == null) return;
|
||||
owner.OnCellPointerEnter(this);
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (owner == null) return;
|
||||
owner.OnCellPointerClick(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f53304a483278347ada0f85f7ff8279
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class GridView : MonoBehaviour
|
||||
{
|
||||
[Header("UI")]
|
||||
[SerializeField] private TMP_Text titleLabel;
|
||||
[SerializeField] private GridLayoutGroup grid;
|
||||
[SerializeField] private GridCellView cellPrefab;
|
||||
|
||||
public int RackIndex { get; private set; } // 1..z
|
||||
public int SizeX { get; private set; } // layout.x
|
||||
public int SizeY { get; private set; } // layout.y
|
||||
|
||||
private readonly Dictionary<(int x, int y), GridCellView> cells = new();
|
||||
private InitialInventoryWindow owner;
|
||||
|
||||
public void Build(InitialInventoryWindow owner, int rackIndex, int sizeX, int sizeY)
|
||||
{
|
||||
this.owner = owner;
|
||||
RackIndex = rackIndex;
|
||||
SizeX = sizeX;
|
||||
SizeY = sizeY;
|
||||
|
||||
if (titleLabel != null)
|
||||
titleLabel.text = $"{rackIndex}번 랙";
|
||||
|
||||
if (grid != null)
|
||||
{
|
||||
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
|
||||
grid.constraintCount = sizeX;
|
||||
}
|
||||
|
||||
foreach (Transform child in grid.transform)
|
||||
Destroy(child.gameObject);
|
||||
cells.Clear();
|
||||
|
||||
// 위가 큰 y가 되도록 내림차순
|
||||
for (int y = sizeY; y >= 1; y--)
|
||||
{
|
||||
for (int x = 1; x <= sizeX; x++)
|
||||
{
|
||||
var cell = Instantiate(cellPrefab, grid.transform);
|
||||
cell.Init(this, rackIndex, x, y);
|
||||
cells[(x, y)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOccupiedCells(HashSet<(int rack, int x, int y)> occupied)
|
||||
{
|
||||
foreach (var kv in cells)
|
||||
{
|
||||
var (x, y) = kv.Key;
|
||||
kv.Value.SetOccupied(occupied.Contains((RackIndex, x, y)));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelectionVisual(AsrsRect? rect)
|
||||
{
|
||||
foreach (var kv in cells)
|
||||
{
|
||||
var (x, y) = kv.Key;
|
||||
bool on = rect.HasValue && rect.Value.Contains(x, y);
|
||||
kv.Value.SetSelecting(on);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSelectionVisual()
|
||||
=> SetSelectionVisual(null);
|
||||
|
||||
// ===== Cell 이벤트 콜백 (GridCellView가 호출) =====
|
||||
public void OnCellPointerEnter(GridCellView cell)
|
||||
{
|
||||
if (owner == null) return;
|
||||
owner.OnCellHover(RackIndex, cell.X, cell.Y);
|
||||
}
|
||||
|
||||
public void OnCellPointerClick(GridCellView cell)
|
||||
{
|
||||
if (owner == null) return;
|
||||
owner.OnCellClick(RackIndex, cell.X, cell.Y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dc116da94c6741419aa342e47f7b841
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IAsrsPrefabCatalog
|
||||
{
|
||||
IReadOnlyList<PrefabCatalogItem> GetItems();
|
||||
|
||||
// 빠른 조회용(선택): prefab id로 PrefabData를 얻고 싶을 때
|
||||
bool TryGetById(string prefabId, out PrefabCatalogItem item);
|
||||
}
|
||||
|
||||
public readonly struct PrefabCatalogItem
|
||||
{
|
||||
public readonly string Id; // initialize.prefab에 저장되는 키
|
||||
public readonly PrefabData Data; // 사용자 제공 PrefabData
|
||||
|
||||
public PrefabCatalogItem(string id, PrefabData data)
|
||||
{
|
||||
Id = id;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
// UI 표시명 우선순위: label > name > id
|
||||
public string DisplayName
|
||||
=> !string.IsNullOrWhiteSpace(Data?.label) ? Data.label
|
||||
: !string.IsNullOrWhiteSpace(Data?.name) ? Data.name
|
||||
: Id;
|
||||
|
||||
public float Cost => Data != null ? Data.cost : 0f;
|
||||
public string Color => Data != null ? Data.color : null;
|
||||
public IReadOnlyList<PrefabDataTag> Tags => Data?.tags;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6851f9df1cc986c48b60c1689f0d9a93
|
||||
@@ -0,0 +1,39 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InitRow : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Toggle toggle;
|
||||
[SerializeField] private TMP_Text typeText;
|
||||
[SerializeField] private TMP_Text countText;
|
||||
[SerializeField] private TMP_Text fromText;
|
||||
[SerializeField] private TMP_Text toText;
|
||||
|
||||
public int EntryId { get; private set; }
|
||||
public bool IsSelected => toggle != null && toggle.isOn;
|
||||
|
||||
public void Bind(int entryId, string typeName, int count, Vector3Int from, Vector3Int to, UnityAction<int> onClickRow)
|
||||
{
|
||||
EntryId = entryId;
|
||||
|
||||
if (typeText != null) typeText.text = typeName;
|
||||
if (countText != null) countText.text = count.ToString();
|
||||
if (fromText != null) fromText.text = $"{from.x}, {from.y}, {from.z}";
|
||||
if (toText != null) toText.text = $"{to.x}, {to.y}, {to.z}";
|
||||
|
||||
// Row 클릭 시 선택 처리(원하면 버튼/이벤트로 바꿔도 됩니다)
|
||||
var btn = GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.RemoveAllListeners();
|
||||
btn.onClick.AddListener(() => onClickRow?.Invoke(entryId));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelected(bool on)
|
||||
{
|
||||
if (toggle != null) toggle.isOn = on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1bd5a5a1f0959b44bf2001e0885373d
|
||||
@@ -0,0 +1,471 @@
|
||||
using Simulator.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InitialInventoryWindow : MonoBehaviour
|
||||
{
|
||||
[Header("Top/Left")]
|
||||
[SerializeField] private TMP_Text dimLabel;
|
||||
[SerializeField] private Transform rackContainer; // ScrollRect Content
|
||||
[SerializeField] private GridView rackSectionPrefab; // Rack 섹션 프리팹
|
||||
|
||||
[Header("Right Controls")]
|
||||
[SerializeField] private TMP_Dropdown prefabDropdown;
|
||||
[SerializeField] private TMP_Text countText;
|
||||
|
||||
[Header("Table")]
|
||||
[SerializeField] private Transform tableContent; // ScrollRect Content
|
||||
[SerializeField] private InitRow rowPrefab;
|
||||
|
||||
[Header("Bottom Buttons")]
|
||||
[SerializeField] private Button btnApply; // Apply 버튼 (요구사항 추가)
|
||||
[SerializeField] private Button btnCancel; // 선택: 닫기
|
||||
[SerializeField] private Button btnReset; // 선택: 초기화
|
||||
|
||||
private ASRSDataClass asrs;
|
||||
|
||||
// rack views
|
||||
private readonly List<GridView> rackViews = new();
|
||||
|
||||
// ====== Click + Hover selection state ======
|
||||
private readonly Dictionary<int, Vector2Int> anchors = new();
|
||||
private readonly Dictionary<int, AsrsRect> previewRects = new();
|
||||
|
||||
// ====== Entry / Occupancy ======
|
||||
private sealed class Entry
|
||||
{
|
||||
public int id;
|
||||
public string prefabId;
|
||||
public string prefabDisplayName;
|
||||
public int count;
|
||||
public Vector3Int from; // inclusive
|
||||
public Vector3Int to; // inclusive
|
||||
}
|
||||
|
||||
private readonly List<Entry> entries = new();
|
||||
private readonly Dictionary<(int rack, int x, int y), int> occupancy = new(); // (rack,x,y) -> entryId
|
||||
private int nextEntryId = 1;
|
||||
|
||||
private int? selectedEntryId = null;
|
||||
|
||||
// prefab dropdown cache
|
||||
private List<PrefabCatalogFromManager.Item> prefabItems = new();
|
||||
|
||||
private int count = 1;
|
||||
|
||||
// 외부 버튼으로 모드 토글 가능
|
||||
public bool IsSelectionMode { get; private set; } = true;
|
||||
|
||||
// ===== Public API =====
|
||||
public void Open(ASRSDataClass asrs)
|
||||
{
|
||||
this.asrs = asrs;
|
||||
|
||||
BuildDimLabel();
|
||||
BuildPrefabDropdown();
|
||||
BuildRacks();
|
||||
|
||||
// 기존 initialize 로딩
|
||||
LoadInitialize(asrs.initialize);
|
||||
|
||||
HookButtons();
|
||||
RefreshAll();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public void SetSelectionMode(bool on)
|
||||
{
|
||||
IsSelectionMode = on;
|
||||
|
||||
if (!on)
|
||||
{
|
||||
anchors.Clear();
|
||||
previewRects.Clear();
|
||||
RefreshSelectionVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Build UI =====
|
||||
private void BuildDimLabel()
|
||||
{
|
||||
if (dimLabel == null || asrs?.asrs_layout == null) return;
|
||||
|
||||
var l = asrs.asrs_layout;
|
||||
|
||||
float xM = l.x * l.x_length;
|
||||
float yM = l.z * l.z_length;
|
||||
float zM = l.y * l.y_length;
|
||||
|
||||
dimLabel.text = $"{xM:0.##} m (x) × {yM:0.##} m (y) × {zM:0.##} m (z)";
|
||||
}
|
||||
|
||||
private void BuildPrefabDropdown()
|
||||
{
|
||||
if (prefabDropdown == null) return;
|
||||
|
||||
prefabItems = PrefabCatalogFromManager.GetSortedItems();
|
||||
|
||||
prefabDropdown.ClearOptions();
|
||||
prefabDropdown.AddOptions(prefabItems.Select(i => i.DisplayName).ToList());
|
||||
|
||||
prefabDropdown.value = 0;
|
||||
prefabDropdown.RefreshShownValue();
|
||||
|
||||
SetCount(count);
|
||||
}
|
||||
|
||||
private void BuildRacks()
|
||||
{
|
||||
foreach (Transform child in rackContainer)
|
||||
Destroy(child.gameObject);
|
||||
rackViews.Clear();
|
||||
|
||||
var l = asrs.asrs_layout;
|
||||
int rackCount = Mathf.Clamp(l.z, 1, 2);
|
||||
|
||||
for (int rack = 1; rack <= rackCount; rack++)
|
||||
{
|
||||
var view = Instantiate(rackSectionPrefab, rackContainer);
|
||||
view.Build(this, rack, l.x, l.y);
|
||||
rackViews.Add(view);
|
||||
}
|
||||
}
|
||||
|
||||
private void HookButtons()
|
||||
{
|
||||
if (btnApply != null)
|
||||
{
|
||||
btnApply.onClick.RemoveAllListeners();
|
||||
btnApply.onClick.AddListener(ApplyToAsrsInitialize);
|
||||
}
|
||||
|
||||
if (btnCancel != null)
|
||||
{
|
||||
btnCancel.onClick.RemoveAllListeners();
|
||||
btnCancel.onClick.AddListener(Close);
|
||||
}
|
||||
|
||||
if (btnReset != null)
|
||||
{
|
||||
btnReset.onClick.RemoveAllListeners();
|
||||
btnReset.onClick.AddListener(() =>
|
||||
{
|
||||
entries.Clear();
|
||||
occupancy.Clear();
|
||||
anchors.Clear();
|
||||
previewRects.Clear();
|
||||
selectedEntryId = null;
|
||||
nextEntryId = 1;
|
||||
RefreshAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCount(int newCount)
|
||||
{
|
||||
count = Mathf.Clamp(newCount, 1, 999999);
|
||||
if (countText != null) countText.text = count.ToString();
|
||||
}
|
||||
|
||||
private string GetSelectedPrefabId()
|
||||
{
|
||||
if (prefabItems == null || prefabItems.Count == 0) return null;
|
||||
int idx = prefabDropdown != null ? prefabDropdown.value : 0;
|
||||
idx = Mathf.Clamp(idx, 0, prefabItems.Count - 1);
|
||||
return prefabItems[idx].Id;
|
||||
}
|
||||
|
||||
private string ResolvePrefabDisplayName(string prefabId)
|
||||
=> PrefabCatalogFromManager.ResolveDisplayName(prefabId);
|
||||
|
||||
// ===== initialize load (기존 데이터 반영) =====
|
||||
private void LoadInitialize(List<InitializeEntry> initList)
|
||||
{
|
||||
entries.Clear();
|
||||
occupancy.Clear();
|
||||
selectedEntryId = null;
|
||||
nextEntryId = 1;
|
||||
|
||||
anchors.Clear();
|
||||
previewRects.Clear();
|
||||
|
||||
if (initList == null) return;
|
||||
|
||||
foreach (var dto in initList)
|
||||
{
|
||||
if (dto == null) continue;
|
||||
|
||||
var fromV = AsrsPositionUtil.ToGridInt(dto.from_position);
|
||||
var toV = AsrsPositionUtil.ToGridInt(dto.to_position);
|
||||
(fromV, toV) = AsrsPositionUtil.Normalize(fromV, toV);
|
||||
|
||||
if (!CanPlace(fromV, toV))
|
||||
continue;
|
||||
|
||||
var entry = new Entry
|
||||
{
|
||||
id = nextEntryId++,
|
||||
prefabId = dto.prefab,
|
||||
prefabDisplayName = ResolvePrefabDisplayName(dto.prefab),
|
||||
count = dto.count,
|
||||
from = fromV,
|
||||
to = toV
|
||||
};
|
||||
|
||||
entries.Add(entry);
|
||||
MarkOccupancy(entry.id, entry.from, entry.to, true);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GridView -> Window 콜백 (Click + Hover)
|
||||
// ============================================================
|
||||
|
||||
public void OnCellHover(int rackIndex, int x, int y)
|
||||
{
|
||||
if (!IsSelectionMode) return;
|
||||
if (!anchors.TryGetValue(rackIndex, out var anchor)) return;
|
||||
|
||||
previewRects[rackIndex] = AsrsRect.FromTwoPoints(anchor, new Vector2Int(x, y));
|
||||
RefreshSelectionVisuals();
|
||||
}
|
||||
|
||||
public void OnCellClick(int rackIndex, int x, int y)
|
||||
{
|
||||
if (!IsSelectionMode) return;
|
||||
|
||||
// ✅ (추가) 앵커를 아직 안 잡은 상태에서, 이미 설정된 셀을 클릭하면 "그 영역(Entry) 삭제"
|
||||
// 사용 예: 1,3,1~4,4,1로 설정된 상태에서 3,4,1 클릭 -> 해당 Entry 전체 삭제
|
||||
if (!anchors.ContainsKey(rackIndex) && occupancy.TryGetValue((rackIndex, x, y), out int entryId))
|
||||
{
|
||||
DeleteEntryById(entryId);
|
||||
|
||||
// 삭제 후에는 선택 관련 미리보기/앵커도 정리(안전)
|
||||
anchors.Remove(rackIndex);
|
||||
previewRects.Remove(rackIndex);
|
||||
|
||||
RefreshAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// 1차 클릭: anchor 설정 + 1셀 미리보기 시작
|
||||
if (!anchors.ContainsKey(rackIndex))
|
||||
{
|
||||
anchors[rackIndex] = new Vector2Int(x, y);
|
||||
previewRects[rackIndex] = AsrsRect.FromTwoPoints(anchors[rackIndex], anchors[rackIndex]);
|
||||
|
||||
selectedEntryId = null;
|
||||
RefreshSelectionVisuals();
|
||||
RefreshTableSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2차 클릭: anchor ~ 현재까지 확정
|
||||
var rect = AsrsRect.FromTwoPoints(anchors[rackIndex], new Vector2Int(x, y));
|
||||
|
||||
bool committed = CommitRectToEntries(rackIndex, rect);
|
||||
if (committed)
|
||||
{
|
||||
anchors.Remove(rackIndex);
|
||||
previewRects.Remove(rackIndex);
|
||||
RefreshAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 실패 시: 미리보기 유지(앵커 유지)
|
||||
previewRects[rackIndex] = rect;
|
||||
RefreshSelectionVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CommitRectToEntries(int rackIndex, AsrsRect rect)
|
||||
{
|
||||
string prefabId = GetSelectedPrefabId();
|
||||
if (string.IsNullOrWhiteSpace(prefabId))
|
||||
return false;
|
||||
|
||||
var from = new Vector3Int(rect.MinX, rect.MinY, rackIndex);
|
||||
var to = new Vector3Int(rect.MaxX, rect.MaxY, rackIndex);
|
||||
(from, to) = AsrsPositionUtil.Normalize(from, to);
|
||||
|
||||
if (!CanPlace(from, to))
|
||||
return false;
|
||||
|
||||
var entry = new Entry
|
||||
{
|
||||
id = nextEntryId++,
|
||||
prefabId = prefabId,
|
||||
prefabDisplayName = ResolvePrefabDisplayName(prefabId),
|
||||
count = count,
|
||||
from = from,
|
||||
to = to
|
||||
};
|
||||
|
||||
entries.Add(entry);
|
||||
MarkOccupancy(entry.id, entry.from, entry.to, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanPlace(Vector3Int from, Vector3Int to)
|
||||
{
|
||||
(from, to) = AsrsPositionUtil.Normalize(from, to);
|
||||
|
||||
int minRack = Mathf.Min(from.z, to.z);
|
||||
int maxRack = Mathf.Max(from.z, to.z);
|
||||
|
||||
for (int rack = minRack; rack <= maxRack; rack++)
|
||||
{
|
||||
for (int y = from.y; y <= to.y; y++)
|
||||
{
|
||||
for (int x = from.x; x <= to.x; x++)
|
||||
{
|
||||
if (occupancy.ContainsKey((rack, x, y)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void MarkOccupancy(int entryId, Vector3Int from, Vector3Int to, bool occupy)
|
||||
{
|
||||
(from, to) = AsrsPositionUtil.Normalize(from, to);
|
||||
|
||||
int minRack = Mathf.Min(from.z, to.z);
|
||||
int maxRack = Mathf.Max(from.z, to.z);
|
||||
|
||||
for (int rack = minRack; rack <= maxRack; rack++)
|
||||
{
|
||||
for (int y = from.y; y <= to.y; y++)
|
||||
{
|
||||
for (int x = from.x; x <= to.x; x++)
|
||||
{
|
||||
var key = (rack, x, y);
|
||||
if (occupy) occupancy[key] = entryId;
|
||||
else occupancy.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Apply: 현재 변경사항을 asrs.initialize에 반영 =====
|
||||
|
||||
private void ApplyToAsrsInitialize()
|
||||
{
|
||||
if (asrs == null) return;
|
||||
|
||||
// 현재 엔트리 -> InitializeEntry 리스트로 변환
|
||||
asrs.initialize = BuildInitializeEntries();
|
||||
|
||||
// 필요하면 여기서 닫거나, “저장됨” 토스트 등을 띄우면 됩니다.
|
||||
// Close();
|
||||
}
|
||||
|
||||
private List<InitializeEntry> BuildInitializeEntries()
|
||||
{
|
||||
// 중요: InitializeEntry의 from/to는 Position 타입이므로 변환 필요
|
||||
// 사용자님 기존 코드 방식 그대로 사용
|
||||
return entries.Select(e => new InitializeEntry
|
||||
{
|
||||
count = e.count,
|
||||
prefab = e.prefabId,
|
||||
from_position = AsrsPositionUtil.ToPosition(e.from),
|
||||
to_position = AsrsPositionUtil.ToPosition(e.to)
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// ===== UI Refresh =====
|
||||
|
||||
private void RefreshAll()
|
||||
{
|
||||
RefreshSelectionVisuals();
|
||||
RefreshOccupiedVisuals();
|
||||
RefreshTable();
|
||||
RefreshTableSelection();
|
||||
}
|
||||
|
||||
private void RefreshSelectionVisuals()
|
||||
{
|
||||
foreach (var view in rackViews)
|
||||
{
|
||||
if (previewRects.TryGetValue(view.RackIndex, out var rect))
|
||||
view.SetSelectionVisual(rect);
|
||||
else
|
||||
view.ClearSelectionVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshOccupiedVisuals()
|
||||
{
|
||||
var occupiedSet = occupancy.Keys.Select(k => (k.rack, k.x, k.y)).ToHashSet();
|
||||
foreach (var view in rackViews)
|
||||
view.SetOccupiedCells(occupiedSet);
|
||||
}
|
||||
|
||||
private void RefreshTable()
|
||||
{
|
||||
if (tableContent == null || rowPrefab == null) return;
|
||||
|
||||
foreach (Transform child in tableContent)
|
||||
Destroy(child.gameObject);
|
||||
|
||||
foreach (var e in entries)
|
||||
{
|
||||
var row = Instantiate(rowPrefab, tableContent);
|
||||
|
||||
// InitRow.Bind 시그니처는 사용자님 프로젝트 기준
|
||||
row.Bind(
|
||||
e.id,
|
||||
e.prefabDisplayName,
|
||||
e.count,
|
||||
e.from,
|
||||
e.to,
|
||||
onClickRow: OnClickRow
|
||||
);
|
||||
|
||||
if (selectedEntryId.HasValue && selectedEntryId.Value == e.id)
|
||||
row.SetSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickRow(int entryId)
|
||||
{
|
||||
selectedEntryId = entryId;
|
||||
RefreshTableSelection();
|
||||
}
|
||||
|
||||
private void RefreshTableSelection()
|
||||
{
|
||||
if (tableContent == null) return;
|
||||
|
||||
var rows = tableContent.GetComponentsInChildren<InitRow>(true);
|
||||
foreach (var r in rows)
|
||||
r.SetSelected(selectedEntryId.HasValue && r.EntryId == selectedEntryId.Value);
|
||||
}
|
||||
|
||||
private void DeleteEntryById(int entryId)
|
||||
{
|
||||
// entries에서 엔트리 찾기
|
||||
var entry = entries.FirstOrDefault(e => e.id == entryId);
|
||||
if (entry == null) return;
|
||||
|
||||
// occupancy 해제 (범위 전체)
|
||||
MarkOccupancy(entry.id, entry.from, entry.to, false);
|
||||
|
||||
// entries 제거
|
||||
entries.Remove(entry);
|
||||
|
||||
// 테이블 선택 상태도 정리
|
||||
if (selectedEntryId.HasValue && selectedEntryId.Value == entryId)
|
||||
selectedEntryId = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0394e3a66510fe94f812d23cefc752c1
|
||||
@@ -1,25 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class InitializeCellPointer : MonoBehaviour, IPointerDownHandler, IPointerEnterHandler, IPointerUpHandler
|
||||
{
|
||||
private InitializePopupCellButton cell;
|
||||
private System.Action<InitializePopupCellButton> onDown;
|
||||
private System.Action<InitializePopupCellButton> onEnter;
|
||||
private System.Action onUp;
|
||||
|
||||
public void Bind(InitializePopupCellButton cell,
|
||||
System.Action<InitializePopupCellButton> onDown,
|
||||
System.Action<InitializePopupCellButton> onEnter,
|
||||
System.Action onUp)
|
||||
{
|
||||
this.cell = cell;
|
||||
this.onDown = onDown;
|
||||
this.onEnter = onEnter;
|
||||
this.onUp = onUp;
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData) => onDown?.Invoke(cell);
|
||||
public void OnPointerEnter(PointerEventData eventData) => onEnter?.Invoke(cell);
|
||||
public void OnPointerUp(PointerEventData eventData) => onUp?.Invoke();
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd40ac7562a71324587a41d17a99920c
|
||||
@@ -1,17 +0,0 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
public class InitializeLayerView : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text title;
|
||||
[SerializeField] private Transform gridRoot;
|
||||
|
||||
public int Z { get; private set; }
|
||||
public Transform GridRoot => gridRoot;
|
||||
|
||||
public void SetLayer(int z)
|
||||
{
|
||||
Z = z;
|
||||
if (title) title.text = $"{z}번 랙";
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 044e2d8fec641e447b6cb5b8c371c591
|
||||
@@ -1,44 +0,0 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InitializePopupCellButton : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Button button;
|
||||
[SerializeField] private Image background;
|
||||
[SerializeField] private TMP_Text text; // 필요 없으면 제거
|
||||
|
||||
public int X { get; private set; }
|
||||
public int Y { get; private set; }
|
||||
public int Z { get; private set; }
|
||||
|
||||
public void Setup(int x, int y, int z, System.Action<InitializePopupCellButton> onDown, System.Action<InitializePopupCellButton> onEnter, System.Action onUp)
|
||||
{
|
||||
X = x; Y = y; Z = z;
|
||||
|
||||
// 버튼 이벤트(단순화): Pointer 이벤트가 더 정확하지만, 여기서는 구성 예시로 단순화
|
||||
button.onClick.RemoveAllListeners();
|
||||
button.onClick.AddListener(() => onDown?.Invoke(this)); // 클릭 시작으로 취급
|
||||
|
||||
// 드래그/호버는 EventTrigger 또는 IPointerEnterHandler로 확장 권장
|
||||
// 여기서는 창 코드에서 추가로 연결할 수 있게 메서드를 남겨둡니다.
|
||||
}
|
||||
|
||||
public void SetEmpty()
|
||||
{
|
||||
// background.color = ...
|
||||
if (text) text.text = "";
|
||||
}
|
||||
|
||||
public void SetOccupied(string prefab)
|
||||
{
|
||||
// background.color = ...
|
||||
if (text) text.text = "■"; // 아이콘/이미지로 바꾸셔도 됨
|
||||
}
|
||||
|
||||
public void SetHighlight(bool on)
|
||||
{
|
||||
// 하이라이트 색/아웃라인 처리
|
||||
// 예: background.color = on ? highlightColor : normalColor;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f30a17680ef568a46a5904393cc225b3
|
||||
@@ -1,344 +0,0 @@
|
||||
using Simulator.Data;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InitializePopupWindow : MonoBehaviour
|
||||
{
|
||||
[Header("Prefabs / Roots")]
|
||||
[SerializeField] private InitializeLayerView layerPrefab;
|
||||
[SerializeField] private InitializePopupCellButton cellPrefab;
|
||||
[SerializeField] private Transform layersParent;
|
||||
|
||||
[Header("Right Panel Controls")]
|
||||
[SerializeField] private TMP_Dropdown prefabDropdown;
|
||||
[SerializeField] private TMP_InputField countInput; // 숫자 입력
|
||||
[SerializeField] private Button applyButton;
|
||||
[SerializeField] private Button deleteButton;
|
||||
|
||||
[Header("Z Layer Toggles (max 2 selected)")]
|
||||
[SerializeField] private Toggle z1Toggle;
|
||||
[SerializeField] private Toggle z2Toggle;
|
||||
// z가 더 늘 수 있으면 Toggle 리스트로 확장 권장(지금은 최대 2층 요구에 맞춰 간단히)
|
||||
|
||||
[Header("Table")]
|
||||
[SerializeField] private Transform tableContent; // row parent
|
||||
[SerializeField] private InitializeRow rowPrefab;
|
||||
|
||||
private ASRSDataClass asrs;
|
||||
|
||||
// grid lookup
|
||||
private readonly Dictionary<(int x, int y, int z), InitializePopupCellButton> cellMap = new();
|
||||
|
||||
// occupied lookup: each cell -> entry
|
||||
private readonly Dictionary<(int x, int y, int z), InitializeEntry> occupied = new();
|
||||
|
||||
// selection
|
||||
private bool dragging;
|
||||
private (int x, int y) dragStart;
|
||||
private (int x, int y) dragEnd;
|
||||
|
||||
// highlight diff (성능)
|
||||
private readonly HashSet<(int x, int y, int z)> lastHighlighted = new();
|
||||
private readonly HashSet<(int x, int y, int z)> newHighlighted = new();
|
||||
|
||||
// table selection
|
||||
private InitializeEntry selectedRowEntry;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
applyButton.onClick.AddListener(ApplySelection);
|
||||
deleteButton.onClick.AddListener(DeleteSelectedRow);
|
||||
|
||||
z1Toggle.onValueChanged.AddListener(_ => OnZToggleChanged());
|
||||
z2Toggle.onValueChanged.AddListener(_ => OnZToggleChanged());
|
||||
}
|
||||
|
||||
public void Open(ASRSDataClass asrsData, int sizeX, int sizeY, int sizeZ)
|
||||
{
|
||||
asrs = asrsData;
|
||||
asrs.initialize ??= new List<InitializeEntry>();
|
||||
|
||||
BuildPrefabDropdown();
|
||||
BuildLayerGrids(sizeX, sizeY, sizeZ);
|
||||
|
||||
RebuildOccupiedIndex();
|
||||
RefreshAllCellsFromOccupied();
|
||||
RefreshTableFromInitialize();
|
||||
|
||||
// 초기 상태: 1번 랙만 체크
|
||||
z1Toggle.isOn = true;
|
||||
z2Toggle.isOn = false;
|
||||
OnZToggleChanged();
|
||||
|
||||
dragging = false;
|
||||
deleteButton.interactable = false;
|
||||
}
|
||||
|
||||
private void BuildPrefabDropdown()
|
||||
{
|
||||
prefabDropdown.ClearOptions();
|
||||
var options = new List<string>(PrefabManager.Instance.prefabDict.Keys);
|
||||
prefabDropdown.AddOptions(options);
|
||||
}
|
||||
|
||||
private void BuildLayerGrids(int sizeX, int sizeY, int sizeZ)
|
||||
{
|
||||
// 기존 UI 삭제
|
||||
foreach (Transform child in layersParent) Destroy(child.gameObject);
|
||||
cellMap.Clear();
|
||||
Debug.Log("asdf");
|
||||
|
||||
for (int z = 1; z <= sizeZ; z++)
|
||||
{
|
||||
var layer = Instantiate(layerPrefab, layersParent);
|
||||
layer.SetLayer(z);
|
||||
|
||||
// y는 화면 상단이 큰 값(이미지처럼)
|
||||
for (int y = sizeY; y >= 1; y--)
|
||||
for (int x = 1; x <= sizeX; x++)
|
||||
{
|
||||
var cell = Instantiate(cellPrefab, layer.GridRoot);
|
||||
|
||||
// Pointer 이벤트 기반 드래그 지원용 컴포넌트 부착
|
||||
var trigger = cell.gameObject.GetComponent<InitializeCellPointer>();
|
||||
if (trigger == null) trigger = cell.gameObject.AddComponent<InitializeCellPointer>();
|
||||
|
||||
trigger.Bind(
|
||||
cell,
|
||||
onDown: OnCellPointerDown,
|
||||
onEnter: OnCellPointerEnter,
|
||||
onUp: OnCellPointerUp
|
||||
);
|
||||
|
||||
cellMap[(x, y, z)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Pointer / Drag Handlers
|
||||
private void OnCellPointerDown(InitializePopupCellButton cell)
|
||||
{
|
||||
dragging = true;
|
||||
dragStart = (cell.X, cell.Y);
|
||||
dragEnd = (cell.X, cell.Y);
|
||||
UpdateHighlights();
|
||||
}
|
||||
|
||||
private void OnCellPointerEnter(InitializePopupCellButton cell)
|
||||
{
|
||||
if (!dragging) return;
|
||||
dragEnd = (cell.X, cell.Y);
|
||||
UpdateHighlights();
|
||||
}
|
||||
|
||||
private void OnCellPointerUp()
|
||||
{
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
// 드래그 확정 상태 유지(하이라이트 유지)
|
||||
UpdateHighlights();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void OnZToggleChanged()
|
||||
{
|
||||
// 최대 2개만 허용(현재 토글이 2개라 사실상 자동 충족)
|
||||
// z가 늘면: 체크 수가 2 초과 시 방금 체크한 토글을 되돌리는 식으로 처리
|
||||
|
||||
UpdateHighlights(); // z 선택 바뀌면 하이라이트도 즉시 반영
|
||||
}
|
||||
|
||||
private List<int> GetSelectedZLayers()
|
||||
{
|
||||
var list = new List<int>(2);
|
||||
if (z1Toggle.isOn) list.Add(1);
|
||||
if (z2Toggle.isOn) list.Add(2);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void UpdateHighlights()
|
||||
{
|
||||
// 선택된 XY rect 계산
|
||||
int minX = Mathf.Min(dragStart.x, dragEnd.x);
|
||||
int maxX = Mathf.Max(dragStart.x, dragEnd.x);
|
||||
int minY = Mathf.Min(dragStart.y, dragEnd.y);
|
||||
int maxY = Mathf.Max(dragStart.y, dragEnd.y);
|
||||
|
||||
var zs = GetSelectedZLayers();
|
||||
newHighlighted.Clear();
|
||||
|
||||
// 새 하이라이트 집합 구성
|
||||
foreach (int z in zs)
|
||||
{
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
newHighlighted.Add((x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
// diff 적용(전체 리셋 방지)
|
||||
foreach (var key in lastHighlighted)
|
||||
{
|
||||
if (!newHighlighted.Contains(key) && cellMap.TryGetValue(key, out var cell))
|
||||
cell.SetHighlight(false);
|
||||
}
|
||||
|
||||
foreach (var key in newHighlighted)
|
||||
{
|
||||
if (!lastHighlighted.Contains(key) && cellMap.TryGetValue(key, out var cell))
|
||||
cell.SetHighlight(true);
|
||||
}
|
||||
|
||||
lastHighlighted.Clear();
|
||||
foreach (var k in newHighlighted) lastHighlighted.Add(k);
|
||||
}
|
||||
|
||||
#region Apply / Delete
|
||||
private void ApplySelection()
|
||||
{
|
||||
var zs = GetSelectedZLayers();
|
||||
if (zs.Count == 0) return;
|
||||
|
||||
// prefab
|
||||
string prefab = prefabDropdown.options[prefabDropdown.value].text;
|
||||
|
||||
// count
|
||||
int count = 1;
|
||||
if (!int.TryParse(countInput.text, out count)) count = 1;
|
||||
count = Mathf.Max(1, count);
|
||||
|
||||
// XY rect
|
||||
int minX = Mathf.Min(dragStart.x, dragEnd.x);
|
||||
int maxX = Mathf.Max(dragStart.x, dragEnd.x);
|
||||
int minY = Mathf.Min(dragStart.y, dragEnd.y);
|
||||
int maxY = Mathf.Max(dragStart.y, dragEnd.y);
|
||||
|
||||
// 1) 중복 검사(선택된 z 각각)
|
||||
foreach (int z in zs)
|
||||
{
|
||||
if (HasOverlap(minX, minY, maxX, maxY, z))
|
||||
{
|
||||
// 여기서 경고 UI 띄우시면 됩니다(Toast/Popup)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) initialize 추가
|
||||
// z가 2개이고 연속(1,2)인 경우: 하나의 entry로 z 범위를 담는 것도 가능
|
||||
// 단, 삭제/표시가 더 쉬운 구조는 “z마다 entry 하나씩”입니다(권장).
|
||||
foreach (int z in zs)
|
||||
{
|
||||
var entry = new InitializeEntry
|
||||
{
|
||||
prefab = prefab,
|
||||
count = count,
|
||||
from_position = new Position { x = minX, y = maxY, z = z }, // y축 방향은 프로젝트 좌표계에 맞춰 조정
|
||||
to_position = new Position { x = maxX, y = minY, z = z },
|
||||
};
|
||||
|
||||
asrs.initialize.Add(entry);
|
||||
}
|
||||
|
||||
// 3) 점유 인덱스/그리드/테이블 갱신
|
||||
RebuildOccupiedIndex();
|
||||
RefreshAllCellsFromOccupied();
|
||||
RefreshTableFromInitialize();
|
||||
|
||||
// 4) 모델 반영(패치 저장 필요 시 여기서)
|
||||
// SaveChange(asrs, asrs.initialize, "initialize");
|
||||
}
|
||||
|
||||
private bool HasOverlap(int minX, int minY, int maxX, int maxY, int z)
|
||||
{
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
if (occupied.ContainsKey((x, y, z)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DeleteSelectedRow()
|
||||
{
|
||||
if (selectedRowEntry == null) return;
|
||||
|
||||
asrs.initialize.Remove(selectedRowEntry);
|
||||
selectedRowEntry = null;
|
||||
deleteButton.interactable = false;
|
||||
|
||||
RebuildOccupiedIndex();
|
||||
RefreshAllCellsFromOccupied();
|
||||
RefreshTableFromInitialize();
|
||||
|
||||
// SaveChange(asrs, asrs.initialize, "initialize");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Occupied / Refresh
|
||||
private void RebuildOccupiedIndex()
|
||||
{
|
||||
occupied.Clear();
|
||||
if (asrs.initialize == null) return;
|
||||
|
||||
foreach (var e in asrs.initialize)
|
||||
{
|
||||
if (e?.from_position == null || e?.to_position == null) continue;
|
||||
|
||||
// normalize
|
||||
int minX = (int)Mathf.Min(e.from_position.x, e.to_position.x);
|
||||
int maxX = (int)Mathf.Max(e.from_position.x, e.to_position.x);
|
||||
int minY = (int)Mathf.Min(e.from_position.y, e.to_position.y);
|
||||
int maxY = (int)Mathf.Max(e.from_position.y, e.to_position.y);
|
||||
int minZ = (int)Mathf.Min(e.from_position.z, e.to_position.z);
|
||||
int maxZ = (int)Mathf.Max(e.from_position.z, e.to_position.z);
|
||||
|
||||
for (int z = minZ; z <= maxZ; z++)
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
occupied[(x, y, z)] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshAllCellsFromOccupied()
|
||||
{
|
||||
foreach (var kv in cellMap)
|
||||
{
|
||||
var key = kv.Key;
|
||||
var cell = kv.Value;
|
||||
|
||||
if (occupied.TryGetValue(key, out var entry))
|
||||
cell.SetOccupied(entry.prefab);
|
||||
else
|
||||
cell.SetEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshTableFromInitialize()
|
||||
{
|
||||
foreach (Transform child in tableContent) Destroy(child.gameObject);
|
||||
|
||||
if (asrs.initialize == null || asrs.initialize.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var e in asrs.initialize)
|
||||
{
|
||||
var row = Instantiate(rowPrefab, tableContent);
|
||||
row.Bind(e, onSelect: OnRowSelected);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRowSelected(InitializeEntry entry, bool selected)
|
||||
{
|
||||
selectedRowEntry = selected ? entry : null;
|
||||
deleteButton.interactable = selectedRowEntry != null;
|
||||
|
||||
// 선택된 row의 범위를 하이라이트로 보여주고 싶으면 여기서 lastHighlighted를 해당 entry 범위로 재구성하면 됩니다.
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d555b3ddff999f74796a726059e53e51
|
||||
@@ -1,30 +0,0 @@
|
||||
using Simulator.Data;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InitializeRow : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Toggle selectToggle;
|
||||
[SerializeField] private TMP_Text prefabText;
|
||||
[SerializeField] private TMP_Text countText;
|
||||
[SerializeField] private TMP_Text fromText;
|
||||
[SerializeField] private TMP_Text toText;
|
||||
|
||||
private InitializeEntry entry;
|
||||
private System.Action<InitializeEntry, bool> onSelect;
|
||||
|
||||
public void Bind(InitializeEntry e, System.Action<InitializeEntry, bool> onSelect)
|
||||
{
|
||||
entry = e;
|
||||
this.onSelect = onSelect;
|
||||
|
||||
prefabText.text = e.prefab;
|
||||
countText.text = e.count.ToString();
|
||||
fromText.text = $"{e.from_position.x}, {e.from_position.y}, {e.from_position.z}";
|
||||
toText.text = $"{e.to_position.x}, {e.to_position.y}, {e.to_position.z}";
|
||||
|
||||
selectToggle.onValueChanged.RemoveAllListeners();
|
||||
selectToggle.onValueChanged.AddListener(v => this.onSelect?.Invoke(entry, v));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b976ee14e46fcfc4c9a120e720899aff
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Simulator.Data;
|
||||
|
||||
public static class PrefabCatalogFromManager
|
||||
{
|
||||
public readonly struct Item
|
||||
{
|
||||
public readonly string Id; // 저장 키 = PrefabData.name
|
||||
public readonly PrefabData Data;
|
||||
|
||||
public Item(string id, PrefabData data)
|
||||
{
|
||||
Id = id;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public string DisplayName =>
|
||||
!string.IsNullOrWhiteSpace(Data?.label) ? Data.label :
|
||||
!string.IsNullOrWhiteSpace(Data?.name) ? Data.name :
|
||||
Id;
|
||||
}
|
||||
|
||||
public static List<Item> GetSortedItems()
|
||||
{
|
||||
var dict = PrefabManager.Instance != null ? PrefabManager.Instance.prefabDict : null;
|
||||
if (dict == null || dict.Count == 0) return new List<Item>();
|
||||
|
||||
return dict
|
||||
.Select(kv => new Item(kv.Key, kv.Value))
|
||||
.OrderBy(i => i.Data != null ? i.Data.priority_order : int.MaxValue)
|
||||
.ThenBy(i => i.DisplayName)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static bool TryGet(string prefabId, out Item item)
|
||||
{
|
||||
item = default;
|
||||
|
||||
if (PrefabManager.Instance == null) return false;
|
||||
if (PrefabManager.Instance.prefabDict == null) return false;
|
||||
|
||||
if (!PrefabManager.Instance.prefabDict.TryGetValue(prefabId, out var data))
|
||||
return false;
|
||||
|
||||
item = new Item(prefabId, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string ResolveDisplayName(string prefabId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(prefabId))
|
||||
return string.Empty;
|
||||
|
||||
if (TryGet(prefabId, out var item))
|
||||
return item.DisplayName;
|
||||
|
||||
// 카탈로그에 없으면 그대로 표시
|
||||
return prefabId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f093db8c0cd3547bd994a340f3d847
|
||||
Reference in New Issue
Block a user