중간저장
This commit is contained in:
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git log:*)",
|
||||
"mcp__UnityMCP__refresh_unity",
|
||||
"mcp__UnityMCP__read_console"
|
||||
]
|
||||
}
|
||||
}
|
||||
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"visualstudiotoolsforunity.vstuc"
|
||||
]
|
||||
}
|
||||
10
.vscode/launch.json
vendored
Normal file
10
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Unity",
|
||||
"type": "vstuc",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
71
.vscode/settings.json
vendored
Normal file
71
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.DS_Store": true,
|
||||
"**/.git": true,
|
||||
"**/.vs": true,
|
||||
"**/.gitmodules": true,
|
||||
"**/.vsconfig": true,
|
||||
"**/*.booproj": true,
|
||||
"**/*.pidb": true,
|
||||
"**/*.suo": true,
|
||||
"**/*.user": true,
|
||||
"**/*.userprefs": true,
|
||||
"**/*.unityproj": true,
|
||||
"**/*.dll": true,
|
||||
"**/*.exe": true,
|
||||
"**/*.pdf": true,
|
||||
"**/*.mid": true,
|
||||
"**/*.midi": true,
|
||||
"**/*.wav": true,
|
||||
"**/*.gif": true,
|
||||
"**/*.ico": true,
|
||||
"**/*.jpg": true,
|
||||
"**/*.jpeg": true,
|
||||
"**/*.png": true,
|
||||
"**/*.psd": true,
|
||||
"**/*.tga": true,
|
||||
"**/*.tif": true,
|
||||
"**/*.tiff": true,
|
||||
"**/*.3ds": true,
|
||||
"**/*.3DS": true,
|
||||
"**/*.fbx": true,
|
||||
"**/*.FBX": true,
|
||||
"**/*.lxo": true,
|
||||
"**/*.LXO": true,
|
||||
"**/*.ma": true,
|
||||
"**/*.MA": true,
|
||||
"**/*.obj": true,
|
||||
"**/*.OBJ": true,
|
||||
"**/*.asset": true,
|
||||
"**/*.cubemap": true,
|
||||
"**/*.flare": true,
|
||||
"**/*.mat": true,
|
||||
"**/*.meta": true,
|
||||
"**/*.prefab": true,
|
||||
"**/*.unity": true,
|
||||
"build/": true,
|
||||
"Build/": true,
|
||||
"Library/": true,
|
||||
"library/": true,
|
||||
"obj/": true,
|
||||
"Obj/": true,
|
||||
"Logs/": true,
|
||||
"logs/": true,
|
||||
"ProjectSettings/": true,
|
||||
"UserSettings/": true,
|
||||
"temp/": true,
|
||||
"Temp/": true
|
||||
},
|
||||
"files.associations": {
|
||||
"*.asset": "yaml",
|
||||
"*.meta": "yaml",
|
||||
"*.prefab": "yaml",
|
||||
"*.unity": "yaml",
|
||||
},
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.sln": "*.csproj",
|
||||
"*.slnx": "*.csproj"
|
||||
},
|
||||
"dotnet.defaultSolution": "OCTOPUS_TWIN-Demo.slnx"
|
||||
}
|
||||
@@ -34,7 +34,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: -16.100006}
|
||||
m_SizeDelta: {x: 0, y: 1}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3812911501507659818
|
||||
@@ -87,14 +87,13 @@ GameObject:
|
||||
- component: {fileID: 6734100349773792131}
|
||||
- component: {fileID: 7955749765911055004}
|
||||
- component: {fileID: 4623412371413303123}
|
||||
- component: {fileID: 8862115974344976431}
|
||||
m_Layer: 0
|
||||
m_Name: TerminalView
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &9100016846584606779
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -107,6 +106,7 @@ RectTransform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8866481709266374613}
|
||||
- {fileID: 5882288525513767156}
|
||||
- {fileID: 5201271029291240114}
|
||||
m_Father: {fileID: 8065352563668446013}
|
||||
@@ -166,26 +166,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: e2bd83bb433a1e043b39643c011b6cc0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.UI.Window.PropertyWindow.TerminalView
|
||||
--- !u!114 &8862115974344976431
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 720111926108502729}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
_scrollRect: {fileID: 0}
|
||||
_content: {fileID: 0}
|
||||
_font: {fileID: 0}
|
||||
--- !u!1 &954952398855517667
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -727,8 +710,8 @@ MonoBehaviour:
|
||||
m_TargetGraphic: {fileID: 8654063950112981296}
|
||||
m_HandleRect: {fileID: 0}
|
||||
m_Direction: 0
|
||||
m_Value: 1
|
||||
m_Size: 1
|
||||
m_Value: 0
|
||||
m_Size: 0.99995804
|
||||
m_NumberOfSteps: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
@@ -1131,17 +1114,17 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5615275534113093535}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
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: 1205322734457541541}
|
||||
m_Father: {fileID: 9100016846584606779}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 100, y: -25}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_AnchoredPosition: {x: 100.2, y: -7.9052124}
|
||||
m_SizeDelta: {x: 200, y: 15.4105}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8512899717493741604
|
||||
CanvasRenderer:
|
||||
@@ -1171,7 +1154,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Log Texts
|
||||
m_text: Terminal Log
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 2ec62c2b5b163f64e92dc4988250c9c8, type: 2}
|
||||
m_sharedMaterial: {fileID: 1892695685711531568, guid: 2ec62c2b5b163f64e92dc4988250c9c8, type: 2}
|
||||
@@ -1361,14 +1344,13 @@ RectTransform:
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8866481709266374613}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8946140259888033016}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: -0.00022888184, y: -0.0010070801}
|
||||
m_SizeDelta: {x: -12.372238, y: -17.451527}
|
||||
m_AnchoredPosition: {x: 0, y: 0.0001373291}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &4790256706883938101
|
||||
MonoBehaviour:
|
||||
@@ -1389,7 +1371,7 @@ MonoBehaviour:
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandWidth: 0
|
||||
m_ChildForceExpandHeight: 1
|
||||
m_ChildControlWidth: 0
|
||||
m_ChildControlHeight: 0
|
||||
@@ -1403,13 +1385,13 @@ MonoBehaviour:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6982330689999531319}
|
||||
m_Enabled: 0
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 0
|
||||
m_HorizontalFit: 2
|
||||
m_VerticalFit: 2
|
||||
--- !u!1 &7857615039342438008
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1595,9 +1577,9 @@ RectTransform:
|
||||
m_Father: {fileID: 5882288525513767156}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: -2, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &319972252397042891
|
||||
CanvasRenderer:
|
||||
@@ -1724,8 +1706,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: -8.299988}
|
||||
m_SizeDelta: {x: 0, y: -16.6}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &4112323457797356702
|
||||
MonoBehaviour:
|
||||
@@ -1740,7 +1722,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ScrollRect
|
||||
m_Content: {fileID: 1205322734457541541}
|
||||
m_Horizontal: 1
|
||||
m_Horizontal: 0
|
||||
m_Vertical: 1
|
||||
m_MovementType: 1
|
||||
m_Elasticity: 0.1
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@@ -19,7 +21,7 @@ namespace UVC.UI.Window.PropertyWindow
|
||||
/// </summary>
|
||||
/// <param name="entity">변환할 Entity</param>
|
||||
/// <returns>탭 데이터 리스트</returns>
|
||||
public static List<EntityTabData> GetTabsForEntity(UVC.Entity.Entity entity)
|
||||
public static List<EntityTabData> GetTabsForEntity(UVC.Entity.Entity entity, TerminalView? terminalView = null)
|
||||
{
|
||||
if (entity == null)
|
||||
{
|
||||
@@ -30,7 +32,7 @@ namespace UVC.UI.Window.PropertyWindow
|
||||
// Entity 타입별로 분기
|
||||
if (entity is StageObjectManager.StageObject stageObject)
|
||||
{
|
||||
return GetTabsForStageObject(stageObject);
|
||||
return GetTabsForStageObject(stageObject, terminalView);
|
||||
}
|
||||
|
||||
// 다른 Entity 타입 추가 가능
|
||||
@@ -45,7 +47,7 @@ namespace UVC.UI.Window.PropertyWindow
|
||||
/// StageObject를 PropertyWindow 탭 데이터로 변환합니다.
|
||||
/// DisplayEquipmentProperties의 로직을 그대로 사용합니다.
|
||||
/// </summary>
|
||||
private static List<EntityTabData> GetTabsForStageObject(StageObjectManager.StageObject stageObject)
|
||||
private static List<EntityTabData> GetTabsForStageObject(StageObjectManager.StageObject stageObject, TerminalView? terminalView = null)
|
||||
{
|
||||
var tabs = new List<EntityTabData>();
|
||||
|
||||
@@ -56,9 +58,9 @@ namespace UVC.UI.Window.PropertyWindow
|
||||
tabs.Add(propertiesTab);
|
||||
|
||||
// ========================================
|
||||
// NETWORK 탭 생성 (나중에 구현)
|
||||
// NETWORK 탭 생성
|
||||
// ========================================
|
||||
var networkTab = CreateNetworkTab(stageObject);
|
||||
var networkTab = CreateNetworkTab(stageObject, terminalView);
|
||||
if (networkTab != null)
|
||||
tabs.Add(networkTab);
|
||||
|
||||
@@ -194,7 +196,7 @@ namespace UVC.UI.Window.PropertyWindow
|
||||
/// StageObject의 NETWORK 탭 데이터를 생성합니다.
|
||||
/// (나중에 구현)
|
||||
/// </summary>
|
||||
private static EntityTabData? CreateNetworkTab(StageObjectManager.StageObject stageObject)
|
||||
private static EntityTabData? CreateNetworkTab(StageObjectManager.StageObject stageObject, TerminalView? terminalView = null)
|
||||
{
|
||||
var properties = new List<IPropertyItem>();
|
||||
var propertyDict = new Dictionary<string, IPropertyItem>(); // Processor 초기화용
|
||||
@@ -274,6 +276,66 @@ private static EntityTabData? CreateNetworkTab(StageObjectManager.StageObject st
|
||||
processor.onComplete += () => { autoButton.ButtonText = "Run"; };
|
||||
processor.onCancel += () => { autoButton.ButtonText = "Run"; };
|
||||
|
||||
// TerminalView 바인딩 (터미널 스타일 로그)
|
||||
if (terminalView != null)
|
||||
{
|
||||
// propertyId → 표시명 매핑
|
||||
var stepNames = new Dictionary<string, string>
|
||||
{
|
||||
["get_entity_info_status"] = "Get Entity Info",
|
||||
["extract_network_info_status"] = "Extract Network Info",
|
||||
["connecting_status"] = "Connecting",
|
||||
["server_status"] = "Server",
|
||||
["port_status"] = "Port",
|
||||
["protocol_status"] = "Protocol",
|
||||
["server_status_check"] = "Status Check",
|
||||
["speed_status"] = "Speed"
|
||||
};
|
||||
|
||||
var tv = terminalView; // 클로저 캡처용 로컬 변수
|
||||
|
||||
processor.onStart += () =>
|
||||
{
|
||||
tv.AddLog("> Starting Twin Agent Auto Process...", Color.cyan);
|
||||
};
|
||||
|
||||
processor.onMessage += (propId, value) =>
|
||||
{
|
||||
// ProcessorId 자체의 상태 메시지는 무시 (onStart/onComplete에서 처리)
|
||||
if (propId == processor.ProcessorId) return;
|
||||
|
||||
string stepName = stepNames.TryGetValue(propId, out var name) ? name : propId;
|
||||
|
||||
if (value == "Done")
|
||||
{
|
||||
tv.AddLog($"> [{stepName}] Done", Color.green);
|
||||
}
|
||||
else if (value == "Canceled")
|
||||
{
|
||||
tv.AddLog($"> [{stepName}] Canceled", new Color(1f, 0.647f, 0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
tv.AddLog($"> [{stepName}] {value}");
|
||||
}
|
||||
};
|
||||
|
||||
processor.onComplete += () =>
|
||||
{
|
||||
tv.AddLog("> Process completed successfully", Color.green);
|
||||
};
|
||||
|
||||
processor.onCancel += () =>
|
||||
{
|
||||
tv.AddLog("> Process canceled", new Color(1f, 0.647f, 0f));
|
||||
};
|
||||
|
||||
processor.onReset += () =>
|
||||
{
|
||||
tv.Clear();
|
||||
};
|
||||
}
|
||||
|
||||
// 저장된 상태 복원
|
||||
if (processor.HasSavedState)
|
||||
{
|
||||
|
||||
@@ -33,6 +33,9 @@ private void Awake()
|
||||
{
|
||||
Debug.LogWarning("[PropertyWindow] EntityName GameObject를 찾을 수 없습니다.");
|
||||
}
|
||||
|
||||
// TerminalView 자동 검색 (비활성 자식 포함)
|
||||
_terminalView = GetComponentInChildren<TerminalView>(true);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
@@ -41,6 +44,11 @@ private void Awake()
|
||||
[SerializeField]
|
||||
private PropertyTabView _tabView;
|
||||
|
||||
/// <summary>
|
||||
/// NETWORK 탭 선택 시 표시되는 터미널 뷰
|
||||
/// </summary>
|
||||
private TerminalView? _terminalView;
|
||||
|
||||
/// <summary>
|
||||
/// Entity 이름을 표시하는 텍스트 컴포넌트
|
||||
/// </summary>
|
||||
@@ -277,7 +285,7 @@ private void Awake()
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 그룹을 제거합니다.
|
||||
/// 그룹을 제거합니다.2
|
||||
/// </summary>
|
||||
/// <param name="groupId">제거할 그룹의 ID</param>
|
||||
public void RemoveGroup(string groupId)
|
||||
@@ -547,6 +555,9 @@ private void Awake()
|
||||
// 탭의 속성 로드
|
||||
LoadProperties(tab.Properties);
|
||||
|
||||
// TerminalView 토글 (network 탭에서만 활성화)
|
||||
UpdateTerminalViewVisibility(tabId);
|
||||
|
||||
// 이벤트 발생
|
||||
TabChanged?.Invoke(this, new TabChangedEventArgs(oldTabId, tabId));
|
||||
|
||||
@@ -712,8 +723,9 @@ public void Open(UVC.Entity.Entity entity)
|
||||
// 3. 새 엔티티 설정
|
||||
_currentEntity = entity;
|
||||
|
||||
// 4. Adapter를 통해 Entity → 탭 데이터 변환
|
||||
var tabsData = EntityPropertyAdapter.GetTabsForEntity(entity);
|
||||
// 4. Adapter를 통해 Entity → 탭 데이터 변환 (TerminalView 전달)
|
||||
if (_terminalView != null) _terminalView.Clear();
|
||||
var tabsData = EntityPropertyAdapter.GetTabsForEntity(entity, _terminalView);
|
||||
|
||||
// 5. 탭 생성
|
||||
foreach (var tabData in tabsData)
|
||||
@@ -728,9 +740,19 @@ public void Open(UVC.Entity.Entity entity)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NETWORK 탭의 Server Type 변경 시 동적 가시성 제어를 설정합니다.
|
||||
/// 탭 ID에 따라 TerminalView 가시성을 업데이트합니다.
|
||||
/// network 탭에서는 TerminalView를 표시하고, 다른 탭에서는 숨깁니다.
|
||||
/// </summary>
|
||||
private void UpdateTerminalViewVisibility(string tabId)
|
||||
{
|
||||
if (_terminalView == null) return;
|
||||
|
||||
bool showTerminal = tabId == "network";
|
||||
if (showTerminal)
|
||||
_terminalView.Show();
|
||||
else
|
||||
_terminalView.Hide();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -803,6 +825,9 @@ public void Clear()
|
||||
// 현재 표시 중인 엔티티 초기화 (윈도우 닫혀도 프로세스는 계속 실행)
|
||||
_currentEntity = null;
|
||||
|
||||
// TerminalView 숨김
|
||||
_terminalView?.Hide();
|
||||
|
||||
EntriesCleared?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
// View 갱신하여 UI에서도 항목 제거
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#nullable enable
|
||||
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UVC.UI.Window.PropertyWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// 터미널 뷰의 개별 로그 라인 컴포넌트입니다.
|
||||
/// 각 로그 메시지를 표시하며, 나중에 아이콘/배경색 등 커스터마이징이 가능합니다.
|
||||
/// </summary>
|
||||
public class TerminalLogEntry : MonoBehaviour
|
||||
{
|
||||
private TMP_Text? _text;
|
||||
|
||||
public string Text => _text != null ? _text.text : string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 로그 라인을 초기화합니다. TerminalView에서 동적 생성 시 호출됩니다.
|
||||
/// </summary>
|
||||
public void Initialize(TMP_Text text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 로그 텍스트와 색상을 설정합니다.
|
||||
/// </summary>
|
||||
public void SetText(string message, Color color)
|
||||
{
|
||||
if (_text == null) return;
|
||||
_text.text = message;
|
||||
_text.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4153316854c2b5b498a614e54dc8ce13
|
||||
@@ -0,0 +1,244 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UVC.UI.Window.PropertyWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// PropertyWindow의 터미널 뷰입니다.
|
||||
/// NETWORK 탭 선택 시 활성화되며, Processor 진행 상황을 터미널 스타일 로그로 표시합니다.
|
||||
/// ScrollView + 개별 항목 방식으로, 각 로그 라인이 별도 GameObject입니다.
|
||||
/// </summary>
|
||||
public class TerminalView : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private ScrollRect? _scrollRect;
|
||||
[SerializeField] private Transform? _content;
|
||||
[SerializeField] private TMP_FontAsset? _font;
|
||||
|
||||
private const int MaxEntries = 200;
|
||||
private const float FontSize = 11f;
|
||||
|
||||
private readonly List<TerminalLogEntry> _entries = new List<TerminalLogEntry>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
EnsureUIHierarchy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ScrollRect와 Content 영역이 없으면 프로그래밍으로 생성합니다.
|
||||
/// </summary>
|
||||
private void EnsureUIHierarchy()
|
||||
{
|
||||
if (_scrollRect == null)
|
||||
_scrollRect = GetComponentInChildren<ScrollRect>(true);
|
||||
|
||||
if (_scrollRect != null && _content == null)
|
||||
_content = _scrollRect.content;
|
||||
|
||||
// ScrollRect가 없으면 자동 생성
|
||||
if (_scrollRect == null)
|
||||
{
|
||||
SetupScrollView();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 기존 ScrollRect가 있어도 Content 설정 보정
|
||||
EnsureContentSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기존 ScrollRect의 Content에 VerticalLayoutGroup, ContentSizeFitter가 없으면 추가하고
|
||||
/// 앵커/피봇을 상단 좌측으로 강제 설정합니다.
|
||||
/// </summary>
|
||||
private void EnsureContentSettings()
|
||||
{
|
||||
if (_content == null) return;
|
||||
|
||||
var contentRt = _content as RectTransform;
|
||||
if (contentRt == null) contentRt = _content.GetComponent<RectTransform>();
|
||||
if (contentRt == null) return;
|
||||
|
||||
// Content 앵커/피봇을 상단으로 고정
|
||||
contentRt.anchorMin = new Vector2(0, 1);
|
||||
contentRt.anchorMax = new Vector2(1, 1);
|
||||
contentRt.pivot = new Vector2(0, 1);
|
||||
|
||||
// VerticalLayoutGroup 보정
|
||||
var vlg = contentRt.GetComponent<VerticalLayoutGroup>();
|
||||
if (vlg == null)
|
||||
vlg = contentRt.gameObject.AddComponent<VerticalLayoutGroup>();
|
||||
vlg.childAlignment = TextAnchor.UpperLeft;
|
||||
vlg.childControlHeight = true;
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.spacing = 0f;
|
||||
vlg.padding = new RectOffset(6, 6, 2, 2);
|
||||
|
||||
// ContentSizeFitter 보정
|
||||
var csf = contentRt.GetComponent<ContentSizeFitter>();
|
||||
if (csf == null)
|
||||
csf = contentRt.gameObject.AddComponent<ContentSizeFitter>();
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
csf.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
}
|
||||
|
||||
private void SetupScrollView()
|
||||
{
|
||||
var rt = GetComponent<RectTransform>();
|
||||
if (rt == null)
|
||||
rt = gameObject.AddComponent<RectTransform>();
|
||||
|
||||
// Viewport
|
||||
var viewportGo = new GameObject("Viewport", typeof(RectTransform), typeof(Image), typeof(Mask));
|
||||
viewportGo.transform.SetParent(transform, false);
|
||||
var viewportRt = viewportGo.GetComponent<RectTransform>();
|
||||
viewportRt.anchorMin = Vector2.zero;
|
||||
viewportRt.anchorMax = Vector2.one;
|
||||
viewportRt.sizeDelta = Vector2.zero;
|
||||
viewportRt.offsetMin = Vector2.zero;
|
||||
viewportRt.offsetMax = Vector2.zero;
|
||||
var viewportImage = viewportGo.GetComponent<Image>();
|
||||
viewportImage.color = new Color(0, 0, 0, 0.01f); // 거의 투명하지만 Mask 동작용
|
||||
var mask = viewportGo.GetComponent<Mask>();
|
||||
mask.showMaskGraphic = false;
|
||||
|
||||
// Content
|
||||
var contentGo = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
|
||||
contentGo.transform.SetParent(viewportGo.transform, false);
|
||||
var contentRt = contentGo.GetComponent<RectTransform>();
|
||||
contentRt.anchorMin = new Vector2(0, 1);
|
||||
contentRt.anchorMax = new Vector2(1, 1);
|
||||
contentRt.pivot = new Vector2(0, 1);
|
||||
contentRt.sizeDelta = new Vector2(0, 0);
|
||||
|
||||
var vlg = contentGo.GetComponent<VerticalLayoutGroup>();
|
||||
vlg.childAlignment = TextAnchor.UpperLeft;
|
||||
vlg.childControlHeight = true;
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.spacing = 0f;
|
||||
vlg.padding = new RectOffset(6, 6, 2, 2);
|
||||
|
||||
var csf = contentGo.GetComponent<ContentSizeFitter>();
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
csf.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
_content = contentRt;
|
||||
|
||||
// ScrollRect
|
||||
_scrollRect = gameObject.AddComponent<ScrollRect>();
|
||||
_scrollRect.viewport = viewportRt;
|
||||
_scrollRect.content = contentRt;
|
||||
_scrollRect.horizontal = false;
|
||||
_scrollRect.vertical = true;
|
||||
_scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
_scrollRect.scrollSensitivity = 20f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 터미널에 로그 라인을 추가합니다. 타임스탬프가 자동으로 붙습니다.
|
||||
/// </summary>
|
||||
/// <param name="message">로그 메시지 (타임스탬프 제외)</param>
|
||||
/// <param name="color">텍스트 색상 (null이면 White)</param>
|
||||
public void AddLog(string message, Color? color = null)
|
||||
{
|
||||
if (_content == null) return;
|
||||
|
||||
string timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
string fullMessage = $"[{timestamp}] {message}";
|
||||
|
||||
// 최대 라인 수 초과 시 가장 오래된 항목 제거
|
||||
while (_entries.Count >= MaxEntries)
|
||||
{
|
||||
RemoveOldestEntry();
|
||||
}
|
||||
|
||||
var entry = CreateLogEntry(fullMessage, color ?? Color.white);
|
||||
_entries.Add(entry);
|
||||
|
||||
// 다음 프레임에 스크롤 (레이아웃 갱신 후)
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 로그를 제거합니다.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (entry != null && entry.gameObject != null)
|
||||
Destroy(entry.gameObject);
|
||||
}
|
||||
_entries.Clear();
|
||||
}
|
||||
|
||||
public void Show() => gameObject.SetActive(true);
|
||||
public void Hide() => gameObject.SetActive(false);
|
||||
public bool IsVisible => gameObject.activeSelf;
|
||||
|
||||
private TerminalLogEntry CreateLogEntry(string message, Color color)
|
||||
{
|
||||
var go = new GameObject("LogEntry", typeof(RectTransform), typeof(TerminalLogEntry), typeof(ContentSizeFitter));
|
||||
go.transform.SetParent(_content, false);
|
||||
|
||||
// ContentSizeFitter로 텍스트 높이에 맞게 자동 조절
|
||||
var csf = go.GetComponent<ContentSizeFitter>();
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
csf.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
// TextMeshProUGUI
|
||||
var tmp = go.AddComponent<TextMeshProUGUI>();
|
||||
tmp.fontSize = FontSize;
|
||||
tmp.alignment = TextAlignmentOptions.TopLeft;
|
||||
tmp.overflowMode = TextOverflowModes.Overflow;
|
||||
tmp.enableWordWrapping = true;
|
||||
tmp.richText = true;
|
||||
tmp.margin = new Vector4(0, 1, 0, 1); // 위아래 1px 여백
|
||||
|
||||
if (_font != null)
|
||||
tmp.font = _font;
|
||||
|
||||
// TerminalLogEntry 초기화
|
||||
var entry = go.GetComponent<TerminalLogEntry>();
|
||||
entry.Initialize(tmp);
|
||||
entry.SetText(message, color);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void RemoveOldestEntry()
|
||||
{
|
||||
if (_entries.Count == 0) return;
|
||||
var oldest = _entries[0];
|
||||
_entries.RemoveAt(0);
|
||||
if (oldest != null && oldest.gameObject != null)
|
||||
Destroy(oldest.gameObject);
|
||||
}
|
||||
|
||||
private async void ScrollToBottom()
|
||||
{
|
||||
if (_scrollRect == null) return;
|
||||
|
||||
// 레이아웃이 갱신될 때까지 1프레임 대기
|
||||
await Cysharp.Threading.Tasks.UniTask.Yield();
|
||||
|
||||
if (_scrollRect == null || _scrollRect.content == null || _scrollRect.viewport == null)
|
||||
return;
|
||||
|
||||
// 콘텐츠가 뷰포트보다 클 때만 하단 스크롤 (적을 때는 상단 유지)
|
||||
float contentHeight = _scrollRect.content.rect.height;
|
||||
float viewportHeight = _scrollRect.viewport.rect.height;
|
||||
if (contentHeight > viewportHeight)
|
||||
_scrollRect.verticalNormalizedPosition = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2bd83bb433a1e043b39643c011b6cc0
|
||||
@@ -346,10 +346,22 @@ PrefabInstance:
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1205322734457541541, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1205322734457541541, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1602519165350808158, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: PropertyWindow
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4790256706883938101, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_ChildControlWidth
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7125265927081152491, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
|
||||
36
Assets/Scripts/Camera/CameraRoute.cs
Normal file
36
Assets/Scripts/Camera/CameraRoute.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
|
||||
public int PointCount => transform.childCount;
|
||||
|
||||
public (Vector3 position, CameraRoutePoint point) GetWaypoint(int index)
|
||||
{
|
||||
var child = transform.GetChild(index);
|
||||
return (child.position, child.GetComponent<CameraRoutePoint>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,8 @@ namespace OCTOPUS_TWIN
|
||||
private bool isZoomOperation;
|
||||
|
||||
public bool Enable;
|
||||
public bool IsRouteActive => routeSequence != null && routeSequence.IsActive();
|
||||
private Sequence routeSequence;
|
||||
|
||||
public bool IsClickUI
|
||||
{
|
||||
@@ -148,11 +150,11 @@ namespace OCTOPUS_TWIN
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
//UI 위에서 카메라 움직임 제한
|
||||
//UI <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ī<><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
if (IsClickUI || IsOnTheUI)
|
||||
return;
|
||||
|
||||
//라이브러리에서 오브젝트 배치시 카메라 움직임 제한
|
||||
//<EFBFBD><EFBFBD><EFBFBD>̺귯<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ <20><>ġ<EFBFBD><C4A1> ī<><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
if (!Enable)
|
||||
{
|
||||
return;
|
||||
@@ -298,7 +300,7 @@ namespace OCTOPUS_TWIN
|
||||
{
|
||||
camera.orthographic = false;
|
||||
|
||||
currentElevation = perspectiveState.elevation; //90 안 섞임
|
||||
currentElevation = perspectiveState.elevation; //90 <EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
currentDistance = perspectiveState.distance;
|
||||
currentAzimuth = perspectiveState.azimuth;
|
||||
|
||||
@@ -339,7 +341,7 @@ namespace OCTOPUS_TWIN
|
||||
break;
|
||||
|
||||
case ViewMode.TopView:
|
||||
orthoState.elevation = 90f; // 90 or 고정값
|
||||
orthoState.elevation = 90f; // 90 or <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
orthoState.distance = 60f;
|
||||
orthoState.azimuth = 0f;
|
||||
orthoState.pivotPosition = Vector3.zero;
|
||||
@@ -352,18 +354,64 @@ namespace OCTOPUS_TWIN
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void AnimateToState(Vector3 pivotPosition, Vector3 eulerAngles, float distance, float duration = 0.4f)
|
||||
public void SetRoute(CameraRoute route)
|
||||
{
|
||||
// 애니메이션 중에는 마우스 입력 비활성화
|
||||
StopRoute();
|
||||
Enable = false;
|
||||
|
||||
// DoTween을 사용하여 컨트롤러의 상태 값들을 부드럽게 변경
|
||||
routeSequence = DOTween.Sequence();
|
||||
|
||||
for (int i = 0; i < route.PointCount; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (route.loop)
|
||||
routeSequence.SetLoops(-1, LoopType.Restart);
|
||||
|
||||
routeSequence.OnUpdate(LastPositioning);
|
||||
routeSequence.OnKill(() => Enable = true);
|
||||
}
|
||||
|
||||
public void StopRoute()
|
||||
{
|
||||
if (routeSequence != null && routeSequence.IsActive())
|
||||
{
|
||||
routeSequence.Kill();
|
||||
routeSequence = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void AnimateToState(Vector3 pivotPosition, Vector3 eulerAngles, float distance, float duration = 0.4f)
|
||||
{
|
||||
// <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(() => {
|
||||
// 애니메이션이 끝나면 마우스 입력을 다시 활성화
|
||||
// <EFBFBD>ִϸ<EFBFBD><EFBFBD>̼<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>콺 <20>Է<EFBFBD><D4B7><EFBFBD> <20>ٽ<EFBFBD> Ȱ<><C8B0>ȭ
|
||||
Enable = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ShortcutConfigurator : MonoBehaviour
|
||||
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>Ű - SelectionManager<65><72> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
shortcutManager.RegisterToolShortcut("select", () =>
|
||||
{
|
||||
//if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Select);
|
||||
if (selectionManager != null) selectionManager.Gizmo.SetActiveTool(TransformToolType.Select);
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("move", () =>
|
||||
|
||||
25
OCTOPUS_TWIN-Demo.slnx
Normal file
25
OCTOPUS_TWIN-Demo.slnx
Normal file
@@ -0,0 +1,25 @@
|
||||
<Solution>
|
||||
<Project Path="Assembly-CSharp-firstpass.csproj" />
|
||||
<Project Path="Assembly-CSharp.csproj" />
|
||||
<Project Path="com.Tivadar.Best.HTTP.csproj" />
|
||||
<Project Path="EPO.csproj" />
|
||||
<Project Path="GraphAndChart.csproj" />
|
||||
<Project Path="IngameDebugConsole.Runtime.csproj" />
|
||||
<Project Path="AssetUsageDetector.Editor.csproj" />
|
||||
<Project Path="RenderHeads.AVProMovieCapture.Demos.csproj" />
|
||||
<Project Path="com.Tivadar.Best.MQTT.csproj" />
|
||||
<Project Path="Assembly-CSharp-Editor-firstpass.csproj" />
|
||||
<Project Path="EPODemo.csproj" />
|
||||
<Project Path="RenderHeads.AVProMovieCapture.Runtime.csproj" />
|
||||
<Project Path="RenderHeads.AVProMovieCapture.Editor.csproj" />
|
||||
<Project Path="GLTFExporter.Runtime.csproj" />
|
||||
<Project Path="com.Tivadar.Best.WebSockets.csproj" />
|
||||
<Project Path="Assembly-CSharp-Editor.csproj" />
|
||||
<Project Path="EPOUtilities.csproj" />
|
||||
<Project Path="EPOEditor.csproj" />
|
||||
<Project Path="SimpleFileBrowser.Runtime.csproj" />
|
||||
<Project Path="EPOURP.csproj" />
|
||||
<Project Path="com.Tivadar.Best.HTTP.Profiler.Editor.csproj" />
|
||||
<Project Path="IngameDebugConsole.Editor.csproj" />
|
||||
<Project Path="EPOHDRP.csproj" />
|
||||
</Solution>
|
||||
Reference in New Issue
Block a user