Merge pull request 'dd' (#40) from dd into main

Reviewed-on: http://220.90.135.190:3000/UVCXR/OCTOPUS_TWIN-Demo/pulls/40
This commit was merged in pull request #40.
This commit is contained in:
2026-02-10 17:04:47 +09:00
28 changed files with 4557 additions and 657 deletions

View File

@@ -2,35 +2,68 @@ using UnityEngine;
namespace OCTOPUS_TWIN
{
/// <summary>
/// 카메라 경유점. CameraRoute의 자식 오브젝트에 추가하여 궤도 파라미터를 설정.
/// 없으면 자식 Transform의 position만 사용하고 궤도 파라미터는 현재 카메라 상태 유지.
/// </summary>
public class CameraRoutePoint : MonoBehaviour
{
public float elevation = 45f;
public float azimuth = 0f;
public float distance = 30f;
[Tooltip("이 지점까지의 이동 시간(초)")]
public float duration = 0.5f;
[Tooltip("이 지점에서의 대기 시간(초)")]
public float waitTime = 0f;
}
/// <summary>
/// 카메라 이동 경로. 자식 Transform 순서대로 카메라가 보간 이동.
/// 각 자식에 CameraRoutePoint를 추가하면 궤도 파라미터(elevation, azimuth, distance)도 제어 가능.
/// </summary>
public class CameraRoute : MonoBehaviour
{
public bool loop;
[SerializeField] private int gizmoResolution = 20;
[SerializeField] private Color gizmoColor = Color.cyan;
public int PointCount => transform.childCount;
public MovePoint[] movePoints;
public (Vector3 position, CameraRoutePoint point) GetWaypoint(int index)
void Awake()
{
var child = transform.GetChild(index);
return (child.position, child.GetComponent<CameraRoutePoint>());
movePoints = GetComponentsInChildren<MovePoint>();
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
var points = Application.isPlaying
? movePoints
: GetComponentsInChildren<MovePoint>();
if (points == null || points.Length < 2) return;
Gizmos.color = gizmoColor;
for (int i = 1; i < points.Length; i++)
{
Vector3 p0 = (i >= 2) ? points[i - 2].transform.position : points[i - 1].transform.position;
Vector3 p1 = points[i - 1].transform.position;
Vector3 p2 = points[i].transform.position;
Vector3 p3 = (i + 1 < points.Length) ? points[i + 1].transform.position : points[i].transform.position;
Vector3 prev = p1;
for (int s = 1; s <= gizmoResolution; s++)
{
float t = s / (float)gizmoResolution;
Vector3 curr = CatmullRom(p0, p1, p2, p3, t);
Gizmos.DrawLine(prev, curr);
prev = curr;
}
}
for (int i = 0; i < points.Length; i++)
{
Gizmos.color = (i == 0) ? Color.green : gizmoColor;
Gizmos.DrawWireSphere(points[i].transform.position, 0.3f);
}
}
private static Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * (
2f * p1 +
(-p0 + p2) * t +
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
);
}
#endif
}
}

View File

@@ -1,2 +1,2 @@
fileFormatVersion: 2
guid: 6de7838db2221394597524e5a7722f9f
guid: 6de7838db2221394597524e5a7722f9f

View File

@@ -0,0 +1,11 @@
using DG.Tweening;
using UnityEngine;
namespace OCTOPUS_TWIN
{
public class MovePoint : MonoBehaviour
{
public Ease easeType = Ease.InOutSine;
public float speed = -1f;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 98ee8b0a292db7549857af100e90ae1e

View File

@@ -65,6 +65,11 @@ namespace OCTOPUS_TWIN
public bool IsRouteActive => routeSequence != null && routeSequence.IsActive();
private Sequence routeSequence;
private CameraRoute currentRoute;
public bool IsRouteRunning { get; private set; }
public Action onRouteComplete;
public CameraRoute route;
public bool IsClickUI
{
get
@@ -150,6 +155,10 @@ namespace OCTOPUS_TWIN
private void LateUpdate()
{
if (Input.GetKeyDown(KeyCode.Q))
{
SetRoute(route);
}
//UI <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ī<>޶<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
if (IsClickUI || IsOnTheUI)
return;
@@ -232,11 +241,11 @@ namespace OCTOPUS_TWIN
currentAzimuth += input.mouseX * rotateSpeed;
currentAzimuth %= 360;
//if(viewMode == ViewMode.PerspectiveView)
//{
// currentElevation -= input.mouseY * rotateSpeed;
// currentElevation = Mathf.Clamp(currentElevation, minElevation, maxElevation);
//}
if(viewMode == ViewMode.PerspectiveView)
{
currentElevation -= input.mouseY * rotateSpeed;
currentElevation = Mathf.Clamp(currentElevation, minElevation, maxElevation);
}
isRotateOperation = true;
}
@@ -354,71 +363,122 @@ namespace OCTOPUS_TWIN
throw new NotImplementedException();
}
public void SetRoute(CameraRoute route)
{
StopRoute();
currentRoute = route;
var points = currentRoute.movePoints;
if (points == null || points.Length == 0) return;
IsRouteRunning = true;
Enable = false;
routeSequence = DOTween.Sequence();
camera.transform.position = points[0].transform.position;
camera.transform.rotation = points[0].transform.rotation;
for (int i = 0; i < route.PointCount; i++)
if (points.Length == 1)
{
var (position, point) = route.GetWaypoint(i);
float elev = point != null ? point.elevation : currentElevation;
float azi = point != null ? point.azimuth : currentAzimuth;
float dist = point != null ? point.distance : currentDistance;
float dur = point != null ? point.duration : 0.5f;
float wait = point != null ? point.waitTime : 0f;
routeSequence.Append(
DOTween.To(() => nextPosition, x => nextPosition = x, position, dur));
routeSequence.Join(
DOTween.To(() => currentElevation, x => currentElevation = x, elev, dur));
routeSequence.Join(
DOTween.To(() => currentAzimuth, x => currentAzimuth = x, azi, dur));
routeSequence.Join(
DOTween.To(() => currentDistance, x => currentDistance = x, dist, dur));
if (wait > 0f)
routeSequence.AppendInterval(wait);
CompleteRoute();
return;
}
if (route.loop)
routeSequence.SetLoops(-1, LoopType.Restart);
int segCount = points.Length - 1;
float[] segDurations = new float[segCount];
float totalDuration = 0f;
for (int i = 0; i < segCount; i++)
{
float dist = Vector3.Distance(points[i].transform.position, points[i + 1].transform.position);
float spd = points[i + 1].speed > 0f ? points[i + 1].speed : moveSpeed;
segDurations[i] = spd > 0f ? dist / spd : 1f;
totalDuration += segDurations[i];
}
routeSequence.OnUpdate(LastPositioning);
routeSequence.OnKill(() => Enable = true);
float[] segEnd = new float[segCount];
float acc = 0f;
for (int i = 0; i < segCount; i++)
{
acc += segDurations[i];
segEnd[i] = acc / totalDuration;
}
Vector3[] pos = new Vector3[points.Length];
Quaternion[] rot = new Quaternion[points.Length];
for (int i = 0; i < points.Length; i++)
{
pos[i] = points[i].transform.position;
rot[i] = points[i].transform.rotation;
}
routeSequence = DOTween.Sequence();
routeSequence.Append(
DOVirtual.Float(0f, 1f, totalDuration, t =>
{
int seg = segCount - 1;
for (int i = 0; i < segEnd.Length; i++)
{
if (t <= segEnd[i]) { seg = i; break; }
}
float start = seg > 0 ? segEnd[seg - 1] : 0f;
float localT = Mathf.InverseLerp(start, segEnd[seg], t);
int idx = seg + 1;
Vector3 p0 = (idx >= 2) ? pos[idx - 2] : pos[idx - 1];
Vector3 p1 = pos[idx - 1];
Vector3 p2 = pos[idx];
Vector3 p3 = (idx + 1 < pos.Length) ? pos[idx + 1] : pos[idx];
camera.transform.position = CatmullRom(p0, p1, p2, p3, localT);
camera.transform.rotation = Quaternion.Slerp(rot[idx - 1], rot[idx], localT);
}).SetEase(Ease.Linear));
routeSequence.OnComplete(CompleteRoute);
}
private static Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * (
2f * p1 +
(-p0 + p2) * t +
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
);
}
public void StopRoute()
{
if (routeSequence != null && routeSequence.IsActive())
{
routeSequence.Kill();
routeSequence = null;
}
if (!IsRouteRunning) return;
routeSequence?.Kill();
routeSequence = null;
currentRoute = null;
IsRouteRunning = false;
SyncFromCamera();
Enable = true;
}
public void AnimateToState(Vector3 pivotPosition, Vector3 eulerAngles, float distance, float duration = 0.4f)
private void CompleteRoute()
{
// <20>ִϸ<D6B4><CFB8>̼<EFBFBD> <20>߿<EFBFBD><DFBF><EFBFBD> <20><><EFBFBD><20>Է<EFBFBD> <20><>Ȱ<EFBFBD><C8B0>ȭ
Enable = false;
// DoTween<65><6E> <20><><EFBFBD><EFBFBD>Ͽ<EFBFBD> <20><>Ʈ<EFBFBD>ѷ<EFBFBD><D1B7><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ε巴<CEB5><E5B7B4> <20><><EFBFBD><EFBFBD>
DOTween.To(() => nextPosition, x => nextPosition = x, pivotPosition, duration);
DOTween.To(() => currentElevation, x => currentElevation = x, eulerAngles.x, duration);
DOTween.To(() => currentAzimuth, x => currentAzimuth = x, eulerAngles.y, duration);
DOTween.To(() => currentDistance, x => currentDistance = x, distance, duration)
.OnComplete(() => {
// <20>ִϸ<D6B4><CFB8>̼<EFBFBD><CCBC><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><20>Է<EFBFBD><D4B7><EFBFBD> <20>ٽ<EFBFBD> Ȱ<><C8B0>ȭ
Enable = true;
});
routeSequence = null;
currentRoute = null;
IsRouteRunning = false;
SyncFromCamera();
Enable = true;
onRouteComplete?.Invoke();
}
//public bool IsOperation()
//{
// if(is)
//}
private void SyncFromCamera()
{
var euler = camera.transform.eulerAngles;
currentElevation = euler.x;
currentAzimuth = euler.y;
var offset = Quaternion.Euler(euler.x, euler.y, 0f) * new Vector3(0, 0, -currentDistance);
nextPosition = camera.transform.position - offset;
cameraPivot.transform.position = nextPosition;
}
}
}

View File

@@ -5,9 +5,15 @@ using Cysharp.Threading.Tasks;
using OCTOPUS_TWIN;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UVC.Config;
using UVC.Entity;
using UVC.Entity.Processors;
using UVC.Management;
using UVC.Object3d.Manager;
using OCTOPUS_TWIN.Command;
using UVC.Core;
using UVC.UI.Window.PropertyWindow;
namespace ChunilENG.Management
@@ -22,6 +28,9 @@ namespace ChunilENG.Management
private List<Machine> machines;
private Dictionary<string, MachineInfoItem> machineKPIs = new();
private Dictionary<MachineInfoItem, Machine> itemToMachines = new();
private Dictionary<MachineInfoItem, StageObjectManager.StageObject> itemToStageObjects = new();
private StageObjectManager stageObjectManager;
public Action<Machine> onClickKPIToMachine;
@@ -39,24 +48,43 @@ namespace ChunilENG.Management
cam = OctopusTwinAppMain.Instance.cameraController; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: Start<72><74><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> ȣ<><C8A3><EFBFBD>Ͽ<EFBFBD> ij<><C4B3>
machineInfoItem = Resources.Load<MachineInfoItem>($"{ResourceURL.chunilENGUIPrefabFolderPath}{nameof(MachineInfoItem)}");
stageObjectManager = InjectorAppContext.Instance.Get<StageObjectManager>();
machines = ChunilENGSceneMain.Instance.building.GetMachines();
if (machines != null)
{
foreach (var machine in machines)
{
var infoItem = Instantiate(machineInfoItem, transform);
infoItem.Init();
machine.machineKPI = infoItem;
infoItem.onClickItem += OnClickMachineInfoItem;
itemToMachines.Add(infoItem, machine);
Regist(machine);
}
}
// StageObject 감시
if (stageObjectManager != null)
{
stageObjectManager.OnObjectAdded += OnStageObjectAdded;
stageObjectManager.OnObjectRemoved += OnStageObjectRemoved;
foreach (var kvp in stageObjectManager.Objects)
{
OnStageObjectAdded(kvp.Value);
}
}
await UniTask.CompletedTask;
}
public void Regist(Machine machine)
{
var infoItem = Instantiate(machineInfoItem, transform);
infoItem.Init();
machine.machineKPI = infoItem;
infoItem.onClickItem += OnClickMachineInfoItem;
itemToMachines.Add(infoItem, machine);
}
public void LateUpdate()
{
if (machines == null || machines.Count == 0)
if ((machines == null || machines.Count == 0) && itemToStageObjects.Count == 0)
return;
IconPositionSetting();
@@ -64,18 +92,35 @@ namespace ChunilENG.Management
}
private void IconPositionSetting()
{
foreach (var machine in machines)
if (machines != null)
{
var worldPos = machine.centerPos + Vector3.up * defaultNameHeight;
foreach (var machine in machines)
{
var worldPos = machine.centerPos + Vector3.up * defaultNameHeight;
var screenPos = Camera.main.WorldToScreenPoint(worldPos);
if (screenPos.z < 0.3f)
{
machine.machineKPI.Deactive();
continue;
}
machine.machineKPI.transform.position = screenPos;
}
}
foreach (var kvp in itemToStageObjects)
{
var worldPos = kvp.Value.gameObject.transform.position + Vector3.up * defaultNameHeight;
var screenPos = Camera.main.WorldToScreenPoint(worldPos);
if (screenPos.z < 0.3f)
{
machine.machineKPI.Deactive();
kvp.Key.Deactive();
continue;
}
machine.machineKPI.transform.position = screenPos;
kvp.Key.transform.position = screenPos;
}
}
void RangeDetection()
@@ -84,11 +129,20 @@ namespace ChunilENG.Management
float scale = Mathf.Lerp(minScale, maxScale, t);
var newScale = new Vector3(scale, scale, scale);
foreach (var machine in machines)
if (machines != null)
{
MachineIconsActive(machine);
var machineInfoItem = machine.machineKPI;
machineInfoItem.transform.localScale = newScale;
foreach (var machine in machines)
{
MachineIconsActive(machine);
var machineInfoItem = machine.machineKPI;
machineInfoItem.transform.localScale = newScale;
}
}
foreach (var kvp in itemToStageObjects)
{
kvp.Key.Active();
kvp.Key.transform.localScale = newScale;
}
}
void MachineIconsActive(Machine machine)
@@ -158,5 +212,137 @@ namespace ChunilENG.Management
icon.gameObject.SetActive(isActive);
}
}
#region StageObject
private void OnStageObjectAdded(StageObjectManager.StageObject stageObject)
{
if (stageObject.TryGetProcessor<TwinAgentAutoProcessor>(out var processor))
{
if (processor.IsCompleted)
{
RegistStageObject(stageObject);
return;
}
processor.onComplete += () => OnProcessorCompleted(stageObject);
return;
}
// 프로세서가 아직 없으면 등록 시점에 구독
stageObject.OnProcessorRegistered += registeredProcessor =>
{
if (registeredProcessor is TwinAgentAutoProcessor)
{
registeredProcessor.onComplete += () => OnProcessorCompleted(stageObject);
}
};
}
private void OnProcessorCompleted(StageObjectManager.StageObject stageObject)
{
if (itemToStageObjects.ContainsValue(stageObject)) return;
RegistStageObject(stageObject);
}
public void RegistStageObject(StageObjectManager.StageObject stageObject)
{
var infoItem = Instantiate(machineInfoItem, transform);
infoItem.Init();
var mockData = CreateMockCompleteInfo(stageObject.Equipment);
infoItem.SetData(mockData, stageObject.Equipment.label);
infoItem.onClickItem += OnClickStageObjectInfoItem;
itemToStageObjects.Add(infoItem, stageObject);
}
private void OnStageObjectRemoved(StageObjectManager.StageObject stageObject)
{
var item = itemToStageObjects.FirstOrDefault(kvp => kvp.Value == stageObject).Key;
if (item != null)
{
itemToStageObjects.Remove(item);
Destroy(item.gameObject);
}
}
private void OnClickStageObjectInfoItem(MachineInfoItem item)
{
item.transform.SetAsLastSibling();
CommandManager.Instance.Execute(
new OpenMachineDashBoardCommand(propertyWindow, null, item.data));
}
private CompleteInfo CreateMockCompleteInfo(EquipmentItem equipment)
{
int plan = UnityEngine.Random.Range(300, 600);
int goal = (int)(plan * UnityEngine.Random.Range(0.85f, 0.95f));
int work = UnityEngine.Random.Range((int)(plan * 0.6f), plan);
int good = (int)(work * UnityEngine.Random.Range(0.93f, 0.99f));
int bad = work - good;
float badRate = work > 0 ? (float)bad / work * 100f : 0f;
float efficiency = UnityEngine.Random.Range(80f, 99f);
float progressRate = plan > 0 ? (float)work / plan * 100f : 0f;
float eoRate = UnityEngine.Random.Range(85f, 99f);
float poRate = UnityEngine.Random.Range(80f, 98f);
float goodQtyRate = work > 0 ? (float)good / work * 100f : 0f;
int startHour = UnityEngine.Random.Range(6, 10);
int startMin = UnityEngine.Random.Range(0, 59);
int endHour = UnityEngine.Random.Range(17, 22);
int endMin = UnityEngine.Random.Range(0, 59);
return new CompleteInfo
{
datagbn = "P",
wordno = $"W{UnityEngine.Random.Range(1000, 9999)}",
workdt = DateTime.Now.ToString("yyyyMMdd"),
daynight = "1",
sitecd = "S01",
wccd = "WC01",
workcd = equipment.id,
worknm = equipment.label,
workseq = UnityEngine.Random.Range(1, 5).ToString(),
status = "30",
statusnm = "가동중",
itemcd = $"ITM-{UnityEngine.Random.Range(10000, 99999)}",
itemdesc = $"{equipment.label} 부품",
pjtcd = $"PJT-{UnityEngine.Random.Range(100, 999)}",
matcd = $"MAT-{UnityEngine.Random.Range(1000, 9999)}",
cycletime = UnityEngine.Random.Range(10, 60).ToString(),
cavity = UnityEngine.Random.Range(1, 8).ToString(),
planqty = plan.ToString(),
goalqty = goal.ToString(),
workqty = work.ToString(),
goodqty = good.ToString(),
badqty = bad.ToString(),
badrate = badRate.ToString("F1"),
efficiency = efficiency.ToString("F1"),
progressrate = progressRate.ToString("F1"),
sttm = $"{startHour:D2}{startMin:D2}",
totm = $"{endHour:D2}{endMin:D2}",
goaltime = "",
ptotm = "",
psttm = "",
moldcd = "",
moldseq = "",
eorate = eoRate.ToString("F1"),
porate = poRate.ToString("F1"),
goodqtyrate = goodQtyRate.ToString("F1"),
lct = "",
wct = "",
};
}
private void OnDestroy()
{
if (stageObjectManager != null)
{
stageObjectManager.OnObjectAdded -= OnStageObjectAdded;
stageObjectManager.OnObjectRemoved -= OnStageObjectRemoved;
}
}
#endregion
}
}

View File

@@ -131,7 +131,7 @@ namespace ChunilENG.UI
}
private void SetTextData(CompleteInfo data, Machine machine)
{
MachineName.SetText(SetNameData(data.worknm, machine.machineName));
MachineName.SetText(SetNameData(data.worknm, machine?.machineName ?? ""));
wordno.SetText(SetTextData(data.wordno));
sttm.SetText(CorrectionTime(data.sttm, "hh:mm"));
totm.SetText(CorrectionTime(data.totm, "hh:mm"));
@@ -179,6 +179,14 @@ namespace ChunilENG.UI
}
private void SetMoldData(Machine machine, CompleteInfo data)
{
if (machine == null)
{
Circle_10.gameObject.SetActive(false);
AttributeName_10.SetText("");
moldData.SetText("");
return;
}
var moldDataTitle = "";
var moldDataValue = "";
@@ -186,14 +194,14 @@ namespace ChunilENG.UI
{
Circle_10.gameObject.SetActive(true);
if (machine.typeOptions[1].Contains("엔겔") || machine.typeOptions[1].Contains("스미토모"))
if (machine.typeOptions[1].Contains("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>") || machine.typeOptions[1].Contains("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"))
{
moldDataTitle = "금형 차수";
moldDataTitle = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>";
moldDataValue = data.moldseq;
}
else if (machine.typeOptions[1].Contains("우진"))
else if (machine.typeOptions[1].Contains("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>"))
{
moldDataTitle = "금형 코드";
moldDataTitle = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ڵ<EFBFBD>";
moldDataValue = data.moldcd;
}
}

View File

@@ -0,0 +1,41 @@
using Cysharp.Threading.Tasks;
using Unity.VisualScripting;
using UnityEngine;
public class ForkLift : MonoBehaviour
{
public float speed;
public Transform route;
Vector3 curr;
Vector3 next;
void Start()
{
curr = transform.position;
//Run().Forget();
}
async UniTask Run()
{
for(int i= 0; i < route.childCount; ++i)
{
var point = route.GetChild(i);
next = point.transform.position;
var dist = Vector3.Distance(curr, next);
var dir = next - curr;
var startRot = transform.rotation;
var targetRot = dir.sqrMagnitude > 0.001f
? Quaternion.LookRotation(dir)
: startRot;
float p = 0;
while (p < 1f)
{
p += speed * Time.deltaTime / Mathf.Max(dist, 0.001f);
transform.position = Vector3.Lerp(curr, next, p);
transform.rotation = Quaternion.Slerp(startRot, targetRot, Mathf.Clamp01(p));
await UniTask.Yield();
}
curr= next;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5305ef3eef137614a99f6d91581d32db

View File

@@ -59,17 +59,17 @@ namespace UVC.Studio.Command
foreach (var stageObject in selectedObjects)
{
if (stageObject.GameObject == null) continue;
if (stageObject.gameObject == null) continue;
// Undo용 데이터 저장
_deletedObjects.Add(new DeletedObjectData
{
OriginalId = stageObject.Id,
ObjectName = stageObject.GameObject.name,
ObjectName = stageObject.gameObject.name,
Equipment = stageObject.Equipment,
Position = stageObject.GameObject.transform.position,
Rotation = stageObject.GameObject.transform.rotation,
Scale = stageObject.GameObject.transform.localScale
Position = stageObject.gameObject.transform.position,
Rotation = stageObject.gameObject.transform.rotation,
Scale = stageObject.gameObject.transform.localScale
});
// 객체 제거 (Unregister가 Destroy도 수행)
@@ -90,9 +90,9 @@ namespace UVC.Studio.Command
GameObject? templateObject = null;
foreach (var kvp in _stageObjectManager.Objects)
{
if (kvp.Value.Equipment?.id == data.Equipment.id && kvp.Value.GameObject != null)
if (kvp.Value.Equipment?.id == data.Equipment.id && kvp.Value.gameObject != null)
{
templateObject = kvp.Value.GameObject;
templateObject = kvp.Value.gameObject;
break;
}
}