<feat> 로봇 TCP 동기화
This commit is contained in:
@@ -122,6 +122,40 @@ public class HybridInverseKinematicsNode : MonoBehaviour
|
||||
return currentRotations;
|
||||
}
|
||||
|
||||
public List<float> GetCurrentJointAxisRotations(char axis = 'z')
|
||||
{
|
||||
List<float> jointAngles = new List<float>();
|
||||
if (nodes == null || nodes.Count == 0) return jointAngles;
|
||||
|
||||
foreach (HybridIKJoint node in nodes)
|
||||
{
|
||||
if (node.jointTransform != null)
|
||||
{
|
||||
// 관절의 부모 기준 로컬 회전값을 Euler 각도(0-360)로 가져옴
|
||||
Vector3 localEulerAngles = node.jointTransform.localEulerAngles;
|
||||
|
||||
// 매개변수로 받은 축에 해당하는 값을 리스트에 추가
|
||||
switch (axis)
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
jointAngles.Add(localEulerAngles.x);
|
||||
break;
|
||||
case 'y':
|
||||
case 'Y':
|
||||
jointAngles.Add(localEulerAngles.y);
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
default:
|
||||
jointAngles.Add(localEulerAngles.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return jointAngles;
|
||||
}
|
||||
|
||||
public void SetJointTargetPositions(List<Vector3> newPositions)
|
||||
{
|
||||
if (nodes == null || nodes.Count != newPositions.Count)
|
||||
@@ -152,6 +186,46 @@ public class HybridInverseKinematicsNode : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCurrentJointAxisRotations(List<float> jointAngles, char axis = 'z')
|
||||
{
|
||||
// 노드 리스트가 없거나, 받은 각도 리스트의 개수가 일치하지 않으면 오류를 출력하고 중단
|
||||
if (nodes == null || nodes.Count == 0 || jointAngles == null || nodes.Count != jointAngles.Count)
|
||||
{
|
||||
Debug.LogError($"관절 개수가 맞지 않습니다. (모델: {nodes?.Count ?? 0}개, 받은 데이터: {jointAngles?.Count ?? 0}개)");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].jointTransform != null)
|
||||
{
|
||||
// 현재 로컬 오일러 각도를 Vector3 변수로 가져옴
|
||||
Vector3 currentLocalEuler = nodes[i].jointTransform.localEulerAngles;
|
||||
|
||||
// 매개변수로 받은 축에 해당하는 값을 Vector3 변수에서 수정
|
||||
switch (axis)
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
currentLocalEuler.x = jointAngles[i];
|
||||
break;
|
||||
case 'y':
|
||||
case 'Y':
|
||||
currentLocalEuler.y = jointAngles[i];
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
default:
|
||||
currentLocalEuler.z = jointAngles[i];
|
||||
break;
|
||||
}
|
||||
|
||||
// 수정된 Vector3 전체를 다시 할당
|
||||
nodes[i].jointTransform.localEulerAngles = currentLocalEuler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region DebugDraw
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class RobotController : MonoBehaviour
|
||||
{
|
||||
[Header("IK")]
|
||||
[SerializeField] private HybridInverseKinematicsNode kinematicsNode;
|
||||
|
||||
[Header("Motor State")]
|
||||
[SerializeField] private GameObject motorStatusIndicator1;
|
||||
[SerializeField] private GameObject motorStatusIndicator2;
|
||||
@@ -11,6 +16,8 @@ public class RobotController : MonoBehaviour
|
||||
[SerializeField] private Material indicatorMaterial1; // 기본색(회색)
|
||||
[SerializeField] private Material indicatorMaterial2; // 초록
|
||||
|
||||
public event Action OnPoseUpdateRequest;
|
||||
|
||||
private bool isMotorOn;
|
||||
|
||||
void Start()
|
||||
@@ -25,6 +32,11 @@ public class RobotController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
OnPoseUpdateRequest?.Invoke();// TODO. 로봇을 잡고 움직일 때와 아닐 때 구분하기
|
||||
}
|
||||
|
||||
public void SetMotorState(bool isOn)
|
||||
{
|
||||
isMotorOn = isOn;
|
||||
@@ -47,4 +59,22 @@ public class RobotController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRobotPosition(RobotData robotData) // 가상 로봇 위치 업데이트
|
||||
{
|
||||
// x, y, z, rx, ry, rz => endpoint값
|
||||
// j1, ..., j6 => 6개 축의 회전값
|
||||
kinematicsNode.targetTransform.localPosition = new Vector3(robotData.x, robotData.y, robotData.z);
|
||||
kinematicsNode.targetTransform.localRotation = new Quaternion(robotData.rx, robotData.ry, robotData.rz, 0);
|
||||
|
||||
List<float> list_jAngle = new List<float>();
|
||||
list_jAngle.Add(robotData.j6);
|
||||
list_jAngle.Add(robotData.j5);
|
||||
list_jAngle.Add(robotData.j4);
|
||||
list_jAngle.Add(robotData.j3);
|
||||
list_jAngle.Add(robotData.j2);
|
||||
list_jAngle.Add(robotData.j1);
|
||||
|
||||
kinematicsNode.SetCurrentJointAxisRotations(list_jAngle, 'x');
|
||||
}
|
||||
}
|
||||
|
||||
77
Assets/Resources/Prefabs/Point_Prefab.prefab
Normal file
77
Assets/Resources/Prefabs/Point_Prefab.prefab
Normal file
@@ -0,0 +1,77 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &280273810869499429
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 23272902704394604}
|
||||
- component: {fileID: 8410384716740639099}
|
||||
- component: {fileID: 7782236473570627370}
|
||||
m_Layer: 5
|
||||
m_Name: Point_Prefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &23272902704394604
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 280273810869499429}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0.2, y: 0.3}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8410384716740639099
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 280273810869499429}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7782236473570627370
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 280273810869499429}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: a4e764ee05645514cab2cf0636654f5f, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
7
Assets/Resources/Prefabs/Point_Prefab.prefab.meta
Normal file
7
Assets/Resources/Prefabs/Point_Prefab.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e14dc8ccaa006641934bf740bd1e88d
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12581,7 +12581,7 @@ Transform:
|
||||
m_GameObject: {fileID: 445093996}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0.174, y: 1.394, z: -1.015}
|
||||
m_LocalPosition: {x: -1.27, y: 1.406, z: -0.054}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
@@ -16515,15 +16515,15 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 1.521
|
||||
value: -0.14
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.00000011920929
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0.52
|
||||
value: 1.66
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
@@ -20564,7 +20564,7 @@ Transform:
|
||||
m_GameObject: {fileID: 722849471}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 1.443, y: 0, z: -0.961}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -25875,7 +25875,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 1}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 23.400135, y: -65}
|
||||
m_AnchoredPosition: {x: 23.400135, y: -64.999985}
|
||||
m_SizeDelta: {x: 400, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &906004066
|
||||
@@ -36822,6 +36822,10 @@ MonoBehaviour:
|
||||
view: {fileID: 1243479632}
|
||||
tcpView: {fileID: 1456747455}
|
||||
robotController: {fileID: 806304512143720359}
|
||||
interactionView: {fileID: 1568384462}
|
||||
pointManagerView: {fileID: 1568384461}
|
||||
pathLineView: {fileID: 1568384459}
|
||||
popupView: {fileID: 1313589743}
|
||||
motorStatePollInterval: 1
|
||||
--- !u!4 &1299890571
|
||||
Transform:
|
||||
@@ -37154,6 +37158,7 @@ GameObject:
|
||||
- component: {fileID: 1313589740}
|
||||
- component: {fileID: 1313589739}
|
||||
- component: {fileID: 1313589738}
|
||||
- component: {fileID: 1313589743}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
@@ -37266,6 +37271,25 @@ RectTransform:
|
||||
m_AnchoredPosition: {x: -0.2, y: 1.221}
|
||||
m_SizeDelta: {x: 0.8, y: 0.8}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1313589743
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1313589737}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9abda78e6c8fdb34a925ac2483efc48b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
popupPanel: {fileID: 1313589737}
|
||||
titleText: {fileID: 331501807}
|
||||
messageText: {fileID: 827370641}
|
||||
confirmButton: {fileID: 618711572}
|
||||
cancelButton: {fileID: 1352558441}
|
||||
option1Button: {fileID: 1755737344}
|
||||
option2Button: {fileID: 196844229}
|
||||
--- !u!1001 &1315555405
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -46151,6 +46175,256 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 2525146012768536108, guid: af5398c451de74544b8b16ae846f351a, type: 3}
|
||||
m_PrefabInstance: {fileID: 1567594223}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &1568384454
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1568384458}
|
||||
- component: {fileID: 1568384457}
|
||||
- component: {fileID: 1568384456}
|
||||
- component: {fileID: 1568384455}
|
||||
- component: {fileID: 1568384460}
|
||||
- component: {fileID: 1568384459}
|
||||
- component: {fileID: 1568384462}
|
||||
- component: {fileID: 1568384461}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1568384455
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &1568384456
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 1
|
||||
--- !u!223 &1568384457
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 1928531731}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!224 &1568384458
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0.94}
|
||||
m_LocalScale: {x: 0.5, y: 0.5, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4070703782762572036}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: -0.2, y: 1.221}
|
||||
m_SizeDelta: {x: 0.8, y: 0.8}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1568384459
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c98a419633709d44ca6c0652dcf63a07, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!120 &1568384460
|
||||
LineRenderer:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 0
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Positions:
|
||||
- {x: 0, y: 0, z: 0}
|
||||
- {x: 0, y: 0, z: 1}
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
widthCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
colorGradient:
|
||||
serializedVersion: 2
|
||||
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||
key2: {r: 0, g: 0, b: 0, a: 0}
|
||||
key3: {r: 0, g: 0, b: 0, a: 0}
|
||||
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 65535
|
||||
ctime2: 0
|
||||
ctime3: 0
|
||||
ctime4: 0
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_ColorSpace: -1
|
||||
m_NumColorKeys: 2
|
||||
m_NumAlphaKeys: 2
|
||||
numCornerVertices: 0
|
||||
numCapVertices: 0
|
||||
alignment: 0
|
||||
textureMode: 0
|
||||
textureScale: {x: 1, y: 1}
|
||||
shadowBias: 0.5
|
||||
generateLightingData: 0
|
||||
m_MaskInteraction: 0
|
||||
m_UseWorldSpace: 1
|
||||
m_Loop: 0
|
||||
m_ApplyActiveColorSpace: 1
|
||||
--- !u!114 &1568384461
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 06c8786b6262dad4a86217e213be1b96, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
pointPrefab: {fileID: 280273810869499429, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
--- !u!114 &1568384462
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1568384454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c1b136fe9693203418aa8d9bacb7cfcf, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &1570547337
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -57054,15 +57328,15 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9d5ec35fa0043fd488588e61261d23de, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 1.451
|
||||
value: -0.21000004
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9d5ec35fa0043fd488588e61261d23de, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.483
|
||||
value: 0.4829999
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9d5ec35fa0043fd488588e61261d23de, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0.562
|
||||
value: 1.7019999
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9d5ec35fa0043fd488588e61261d23de, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
@@ -60885,15 +61159,15 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 1.521
|
||||
value: -0.13999999
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.00000011920929
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: -2.71
|
||||
value: -1.57
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 6c52fe1416d967a409332c0065a41575, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
@@ -61191,7 +61465,7 @@ RectTransform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2084353331}
|
||||
m_LocalRotation: {x: -0, y: -0.72839886, z: -0, w: 0.68515337}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0.368}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0.55}
|
||||
m_LocalScale: {x: 0.673158, y: 0.904753, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
@@ -61200,7 +61474,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: -93.505, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 2.676, y: 1.548}
|
||||
m_AnchoredPosition: {x: 1.67, y: 1.548}
|
||||
m_SizeDelta: {x: 1, y: 1}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1001 &2085318412
|
||||
@@ -62731,7 +63005,7 @@ Transform:
|
||||
m_GameObject: {fileID: 34692899277808924}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 1.443, y: 0, z: -0.961}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
@@ -62750,6 +63024,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: c12f4ab377ddfdc46820089b240eaf27, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
kinematicsNode: {fileID: 722849473}
|
||||
motorStatusIndicator1: {fileID: 1475297475771640280}
|
||||
motorStatusIndicator2: {fileID: 2476781507827223150}
|
||||
indicatorMaterial1: {fileID: 2100000, guid: 8429ea8a04d5dd844875dc07c5f6c06b, type: 2}
|
||||
@@ -63190,6 +63465,108 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6476108356885335244}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &4070703782762572035
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 1568384458}
|
||||
m_Modifications:
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0.2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0.3
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 280273810869499429, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Point_Prefab
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
--- !u!224 &4070703782762572036 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 23272902704394604, guid: 3e14dc8ccaa006641934bf740bd1e88d, type: 3}
|
||||
m_PrefabInstance: {fileID: 4070703782762572035}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!23 &4421136600761446311
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -63960,6 +64337,7 @@ SceneRoots:
|
||||
- {fileID: 1814192178}
|
||||
- {fileID: 2084353335}
|
||||
- {fileID: 1313589742}
|
||||
- {fileID: 1568384458}
|
||||
- {fileID: 1299890571}
|
||||
- {fileID: 580519499}
|
||||
- {fileID: 2077727516}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
@@ -9,12 +10,19 @@ public class AppManager : MonoBehaviour
|
||||
[SerializeField] private ProgramView view;
|
||||
[SerializeField] private TCPView tcpView;
|
||||
[SerializeField] private RobotController robotController;
|
||||
|
||||
[SerializeField] private InteractionView interactionView;
|
||||
[SerializeField] private PointManagerView pointManagerView;
|
||||
[SerializeField] private PathLineView pathLineView;
|
||||
[SerializeField] private PopupView popupView;
|
||||
|
||||
[SerializeField] private float motorStatePollInterval = 1.0f;
|
||||
ProgramPresenter presenter;
|
||||
private ProgramPresenter presenter;
|
||||
private string hostip;
|
||||
private int tcpPort;
|
||||
private int udpPort;
|
||||
private string configFileName = "config.cfg";
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
async void Start()
|
||||
{
|
||||
@@ -22,15 +30,13 @@ public class AppManager : MonoBehaviour
|
||||
|
||||
ProgramModel model = new ProgramModel(hostip, tcpPort, udpPort);
|
||||
await model.InitializeAsync();
|
||||
_ = model.GetTCPAsync();
|
||||
_ = model.GetTCPAsync(cancellationToken);
|
||||
|
||||
presenter = new ProgramPresenter(model, view, tcpView);
|
||||
presenter = new ProgramPresenter(model, view, tcpView, interactionView, pointManagerView, popupView, pathLineView);
|
||||
presenter.RegisterControlledRobot(robotController);
|
||||
|
||||
await presenter.UpdateMotorStateAsync();
|
||||
|
||||
view.DisplayProgram(null);
|
||||
|
||||
StartCoroutine(PollMotorStateCoroutine());
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Newtonsoft.Json.Linq;
|
||||
using Palmmedia.ReportGenerator.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
@@ -22,6 +23,7 @@ public class ProgramModel : IProgramModel
|
||||
private string tcpBaseUrl;
|
||||
private string udpBaseUrl;
|
||||
HttpClient httpClient = new HttpClient();
|
||||
private SingleTcpClient tcpClient;
|
||||
private SingleUdpClient udpClientForHttp;
|
||||
public UdpClientManager manager = new UdpClientManager();
|
||||
|
||||
@@ -32,8 +34,10 @@ public class ProgramModel : IProgramModel
|
||||
|
||||
private readonly object lockObject = new object();
|
||||
private bool hasNewData;
|
||||
|
||||
public bool isUdpLoopRunning = false;
|
||||
public bool IsUdpLoopRunning = false;
|
||||
public bool IsMoving;
|
||||
public CancellationTokenSource cancellationTokenSource;
|
||||
private Vector3 startMovementPosition;
|
||||
|
||||
public ProgramModel(string hostip, int tcpPort, int udpPort)
|
||||
{
|
||||
@@ -46,7 +50,8 @@ public class ProgramModel : IProgramModel
|
||||
{
|
||||
await LoadAllPrograms();
|
||||
hasNewData = false;
|
||||
isUdpLoopRunning = true;
|
||||
IsUdpLoopRunning = true;
|
||||
IsMoving = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,10 +68,14 @@ public class ProgramModel : IProgramModel
|
||||
lock (lockObject)
|
||||
{
|
||||
hasNewData = false; // 데이터를 읽었으므로 플래그를 내림
|
||||
return robotData; // (데이터 복사본을 반환하는 것이 더 안전할 수 있음)
|
||||
return robotData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 프로그램 생성/불러오기 시스템
|
||||
/// </summary>
|
||||
|
||||
public async Task<bool> CheckProgramExists(string jobProgramName)
|
||||
{
|
||||
string requestUri = $"{tcpBaseUrl}/file_manager/file_exist?pathname=project/jobs/{jobProgramName}";
|
||||
@@ -171,9 +180,9 @@ public class ProgramModel : IProgramModel
|
||||
}
|
||||
}
|
||||
|
||||
public async Task GetTCPAsync()
|
||||
public async Task GetTCPAsync(CancellationToken token)
|
||||
{
|
||||
while (isUdpLoopRunning)
|
||||
while (IsUdpLoopRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -260,8 +269,148 @@ public class ProgramModel : IProgramModel
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 로봇 위치 기록 시스템
|
||||
/// </summary>
|
||||
public async Task<bool> SavePointToProgramAsync(RobotData pointData, int index = -1)
|
||||
{
|
||||
if (CurrentProgram == null)
|
||||
{
|
||||
Debug.LogError("저장할 프로그램이 로드되지 않았습니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// DTO(전송 객체) 생성
|
||||
// (서버가 RobotData와 index, 프로그램 ID를 어떻게 받는지에 따라 수정 필요)
|
||||
var payload = new
|
||||
{
|
||||
programId = CurrentProgram.ProgramId,
|
||||
indexToUpdate = index, // -1이면 새 포인트, 0 이상이면 해당 인덱스 수정
|
||||
pose = pointData
|
||||
};
|
||||
string jsonPayload = JsonConvert.SerializeObject(payload);
|
||||
HttpContent content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
string requestUri = (index == -1)
|
||||
? $"{tcpBaseUrl}/project/jobs/{CurrentProgram.ProgramId}/ins_cmd_line"
|
||||
: $"{tcpBaseUrl}/project/jobs/{CurrentProgram.ProgramId}/pose_modify";
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage result = await httpClient.PostAsync(requestUri, content);
|
||||
if (result.IsSuccessStatusCode)
|
||||
{
|
||||
// 서버 저장이 성공하면, 메모리(CurrentProgram)에도 반영
|
||||
if (index == -1)
|
||||
CurrentProgram.AddStep(pointData); // RobotProgram에 새 RobotMoveStep 추가
|
||||
else
|
||||
CurrentProgram.UpdateStep(index, pointData); // RobotProgram의 해당 스텝 갱신
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"포인트 저장 실패: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 서버에 포인트 삭제 요청
|
||||
public async Task<bool> DeletePointFromProgramAsync(int index)
|
||||
{
|
||||
if (CurrentProgram == null) return false;
|
||||
|
||||
var payload = new { programId = CurrentProgram.ProgramId, indexToDelete = index };
|
||||
string jsonPayload = JsonConvert.SerializeObject(payload);
|
||||
HttpContent content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
string requestUri = $"{tcpBaseUrl}/project/jobs/{CurrentProgram.ProgramId}/del_cmd_line";
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage result = await httpClient.PostAsync(requestUri, content);
|
||||
if (result.IsSuccessStatusCode)
|
||||
{
|
||||
// 서버 삭제 성공 시, 메모리(CurrentProgram)에서도 삭제
|
||||
CurrentProgram.DeleteStep(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"포인트 삭제 실패: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 실시간 로봇 TCP 이동
|
||||
public async Task StreamPoseToRobotUdpAsync(RobotData pose)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] udpPacket = ConvertPoseToPacket(pose);
|
||||
|
||||
await udpClientForHttp.SendBytesAsync(udpPacket);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"UDP 스트리밍 실패: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ConvertPoseToPacket(RobotData pose)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write(pose.x);
|
||||
writer.Write(pose.y);
|
||||
writer.Write(pose.z);
|
||||
writer.Write(pose.rx);
|
||||
writer.Write(pose.ry);
|
||||
writer.Write(pose.rz);
|
||||
}
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
//사용자 tcp 드래깅 후 제어기로 이동 명령 하달
|
||||
public async Task<bool> StartMovement(Vector3 position)
|
||||
{
|
||||
startMovementPosition.x = Convert.ToSingle(Math.Round(-1 * position.x * 1000, 2));
|
||||
startMovementPosition.y = Convert.ToSingle(Math.Round(-1 * position.z * 1000, 2));
|
||||
startMovementPosition.z = Convert.ToSingle(Math.Round(position.y * 1000, 2));
|
||||
var jsonResponse = await tcpClient.SendPostRequestAsync("/project/robot/move_to_pose_manual", $"{{\"pose_tg\":{{\"crd\":\"robot\",\"_type\":\"Pose\",\"mechinfo\":1,\"x\":{startMovementPosition.x},\"y\":{startMovementPosition.y},\"z\":{startMovementPosition.z}, \"rx\":{robotData.rx}, \"ry\":{robotData.ry}, \"rz\":{robotData.rz}}}}}");
|
||||
return jsonResponse.Contains("200");
|
||||
}
|
||||
|
||||
//타겟 포지션 도달까지 이동 명령
|
||||
private async Task MovementLoopAsync()
|
||||
{
|
||||
while (!cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
if (IsMoving)
|
||||
{
|
||||
await udpClientForHttp.SendFilledBytesAsync(new Dictionary<int, byte> { { 2, 0x20 } });
|
||||
await Task.Delay(100);
|
||||
bool isApproximatelyX = Mathf.Approximately(startMovementPosition.x, Convert.ToSingle(Math.Round(robotData.x, 2)));
|
||||
bool isApproximatelyY = Mathf.Approximately(startMovementPosition.y, Convert.ToSingle(Math.Round(robotData.y, 2)));
|
||||
bool isApproximatelyZ = Mathf.Approximately(startMovementPosition.z, Convert.ToSingle(Math.Round(robotData.z, 2)));
|
||||
if (isApproximatelyX && isApproximatelyY && isApproximatelyZ)
|
||||
{
|
||||
IsMoving = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
isUdpLoopRunning = false;
|
||||
IsUdpLoopRunning = false;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,10 @@ public class RobotProgram
|
||||
|
||||
private void ParseJobContent(string rawText)
|
||||
{
|
||||
// 1. 헤더 파싱 (예: "Robot Job File; { version: 2.0, ... }")
|
||||
// 헤더 파싱 (예: "Robot Job File; { version: 2.0, ... }")
|
||||
// 정규식이나 Substring을 사용해 version, mech_type 등 추출
|
||||
|
||||
// 2. 스텝(S1, S2...) 파싱
|
||||
// 스텝(S1, S2...) 파싱
|
||||
string[] lines = rawText.Split('\n');
|
||||
foreach (string line in lines)
|
||||
{
|
||||
@@ -45,4 +45,30 @@ public class RobotProgram
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public void AddStep(RobotData data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void UpdateStep(int index, RobotData data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DeleteStep(int index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RobotData GetStepPose(int index)
|
||||
{
|
||||
RobotData data = null;
|
||||
return data;
|
||||
}
|
||||
|
||||
public void GetAllStepPoses()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.ARSubsystems;
|
||||
|
||||
public enum PopupState
|
||||
{
|
||||
None,
|
||||
ConfirmAddPoint,
|
||||
ConfirmModifyPoint,
|
||||
MoveOrDelete,
|
||||
ConfirmDelete
|
||||
}
|
||||
|
||||
public class ProgramPresenter
|
||||
{
|
||||
private ProgramModel model;
|
||||
@@ -12,7 +22,19 @@ public class ProgramPresenter
|
||||
private string _programId;
|
||||
private bool lastKnownMotorState = false;
|
||||
|
||||
public ProgramPresenter(ProgramModel model, IProgramView view, TCPView tcpView)
|
||||
private IInteractionView interactionView;
|
||||
private IPointManagerView pointManagerView;
|
||||
private IPathLineView pathLineView;
|
||||
private IPopupView popupView;
|
||||
|
||||
private PopupState currentPopupState = PopupState.None;
|
||||
private RobotData pendingPointData; // 팝업창에 대한 응답을 기다리는 임시 데이터
|
||||
private int activePointIndex = -1; // 현재 수정/삭제 중인 포인트의 인덱스
|
||||
|
||||
private bool IsDragging = false;
|
||||
|
||||
public ProgramPresenter(ProgramModel model, IProgramView view, TCPView tcpView,
|
||||
IInteractionView interactionView, IPointManagerView pmView, IPopupView popView, IPathLineView pathLineView)
|
||||
{
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
@@ -25,11 +47,20 @@ public class ProgramPresenter
|
||||
this.view.OnSaveClicked += HandleSaveProgram;
|
||||
this.view.OnAddPointClicked += HandleAddPoint;
|
||||
this.tcpView.OnTCPupdateRequested += HandleTCPViewUpdate;
|
||||
|
||||
//this.interactionView.OnRobotReleased += HandleRobotReleased;
|
||||
//this.interactionView.OnPointClicked += HandlePointClicked;
|
||||
//this.interactionView.OnPointDragStart += HandlePointDragStart;
|
||||
//this.interactionView.OnPointDragUpdate += HandlePointDragUpdate;
|
||||
//this.interactionView.OnPointDragEnd += HandlePointDragEnd;
|
||||
|
||||
//this.popupView.OnPopupResponse += HandlePopupResponse;
|
||||
}
|
||||
|
||||
public void RegisterControlledRobot(RobotController robot)
|
||||
{
|
||||
this.controlledRobot = robot;
|
||||
this.controlledRobot.OnPoseUpdateRequest += HandlePoseViewUpdate;
|
||||
}
|
||||
|
||||
public async Task UpdateMotorStateAsync()
|
||||
@@ -62,7 +93,6 @@ public class ProgramPresenter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task HandleCreateProgram(string programId)
|
||||
{
|
||||
if (await model.CreateNewProgram(programId))
|
||||
@@ -126,6 +156,143 @@ public class ProgramPresenter
|
||||
tcpView.SetCoordinates(data);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 실시간 동기화 ---
|
||||
private void HandlePoseViewUpdate()
|
||||
{
|
||||
RobotData data = model.GetLatestRobotData();
|
||||
controlledRobot.SetRobotPosition(data); // 3D 가상 로봇 모델 위치 업데이트
|
||||
}
|
||||
|
||||
// --- 새 포인트 추가 ---
|
||||
private void HandleRobotReleased(RobotData pose)
|
||||
{
|
||||
pendingPointData = pose; // 1. 임시 저장
|
||||
currentPopupState = PopupState.ConfirmAddPoint; // 2. 상태 설정
|
||||
popupView.ShowConfirmPopup("위치 확정", "이 위치를 새 포인트로 저장하시겠습니까?"); // 3. 팝업 요청
|
||||
}
|
||||
|
||||
// --- 포인트 클릭 ---
|
||||
private void HandlePointClicked(int index)
|
||||
{
|
||||
activePointIndex = index; // 인덱스 저장
|
||||
currentPopupState = PopupState.MoveOrDelete; // 상태 설정
|
||||
popupView.ShowOptionPopup("포인트 작업", "무엇을 하시겠습니까?", "여기로 이동", "삭제"); // 팝업 요청
|
||||
}
|
||||
|
||||
// --- 포인트 드래그 ---
|
||||
private RobotData originalDragPose; // 취소 시 돌아갈 원본 위치
|
||||
|
||||
private void HandlePointDragStart(int index)
|
||||
{
|
||||
IsDragging = true;
|
||||
activePointIndex = index;
|
||||
originalDragPose = model.CurrentProgram.GetStepPose(index);
|
||||
|
||||
//interactionView.ShowDragArrow(GetPositionFromPose(originalDragPose));
|
||||
interactionView.ShowGhostRobot(originalDragPose);
|
||||
}
|
||||
|
||||
private async void HandlePointDragUpdate(int index, Vector3 newWorldPos)
|
||||
{
|
||||
if (!IsDragging) return;
|
||||
//RobotData newPose = ConvertVectorToRobotData(newWorldPos);
|
||||
|
||||
// 고스트 로봇, 포인트, 경로 실시간 이동
|
||||
//interactionView.ShowGhostRobot(newPose);
|
||||
//pointManagerView.UpdatePointPosition(index, newPose);
|
||||
//pathLineView.DrawPath(GetFullPathOfProgramWithTempChange(index, newPose)); // 임시 경로 그리기
|
||||
|
||||
//await model.StreamPoseToRobotUdpAsync(newPose);
|
||||
}
|
||||
|
||||
private void HandlePointDragEnd(int index)
|
||||
{
|
||||
//IsDragging = false;
|
||||
//interactionView.HideDragArrow();
|
||||
//interactionView.HideGhostRobot();
|
||||
|
||||
//// (가상 로봇 위치 이동 로직 - 5단계)
|
||||
//// robotController.SetRobotPosition(newPose);
|
||||
|
||||
//pendingPointData = ConvertVectorToRobotData(GetLastDragPosition()); // 임시 저장
|
||||
//currentPopupState = PopupState.ConfirmModifyPoint; // 상태 설정
|
||||
//popupView.ShowConfirmPopup("위치 수정", "이 위치로 포인트를 수정하시겠습니까?"); // 팝업 요청
|
||||
}
|
||||
|
||||
|
||||
// --- 팝업 응답 통합 핸들러 ---
|
||||
private async void HandlePopupResponse(PopupResponse response)
|
||||
{
|
||||
popupView.HidePopup();
|
||||
|
||||
switch (currentPopupState)
|
||||
{
|
||||
case PopupState.ConfirmAddPoint:
|
||||
if (response == PopupResponse.Confirm) // 확정
|
||||
{
|
||||
await model.SavePointToProgramAsync(pendingPointData);
|
||||
RedrawSceneFromModel(); // 뷰 갱신
|
||||
}
|
||||
break;
|
||||
|
||||
// 드래그 확정/취소
|
||||
case PopupState.ConfirmModifyPoint:
|
||||
if (response == PopupResponse.Confirm) // 확정
|
||||
{
|
||||
// 최종 위치를 서버에 저장
|
||||
// 드래그가 끝났으니 이 위치를 프로그램에 저장
|
||||
await model.SavePointToProgramAsync(pendingPointData, activePointIndex);
|
||||
RedrawSceneFromModel();
|
||||
}
|
||||
else // 취소
|
||||
{
|
||||
pointManagerView.UpdatePointPosition(activePointIndex, originalDragPose); // 원위치
|
||||
RedrawSceneFromModel(); // 경로 원위치
|
||||
}
|
||||
break;
|
||||
|
||||
// 이동/삭제
|
||||
case PopupState.MoveOrDelete:
|
||||
if (response == PopupResponse.Option1) // 여기로 이동
|
||||
{
|
||||
RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex);
|
||||
//await model.StartMovement(targetPose);
|
||||
}
|
||||
else if (response == PopupResponse.Option2) // 삭제
|
||||
{
|
||||
currentPopupState = PopupState.ConfirmDelete;
|
||||
popupView.ShowConfirmPopup("삭제 확인", "정말로 이 포인트를 삭제하시겠습니까?");
|
||||
}
|
||||
break;
|
||||
|
||||
// 최종 삭제
|
||||
case PopupState.ConfirmDelete:
|
||||
if (response == PopupResponse.Confirm)
|
||||
{
|
||||
await model.DeletePointFromProgramAsync(activePointIndex);
|
||||
RedrawSceneFromModel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 모든 작업 후 상태 초기화
|
||||
currentPopupState = PopupState.None;
|
||||
activePointIndex = -1;
|
||||
}
|
||||
|
||||
// Model의 현재 상태를 읽어 모든 View(포인트, 경로)를 새로 고침
|
||||
private void RedrawSceneFromModel()
|
||||
{
|
||||
//if (model.CurrentProgram == null) return;
|
||||
|
||||
//// (RobotProgram.Steps (List<RobotMoveStep>)를 List<RobotData>로 변환하는 로직)
|
||||
//List<RobotData> poses = model.CurrentProgram.GetAllStepPoses();
|
||||
|
||||
//pointManagerView.RedrawPoints(poses); // 포인트 다시 그림
|
||||
//pathLineView.DrawPath(poses); // 경로 다시 그림
|
||||
}
|
||||
|
||||
private void Destroy()
|
||||
{
|
||||
this.view.OnCreateProgramClicked -= async (id) => await HandleCreateProgram(id);
|
||||
|
||||
62
Assets/Scripts/View/InteractionView.cs
Normal file
62
Assets/Scripts/View/InteractionView.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
// Presenter가 InteractionView를 제어하기 위한 인터페이스
|
||||
public interface IInteractionView
|
||||
{
|
||||
// VR 컨트롤러가 로봇을 잡았다 놨을 때 발생
|
||||
event Action<RobotData> OnRobotReleased;
|
||||
// VR 컨트롤러가 특정 포인트를 클릭했을 때 발생
|
||||
event Action<int> OnPointClicked;
|
||||
// VR 컨트롤러가 포인트를 잡고 드래그 시작/중/끝 했을 때 발생
|
||||
event Action<int> OnPointDragStart;
|
||||
event Action<int, Vector3> OnPointDragUpdate; // (포인트 인덱스, 새 월드 좌표)
|
||||
event Action<int> OnPointDragEnd;
|
||||
|
||||
// Presenter가 호출할 함수들
|
||||
void ShowGhostRobot(RobotData pose);
|
||||
void HideGhostRobot();
|
||||
void ShowDragArrow(Vector3 position);
|
||||
void HideDragArrow();
|
||||
}
|
||||
|
||||
// (이 스크립트는 VR 컨트롤러 로직이 있는 곳에 붙어야 합니다)
|
||||
public class InteractionView : MonoBehaviour, IInteractionView
|
||||
{
|
||||
public event Action<RobotData> OnRobotReleased;
|
||||
public event Action<int> OnPointClicked;
|
||||
public event Action<int> OnPointDragStart;
|
||||
public event Action<int, Vector3> OnPointDragUpdate;
|
||||
public event Action<int> OnPointDragEnd;
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 컨트롤러로 로봇을 잡고있는지 감지
|
||||
// if (IsGrabbingRobot()) { ... }
|
||||
|
||||
// 컨트롤러 그랩 버튼을 뗐을 때
|
||||
// if (OnGrabRelease())
|
||||
// {
|
||||
// RobotData currentPose = GetCurrentRobotPose();
|
||||
// OnRobotReleased?.Invoke(currentPose); // 2. "위치 확정?" 팝업 요청
|
||||
// }
|
||||
|
||||
// 컨트롤러로 포인트를 클릭했을 때
|
||||
// if (OnPointClick(out int clickedPointIndex))
|
||||
// {
|
||||
// OnPointClicked?.Invoke(clickedPointIndex); // 8. "이동/삭제?" 팝업 요청
|
||||
// }
|
||||
|
||||
// 컨트롤러로 포인트를 꾹 누르기 시작(드래그 시작)
|
||||
// if (OnPointHold(out int draggedPointIndex))
|
||||
// {
|
||||
// OnPointDragStart?.Invoke(draggedPointIndex); // 5. 드래그 시작
|
||||
// }
|
||||
}
|
||||
|
||||
// --- Presenter가 호출할 함수들 ---
|
||||
public void ShowGhostRobot(RobotData pose) { /* 반투명 로봇2 활성화 및 위치 설정 */ }
|
||||
public void HideGhostRobot() { /* 반투명 로봇2 비활성화 */ }
|
||||
public void ShowDragArrow(Vector3 position) { /* 드래그용 화살표 UI 활성화 및 위치 설정 */ }
|
||||
public void HideDragArrow() { /* 드래그용 화살표 UI 비활성화 */ }
|
||||
}
|
||||
2
Assets/Scripts/View/InteractionView.cs.meta
Normal file
2
Assets/Scripts/View/InteractionView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1b136fe9693203418aa8d9bacb7cfcf
|
||||
35
Assets/Scripts/View/PathLineView.cs
Normal file
35
Assets/Scripts/View/PathLineView.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// ÀÎÅÍÆäÀ̽º
|
||||
public interface IPathLineView
|
||||
{
|
||||
void DrawPath(List<RobotData> poses); // (RobotProgram.Steps¿¡¼ º¯È¯)
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
public class PathLineView : MonoBehaviour, IPathLineView
|
||||
{
|
||||
private LineRenderer lineRenderer;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
lineRenderer = GetComponent<LineRenderer>();
|
||||
lineRenderer.positionCount = 0;
|
||||
}
|
||||
|
||||
public void DrawPath(List<RobotData> poses)
|
||||
{
|
||||
if (poses == null || poses.Count < 2)
|
||||
{
|
||||
lineRenderer.positionCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
lineRenderer.positionCount = poses.Count;
|
||||
for (int i = 0; i < poses.Count; i++)
|
||||
{
|
||||
lineRenderer.SetPosition(i, new Vector3(poses[i].x, poses[i].y, poses[i].z));
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/View/PathLineView.cs.meta
Normal file
2
Assets/Scripts/View/PathLineView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c98a419633709d44ca6c0652dcf63a07
|
||||
47
Assets/Scripts/View/PointManagerView.cs
Normal file
47
Assets/Scripts/View/PointManagerView.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// 인터페이스
|
||||
public interface IPointManagerView
|
||||
{
|
||||
void CreatePoint(RobotData pose);
|
||||
void UpdatePointPosition(int index, RobotData pose);
|
||||
void DeletePoint(int index);
|
||||
void RedrawPoints(List<RobotData> poses); // (RobotProgram.Steps에서 변환)
|
||||
}
|
||||
|
||||
public class PointManagerView : MonoBehaviour, IPointManagerView
|
||||
{
|
||||
[SerializeField] private GameObject pointPrefab; // 인스펙터에서 포인트 프리팹 연결
|
||||
private List<GameObject> activePoints = new List<GameObject>();
|
||||
|
||||
public void CreatePoint(RobotData pose)
|
||||
{
|
||||
Vector3 position = new Vector3(pose.x, pose.y, pose.z);
|
||||
GameObject pointObj = Instantiate(pointPrefab, position, Quaternion.identity, this.transform);
|
||||
activePoints.Add(pointObj);
|
||||
// (참고: 이 pointObj에 'InteractionView'가 감지할 수 있는 콜라이더와 스크립트가 있어야 함)
|
||||
}
|
||||
|
||||
public void UpdatePointPosition(int index, RobotData pose)
|
||||
{
|
||||
if (index < 0 || index >= activePoints.Count) return;
|
||||
activePoints[index].transform.position = new Vector3(pose.x, pose.y, pose.z);
|
||||
}
|
||||
|
||||
public void DeletePoint(int index)
|
||||
{
|
||||
if (index < 0 || index >= activePoints.Count) return;
|
||||
Destroy(activePoints[index]);
|
||||
activePoints.RemoveAt(index);
|
||||
}
|
||||
|
||||
public void RedrawPoints(List<RobotData> poses)
|
||||
{
|
||||
// 기존 포인트 모두 삭제
|
||||
foreach (var point in activePoints) Destroy(point);
|
||||
activePoints.Clear();
|
||||
// 모든 포인트 새로 생성
|
||||
foreach (var pose in poses) CreatePoint(pose);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/View/PointManagerView.cs.meta
Normal file
2
Assets/Scripts/View/PointManagerView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06c8786b6262dad4a86217e213be1b96
|
||||
76
Assets/Scripts/View/PopupView.cs
Normal file
76
Assets/Scripts/View/PopupView.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
// 팝업 응답 타입
|
||||
public enum PopupResponse
|
||||
{
|
||||
Confirm, // 확정
|
||||
Cancel, // 취소
|
||||
Option1, // (예: 여기로 이동)
|
||||
Option2 // (예: 삭제)
|
||||
}
|
||||
|
||||
public interface IPopupView
|
||||
{
|
||||
event Action<PopupResponse> OnPopupResponse;
|
||||
void ShowConfirmPopup(string title, string message); // 2, 5단계용 (확정/취소)
|
||||
void ShowOptionPopup(string title, string message, string opt1Text, string opt2Text); // 8단계용 (이동/삭제)
|
||||
void HidePopup();
|
||||
}
|
||||
|
||||
public class PopupView : MonoBehaviour, IPopupView
|
||||
{
|
||||
public event Action<PopupResponse> OnPopupResponse;
|
||||
|
||||
[SerializeField] private GameObject popupPanel;
|
||||
[SerializeField] private TextMeshProUGUI titleText;
|
||||
[SerializeField] private TextMeshProUGUI messageText;
|
||||
|
||||
[SerializeField] private Button confirmButton; // '확정' 버튼
|
||||
[SerializeField] private Button cancelButton; // '취소' 버튼
|
||||
[SerializeField] private Button option1Button; // '옵션1(이동)' 버튼
|
||||
[SerializeField] private Button option2Button; // '옵션2(삭제)' 버튼
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 각 버튼이 클릭되면 Presenter에게 응답 이벤트를 보냄
|
||||
confirmButton.onClick.AddListener(() => OnPopupResponse?.Invoke(PopupResponse.Confirm));
|
||||
cancelButton.onClick.AddListener(() => OnPopupResponse?.Invoke(PopupResponse.Cancel));
|
||||
option1Button.onClick.AddListener(() => OnPopupResponse?.Invoke(PopupResponse.Option1));
|
||||
option2Button.onClick.AddListener(() => OnPopupResponse?.Invoke(PopupResponse.Option2));
|
||||
|
||||
popupPanel.SetActive(false);
|
||||
}
|
||||
|
||||
public void ShowConfirmPopup(string title, string message)
|
||||
{
|
||||
titleText.text = title;
|
||||
messageText.text = message;
|
||||
confirmButton.gameObject.SetActive(true);
|
||||
cancelButton.gameObject.SetActive(true);
|
||||
option1Button.gameObject.SetActive(false);
|
||||
option2Button.gameObject.SetActive(false);
|
||||
popupPanel.SetActive(true);
|
||||
}
|
||||
|
||||
public void ShowOptionPopup(string title, string message, string opt1Text, string opt2Text)
|
||||
{
|
||||
titleText.text = title;
|
||||
messageText.text = message;
|
||||
option1Button.GetComponentInChildren<Text>().text = opt1Text;
|
||||
option2Button.GetComponentInChildren<Text>().text = opt2Text;
|
||||
|
||||
confirmButton.gameObject.SetActive(false);
|
||||
cancelButton.gameObject.SetActive(true); // 취소 버튼은 공용으로 사용
|
||||
option1Button.gameObject.SetActive(true);
|
||||
option2Button.gameObject.SetActive(true);
|
||||
popupPanel.SetActive(true);
|
||||
}
|
||||
|
||||
public void HidePopup()
|
||||
{
|
||||
popupPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/View/PopupView.cs.meta
Normal file
2
Assets/Scripts/View/PopupView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9abda78e6c8fdb34a925ac2483efc48b
|
||||
Reference in New Issue
Block a user