build enviroment
This commit is contained in:
@@ -10,7 +10,7 @@ IPointerEnterHandler, IPointerClickHandler
|
||||
[SerializeField] private Image background;
|
||||
[SerializeField] private TMP_Text label;
|
||||
[SerializeField] private GameObject plusIcon; // 미리보기 선택
|
||||
[SerializeField] private GameObject setIcon; // 확정 설정
|
||||
[SerializeField] private Image setIcon; // 확정 설정
|
||||
|
||||
public int RackIndex { get; private set; } // 1..z
|
||||
public int X { get; private set; } // 1..layout.x
|
||||
@@ -29,7 +29,7 @@ IPointerEnterHandler, IPointerClickHandler
|
||||
label.text = "1";
|
||||
|
||||
SetSelecting(false);
|
||||
SetOccupied(false);
|
||||
SetOccupiedIcon(null);
|
||||
}
|
||||
|
||||
public void SetSelecting(bool on)
|
||||
@@ -45,9 +45,20 @@ IPointerEnterHandler, IPointerClickHandler
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOccupied(bool on)
|
||||
public void SetOccupiedIcon(Sprite sprite)
|
||||
{
|
||||
if (setIcon != null) setIcon.SetActive(on);
|
||||
if (setIcon == null) return;
|
||||
|
||||
if (sprite == null)
|
||||
{
|
||||
setIcon.sprite = null;
|
||||
setIcon.enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
setIcon.sprite = sprite;
|
||||
setIcon.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
|
||||
@@ -49,12 +49,17 @@ public class GridView : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOccupiedCells(HashSet<(int rack, int x, int y)> occupied)
|
||||
public void SetOccupiedCells(Dictionary<(int rack, int x, int y), Sprite> occupiedSprites)
|
||||
{
|
||||
foreach (var kv in cells)
|
||||
{
|
||||
var (x, y) = kv.Key;
|
||||
kv.Value.SetOccupied(occupied.Contains((RackIndex, x, y)));
|
||||
var cell = kv.Value;
|
||||
|
||||
if (occupiedSprites != null && occupiedSprites.TryGetValue((RackIndex, x, y), out var sprite))
|
||||
cell.SetOccupiedIcon(sprite);
|
||||
else
|
||||
cell.SetOccupiedIcon(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using static UnityEngine.Rendering.DebugUI.Table;
|
||||
|
||||
public class InitialInventoryWindow : MonoBehaviour
|
||||
{
|
||||
[Header("Top/Left")]
|
||||
@@ -27,11 +25,33 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
[SerializeField] private InitRow rowPrefab;
|
||||
|
||||
[Header("Bottom Buttons")]
|
||||
[SerializeField] private Button btnApply; // Apply 버튼 (요구사항 추가)
|
||||
[SerializeField] private Button btnCancel; // 선택: 닫기
|
||||
[SerializeField] private Button btnReset; // 선택: 초기화
|
||||
[SerializeField] private Button btnApply;
|
||||
[SerializeField] private Button btnCancel;
|
||||
[SerializeField] private Button btnReset;
|
||||
|
||||
private ASRSDataClass asrs;
|
||||
// ===== 대상(ASRS/Rack) 공통 핸들 =====
|
||||
private Func<List<InitializeEntry>> initGetter;
|
||||
private Action<List<InitializeEntry>> initSetter;
|
||||
|
||||
// 필요 시 외부에서 "지금 어느 타입을 열었나"를 추적하고 싶다면 유지
|
||||
private ASRSDataClass asrs; // 마지막으로 연 ASRS (선택)
|
||||
private RackDataClass rack; // 마지막으로 연 Rack (선택) - 실제 클래스명에 맞게 변경
|
||||
|
||||
[Serializable]
|
||||
private struct LayoutInfo
|
||||
{
|
||||
public int x, y, z;
|
||||
public float x_length, y_length, z_length;
|
||||
}
|
||||
|
||||
private enum DimLabelMode
|
||||
{
|
||||
Asrs, // 기존 ASRS 표기(스크린샷 기준 y/z 매핑)
|
||||
Rack // 일반 표기(x,y,z 그대로)
|
||||
}
|
||||
|
||||
private LayoutInfo currentLayout;
|
||||
private DimLabelMode dimLabelMode = DimLabelMode.Asrs;
|
||||
|
||||
// rack views
|
||||
private readonly List<GridView> rackViews = new();
|
||||
@@ -59,24 +79,114 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
|
||||
// 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)
|
||||
// =====================================================================
|
||||
// Public API
|
||||
// =====================================================================
|
||||
|
||||
// 기존 호출부 호환: Open(ASRSDataClass) 유지 (원하시면 제거 가능)
|
||||
public void Open(ASRSDataClass asrs) => OpenAsrs(asrs);
|
||||
|
||||
void SaveChange(object source, object value, string name)
|
||||
{
|
||||
var path = PathIndexer.GetNodePath(source);
|
||||
Patch updateData = new Patch();
|
||||
updateData.value = value;
|
||||
UpdateValueStack.AddPatch($"{path}.{name}", value);
|
||||
}
|
||||
|
||||
public void OpenAsrs(ASRSDataClass asrs)
|
||||
{
|
||||
if (asrs == null) return;
|
||||
|
||||
this.asrs = asrs;
|
||||
this.rack = null;
|
||||
|
||||
var l = asrs.asrs_layout;
|
||||
var layout = new LayoutInfo
|
||||
{
|
||||
x = l.x,
|
||||
y = l.y,
|
||||
z = Mathf.Clamp(l.z, 1, 2),
|
||||
x_length = l.x_length,
|
||||
y_length = l.y_length,
|
||||
z_length = l.z_length
|
||||
};
|
||||
|
||||
OpenInternal(
|
||||
layout,
|
||||
getter: () => asrs.initialize,
|
||||
setter: list => asrs.initialize = list,
|
||||
mode: DimLabelMode.Asrs
|
||||
);
|
||||
}
|
||||
|
||||
public void OpenRack(RackDataClass rack,string path) // 실제 클래스명에 맞게 변경
|
||||
{
|
||||
if (rack == null) return;
|
||||
|
||||
this.rack = rack;
|
||||
this.asrs = null;
|
||||
|
||||
var l = rack.rack_layout; // 실제 필드명에 맞게 변경
|
||||
var layout = new LayoutInfo
|
||||
{
|
||||
x = l.x,
|
||||
y = l.y,
|
||||
z = Mathf.Clamp(l.z, 1, 2),
|
||||
x_length = l.x_length,
|
||||
y_length = l.y_length,
|
||||
z_length = l.z_length
|
||||
};
|
||||
|
||||
// initialize가 set 가능하면 아래 그대로
|
||||
OpenInternal(
|
||||
layout,
|
||||
getter: () => rack.initialize,
|
||||
setter: list => { rack.initialize = list; SaveChange(rack,list, "initialize"); },
|
||||
mode: DimLabelMode.Rack
|
||||
);
|
||||
|
||||
// 만약 rack.initialize가 set 불가(읽기전용 리스트)라면, 위 setter 대신 아래로 바꾸세요.
|
||||
/*
|
||||
OpenInternal(
|
||||
layout,
|
||||
getter: () => rack.initialize,
|
||||
setter: list =>
|
||||
{
|
||||
rack.initialize.Clear();
|
||||
rack.initialize.AddRange(list);
|
||||
},
|
||||
mode: DimLabelMode.Rack
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
private void OpenInternal(
|
||||
LayoutInfo layout,
|
||||
Func<List<InitializeEntry>> getter,
|
||||
Action<List<InitializeEntry>> setter,
|
||||
DimLabelMode mode
|
||||
)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
this.asrs = asrs;
|
||||
|
||||
BuildDimLabel();
|
||||
currentLayout = layout;
|
||||
initGetter = getter;
|
||||
initSetter = setter;
|
||||
dimLabelMode = mode;
|
||||
|
||||
// UI Build
|
||||
BuildDimLabel(layout, mode);
|
||||
BuildPrefabDropdown();
|
||||
BuildRacks();
|
||||
BuildRacks(layout);
|
||||
|
||||
// 기존 initialize 로딩
|
||||
LoadInitialize(asrs.initialize);
|
||||
LoadInitialize(getter?.Invoke());
|
||||
|
||||
HookButtons();
|
||||
RefreshAll();
|
||||
@@ -99,18 +209,35 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Build UI =====
|
||||
private void BuildDimLabel()
|
||||
// =====================================================================
|
||||
// Build UI
|
||||
// =====================================================================
|
||||
|
||||
private void BuildDimLabel(LayoutInfo l, DimLabelMode mode)
|
||||
{
|
||||
var l = asrs.asrs_layout;
|
||||
float xM, yM, zM;
|
||||
|
||||
float xM = l.x * l.x_length;
|
||||
float yM = l.z * l.z_length;
|
||||
float zM = l.y * l.y_length;
|
||||
if (mode == DimLabelMode.Asrs)
|
||||
{
|
||||
// 기존 ASRS 표기 유지:
|
||||
// x => x*x_length
|
||||
// y => z*z_length
|
||||
// z => y*y_length
|
||||
xM = l.x * l.x_length;
|
||||
yM = l.z * l.z_length;
|
||||
zM = l.y * l.y_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rack은 일반 표기(원하시면 Asrs처럼 바꿔도 됩니다)
|
||||
xM = l.x * l.x_length;
|
||||
yM = l.y * l.y_length;
|
||||
zM = l.z * l.z_length;
|
||||
}
|
||||
|
||||
xLabel.text = $"{xM:0.##} m (x)";
|
||||
yLabel.text = $"{yM:0.##} m (y)";
|
||||
zLabel.text = $"{zM:0.##} m (z)";
|
||||
if (xLabel != null) xLabel.text = $"{xM:0.##} m (x)";
|
||||
if (yLabel != null) yLabel.text = $"{yM:0.##} m (y)";
|
||||
if (zLabel != null) zLabel.text = $"{zM:0.##} m (z)";
|
||||
}
|
||||
|
||||
private void BuildPrefabDropdown()
|
||||
@@ -128,19 +255,18 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
SetCount(count);
|
||||
}
|
||||
|
||||
private void BuildRacks()
|
||||
private void BuildRacks(LayoutInfo l)
|
||||
{
|
||||
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++)
|
||||
for (int rackIndex = 1; rackIndex <= rackCount; rackIndex++)
|
||||
{
|
||||
var view = Instantiate(rackSectionPrefab, rackContainer);
|
||||
view.Build(this, rack, l.x, l.y);
|
||||
view.Build(this, rackIndex, l.x, l.y);
|
||||
rackViews.Add(view);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +276,7 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
if (btnApply != null)
|
||||
{
|
||||
btnApply.onClick.RemoveAllListeners();
|
||||
btnApply.onClick.AddListener(ApplyToAsrsInitialize);
|
||||
btnApply.onClick.AddListener(ApplyToTargetInitialize);
|
||||
}
|
||||
|
||||
if (btnCancel != null)
|
||||
@@ -173,6 +299,7 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
RefreshAll();
|
||||
});
|
||||
}
|
||||
|
||||
if (toggleSelectAll != null)
|
||||
{
|
||||
toggleSelectAll.onValueChanged.RemoveAllListeners();
|
||||
@@ -203,6 +330,10 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
private string ResolvePrefabDisplayName(string prefabId)
|
||||
=> PrefabCatalogFromManager.ResolveDisplayName(prefabId);
|
||||
|
||||
// =====================================================================
|
||||
// Table: select all / delete selected
|
||||
// =====================================================================
|
||||
|
||||
private void OnToggleSelectAllChanged(bool on)
|
||||
{
|
||||
var rows = tableContent.GetComponentsInChildren<InitRow>(true);
|
||||
@@ -218,7 +349,6 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
var ids = rows.Where(r => r.IsSelected).Select(r => r.EntryId).ToHashSet();
|
||||
if (ids.Count == 0) return;
|
||||
|
||||
// entries에서 id에 해당하는 것들을 삭제
|
||||
for (int i = entries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!ids.Contains(entries[i].id)) continue;
|
||||
@@ -228,9 +358,8 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
entries.RemoveAt(i);
|
||||
}
|
||||
|
||||
// 선택 상태/토글 상태 정리
|
||||
selectedEntryId = null;
|
||||
if (toggleSelectAll != null) toggleSelectAll.isOn = false;
|
||||
if (toggleSelectAll != null) toggleSelectAll.SetIsOnWithoutNotify(false);
|
||||
|
||||
RefreshAll();
|
||||
}
|
||||
@@ -244,7 +373,6 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
bool anySelected = rows.Any(r => r.IsSelected);
|
||||
btnDeleteSelected.interactable = anySelected;
|
||||
|
||||
// ✅ 추가: 전체 선택 토글 상태 동기화
|
||||
if (toggleSelectAll != null)
|
||||
{
|
||||
bool allOff = rows.Length > 0 && rows.All(r => !r.IsSelected);
|
||||
@@ -252,12 +380,13 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
|
||||
if (allOff) toggleSelectAll.SetIsOnWithoutNotify(false);
|
||||
else if (allOn) toggleSelectAll.SetIsOnWithoutNotify(true);
|
||||
// 일부만 선택된 경우는 toggleSelectAll 상태를 유지 (원하시면 false로 내리도록 변경 가능)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ===== initialize load (기존 데이터 반영) =====
|
||||
// =====================================================================
|
||||
// initialize load
|
||||
// =====================================================================
|
||||
|
||||
private void LoadInitialize(List<InitializeEntry> initList)
|
||||
{
|
||||
entries.Clear();
|
||||
@@ -296,9 +425,9 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// =====================================================================
|
||||
// GridView -> Window 콜백 (Click + Hover)
|
||||
// ============================================================
|
||||
// =====================================================================
|
||||
|
||||
public void OnCellHover(int rackIndex, int x, int y)
|
||||
{
|
||||
@@ -313,13 +442,11 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
{
|
||||
if (!IsSelectionMode) return;
|
||||
|
||||
// ✅ (추가) 앵커를 아직 안 잡은 상태에서, 이미 설정된 셀을 클릭하면 "그 영역(Entry) 삭제"
|
||||
// 사용 예: 1,3,1~4,4,1로 설정된 상태에서 3,4,1 클릭 -> 해당 Entry 전체 삭제
|
||||
// 앵커를 아직 안 잡은 상태에서, 이미 설정된 셀 클릭 시 -> 그 Entry 전체 삭제
|
||||
if (!anchors.ContainsKey(rackIndex) && occupancy.TryGetValue((rackIndex, x, y), out int entryId))
|
||||
{
|
||||
DeleteEntryById(entryId);
|
||||
|
||||
// 삭제 후에는 선택 관련 미리보기/앵커도 정리(안전)
|
||||
anchors.Remove(rackIndex);
|
||||
previewRects.Remove(rackIndex);
|
||||
|
||||
@@ -351,7 +478,6 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
// 실패 시: 미리보기 유지(앵커 유지)
|
||||
previewRects[rackIndex] = rect;
|
||||
RefreshSelectionVisuals();
|
||||
}
|
||||
@@ -428,23 +554,20 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Apply: 현재 변경사항을 asrs.initialize에 반영 =====
|
||||
// =====================================================================
|
||||
// Apply: 현재 변경사항을 "현재 대상" initialize에 반영
|
||||
// =====================================================================
|
||||
|
||||
private void ApplyToAsrsInitialize()
|
||||
private void ApplyToTargetInitialize()
|
||||
{
|
||||
if (asrs == null) return;
|
||||
if (initSetter == null) return;
|
||||
|
||||
// 현재 엔트리 -> InitializeEntry 리스트로 변환
|
||||
asrs.initialize = BuildInitializeEntries();
|
||||
|
||||
// 필요하면 여기서 닫거나, “저장됨” 토스트 등을 띄우면 됩니다.
|
||||
// Close();
|
||||
initSetter.Invoke(BuildInitializeEntries());
|
||||
Close();
|
||||
}
|
||||
|
||||
private List<InitializeEntry> BuildInitializeEntries()
|
||||
{
|
||||
// 중요: InitializeEntry의 from/to는 Position 타입이므로 변환 필요
|
||||
// 사용자님 기존 코드 방식 그대로 사용
|
||||
return entries.Select(e => new InitializeEntry
|
||||
{
|
||||
count = e.count,
|
||||
@@ -454,7 +577,9 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// ===== UI Refresh =====
|
||||
// =====================================================================
|
||||
// UI Refresh
|
||||
// =====================================================================
|
||||
|
||||
private void RefreshAll()
|
||||
{
|
||||
@@ -478,9 +603,30 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
|
||||
private void RefreshOccupiedVisuals()
|
||||
{
|
||||
var occupiedSet = occupancy.Keys.Select(k => (k.rack, k.x, k.y)).ToHashSet();
|
||||
// (rack,x,y) -> sprite
|
||||
var map = new Dictionary<(int rack, int x, int y), Sprite>();
|
||||
|
||||
foreach (var kv in occupancy)
|
||||
{
|
||||
var key = kv.Key; // (rack,x,y)
|
||||
int entryId = kv.Value;
|
||||
|
||||
var entry = entries.FirstOrDefault(e => e.id == entryId);
|
||||
if (entry == null) continue;
|
||||
|
||||
// prefabId -> shape
|
||||
string shape = PrefabManager.Instance.GetPrefab(entry.prefabId).shape;
|
||||
|
||||
// shape -> sprite (SingletonScene 기반 Catalog 사용)
|
||||
Sprite sprite = ShapeIconCatalog.Instance != null
|
||||
? ShapeIconCatalog.Instance.GetIcon(shape)
|
||||
: null;
|
||||
|
||||
map[key] = sprite;
|
||||
}
|
||||
|
||||
foreach (var view in rackViews)
|
||||
view.SetOccupiedCells(occupiedSet);
|
||||
view.SetOccupiedCells(map);
|
||||
}
|
||||
|
||||
private void RefreshTable()
|
||||
@@ -494,7 +640,7 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
{
|
||||
var row = Instantiate(rowPrefab, tableContent);
|
||||
|
||||
// InitRow.Bind 시그니처는 사용자님 프로젝트 기준
|
||||
// InitRow가 onSelectionChanged를 받는 버전이어야 합니다.
|
||||
row.Bind(
|
||||
e.id,
|
||||
e.prefabDisplayName,
|
||||
@@ -527,17 +673,12 @@ public class InitialInventoryWindow : MonoBehaviour
|
||||
|
||||
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,36 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
public class ShapeIconCatalog : SingletonScene<ShapeIconCatalog>
|
||||
{
|
||||
Dictionary<string,Sprite> iconMap = new Dictionary<string,Sprite>();
|
||||
private readonly Dictionary<string, string> iconPaths = new Dictionary<string, string>()
|
||||
{
|
||||
{"box","Images/GridCellIcon/si_icon_box"},
|
||||
{"flexible","Images/GridCellIcon/si_icon_flexible"},
|
||||
{"sheet","Images/GridCellIcon/si_icon_flexible"},
|
||||
{"barrel","Images/GridCellIcon/si_icon_barrel"},
|
||||
{"bottle","Images/GridCellIcon/si_icon_bottle"},
|
||||
{"coil","Images/GridCellIcon/si_icon_coil"},
|
||||
{"powder","Images/GridCellIcon/is_icon_powder"},
|
||||
{"hazard","Images/GridCellIcon/si_icon_hazard"}
|
||||
};
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
foreach (var path in iconPaths)
|
||||
{
|
||||
iconMap.Add(path.Key,Resources.Load<Sprite>(path.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite GetIcon(string key)
|
||||
{
|
||||
if (iconMap.ContainsKey(key))
|
||||
{
|
||||
return iconMap[key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b221654387751dc4784a90553648487a
|
||||
Reference in New Issue
Block a user