OrderedDictionary<TKey, TValue> 추가
This commit is contained in:
@@ -4,7 +4,9 @@ using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using UVC.Extention;
|
||||
using UVC.Log;
|
||||
|
||||
namespace UVC.Data
|
||||
@@ -47,7 +49,7 @@ namespace UVC.Data
|
||||
/// // - password는 제외됨
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class DataObject : SortedDictionary<string, object>, IDataObject
|
||||
public class DataObject : OrderedDictionary<string, object>, IDataObject
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -103,11 +105,12 @@ namespace UVC.Data
|
||||
/// Dictionary로 데이터 객체를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">초기화에 사용할 Dictionary 객체</param>
|
||||
public DataObject(Dictionary<string, object> dictionary) : base(dictionary)
|
||||
public DataObject(Dictionary<string, object> dictionary) : base()
|
||||
{
|
||||
// 생성자에서 초기 속성들을 기존 속성으로 등록
|
||||
foreach (var key in dictionary.Keys)
|
||||
{
|
||||
Add(key, dictionary[key]);
|
||||
existingProperties.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.Editor
|
||||
{
|
||||
public abstract class EditableObject : MonoBehaviour, ISelectable
|
||||
{
|
||||
[field: SerializeField]
|
||||
public string ItemId { get; private set; }
|
||||
|
||||
public abstract void OnSelect();
|
||||
public abstract void OnDeselect();
|
||||
|
||||
public virtual void Initialize(string id)
|
||||
{
|
||||
this.ItemId = id;
|
||||
this.name = $"{GetType().Name}_{id}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0224adc9ffbf484eadece57e5583624
|
||||
@@ -1,22 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.Editor
|
||||
{
|
||||
public class EditableObject3D : EditableObject
|
||||
{
|
||||
// 3D 객체 고유의 속성 및 로직
|
||||
// 예: Material, Mesh 등
|
||||
|
||||
public override void OnSelect()
|
||||
{
|
||||
// 외곽선 표시 로직 호출
|
||||
Debug.Log($"{name} selected.");
|
||||
}
|
||||
|
||||
public override void OnDeselect()
|
||||
{
|
||||
// 외곽선 숨김 로직 호출
|
||||
Debug.Log($"{name} deselected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f0f7e0e4ee826a409789e44ac3584fb
|
||||
413
Assets/Scripts/UVC/Extention/OrderedDictionary.cs
Normal file
413
Assets/Scripts/UVC/Extention/OrderedDictionary.cs
Normal file
@@ -0,0 +1,413 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UVC.Extention
|
||||
{
|
||||
/// <summary>
|
||||
/// 키의 순서가 보장되는 스레드 안전한 제네릭 컬렉션을 나타냅니다.
|
||||
/// 항목이 추가된 순서대로 유지되며, 인덱스를 통해 접근할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">사전의 키 형식입니다.</typeparam>
|
||||
/// <typeparam name="TValue">사전의 값 형식입니다.</typeparam>
|
||||
/// <example>
|
||||
/// 다음은 OrderedDictionary의 사용 예제입니다.
|
||||
/// <code>
|
||||
/// // OrderedDictionary 인스턴스 생성
|
||||
/// var cityPopulation = new OrderedDictionary<string, int>();
|
||||
///
|
||||
/// // 데이터 추가 (순서대로 추가됨)
|
||||
/// cityPopulation.Add("서울", 9700000);
|
||||
/// cityPopulation.Add("부산", 3400000);
|
||||
/// cityPopulation.Add("인천", 3000000);
|
||||
///
|
||||
/// // 키를 이용한 값 접근
|
||||
/// Console.WriteLine($"서울 인구: {cityPopulation["서울"]}"); // 출력: 서울 인구: 9700000
|
||||
///
|
||||
/// // 인덱스를 이용한 값 접근 (순서 보장)
|
||||
/// Console.WriteLine($"첫 번째 추가된 도시의 인구: {cityPopulation.GetValueAt(0)}"); // 출력: 첫 번째 추가된 도시의 인구: 9700000
|
||||
///
|
||||
/// // 특정 위치에 데이터 삽입
|
||||
/// cityPopulation.Insert(1, "대구", 2400000); // "부산" 앞에 "대구" 삽입
|
||||
///
|
||||
/// // 추가된 순서대로 반복
|
||||
/// Console.WriteLine("\n현재 도시 목록 (순서 보장):");
|
||||
/// foreach(var city in cityPopulation)
|
||||
/// {
|
||||
/// Console.WriteLine($"- {city.Key}: {city.Value}");
|
||||
/// }
|
||||
/// // 출력:
|
||||
/// // - 서울: 9700000
|
||||
/// // - 대구: 2400000
|
||||
/// // - 부산: 3400000
|
||||
/// // - 인천: 3000000
|
||||
///
|
||||
/// // 항목 삭제
|
||||
/// cityPopulation.Remove("부산");
|
||||
///
|
||||
/// Console.WriteLine("\n'부산' 삭제 후 도시 목록:");
|
||||
/// foreach(var city in cityPopulation)
|
||||
/// {
|
||||
/// Console.WriteLine($"- {city.Key}: {city.Value}");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public partial class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly Dictionary<TKey, TValue> _dict;
|
||||
private readonly List<TKey> _keys;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// 비어 있고 기본 초기 용량을 가지며 키 형식에 대한 기본 같음 비교자를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
public OrderedDictionary()
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>();
|
||||
_keys = new List<TKey>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비어 있고 기본 초기 용량을 가지며 지정된 <see cref="IEqualityComparer{TKey}"/>를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="comparer">키를 비교할 때 사용할 <see cref="IEqualityComparer{TKey}"/> 구현 또는 키 형식에 대한 기본 <see cref="EqualityComparer{TKey}"/>를 사용하려면 null입니다.</param>
|
||||
public OrderedDictionary(IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>(comparer);
|
||||
_keys = new List<TKey>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비어 있고 지정된 초기 용량을 가지며 키 형식에 대한 기본 같음 비교자를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="capacity"><see cref="OrderedDictionary{TKey, TValue}"/>가 초기에 포함할 수 있는 요소의 수입니다.</param>
|
||||
public OrderedDictionary(int capacity)
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>(capacity);
|
||||
_keys = new List<TKey>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 비어 있고 지정된 초기 용량을 가지며 지정된 <see cref="IEqualityComparer{TKey}"/>를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="capacity"><see cref="OrderedDictionary{TKey, TValue}"/>가 초기에 포함할 수 있는 요소의 수입니다.</param>
|
||||
/// <param name="comparer">키를 비교할 때 사용할 <see cref="IEqualityComparer{TKey}"/> 구현 또는 키 형식에 대한 기본 <see cref="EqualityComparer{TKey}"/>를 사용하려면 null입니다.</param>
|
||||
public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>(capacity, comparer);
|
||||
_keys = new List<TKey>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 <see cref="IDictionary{TKey, TValue}"/>에서 복사한 요소를 포함하고 키 형식에 대한 기본 같음 비교자를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">요소가 새 <see cref="OrderedDictionary{TKey, TValue}"/>에 복사되는 <see cref="IDictionary{TKey, TValue}"/>입니다.</param>
|
||||
public OrderedDictionary(IDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>(dictionary);
|
||||
_keys = new List<TKey>(dictionary.Keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 <see cref="IDictionary{TKey, TValue}"/>에서 복사한 요소를 포함하고 지정된 <see cref="IEqualityComparer{TKey}"/>를 사용하는 <see cref="OrderedDictionary{TKey, TValue}"/> 클래스의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">요소가 새 <see cref="OrderedDictionary{TKey, TValue}"/>에 복사되는 <see cref="IDictionary{TKey, TValue}"/>입니다.</param>
|
||||
/// <param name="comparer">키를 비교할 때 사용할 <see cref="IEqualityComparer{TKey}"/> 구현 또는 키 형식에 대한 기본 <see cref="EqualityComparer{TKey}"/>를 사용하려면 null입니다.</param>
|
||||
public OrderedDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
_dict = new Dictionary<TKey, TValue>(dictionary, comparer);
|
||||
_keys = new List<TKey>(dictionary.Keys);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary<TKey, TValue> Members
|
||||
|
||||
/// <summary>
|
||||
/// 사전에 지정된 키와 값을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="key">추가할 요소의 키입니다.</param>
|
||||
/// <param name="value">추가할 요소의 값입니다.</param>
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_dict.ContainsKey(key))
|
||||
throw new ArgumentException("이미 존재하는 키입니다.");
|
||||
_keys.Add(key);
|
||||
_dict.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전에 지정된 키가 포함되어 있는지 확인합니다.
|
||||
/// </summary>
|
||||
/// <param name="key">찾을 키입니다.</param>
|
||||
/// <returns>사전에 지정된 키를 가진 요소가 있으면 true이고, 그렇지 않으면 false입니다.</returns>
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dict.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전의 키를 순서대로 포함하는 컬렉션을 가져옵니다.
|
||||
/// </summary>
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _keys.AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전에서 지정된 키를 가진 요소를 제거합니다.
|
||||
/// <para>참고: 이 작업은 내부 리스트에서 키를 찾아 제거하므로 O(n)의 시간 복잡도를 가질 수 있습니다.</para>
|
||||
/// </summary>
|
||||
/// <param name="key">제거할 요소의 키입니다.</param>
|
||||
/// <returns>요소를 성공적으로 찾아 제거했으면 true이고, 그렇지 않으면 false입니다.</returns>
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_dict.Remove(key))
|
||||
{
|
||||
_keys.Remove(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 키와 연결된 값을 가져옵니다.
|
||||
/// </summary>
|
||||
/// <param name="key">가져올 값의 키입니다.</param>
|
||||
/// <param name="value">이 메서드가 반환될 때, 키가 있으면 해당 키와 연결된 값을 포함하고, 그렇지 않으면 value 매개 변수의 형식에 대한 기본값을 포함합니다.</param>
|
||||
/// <returns>사전에 지정된 키를 가진 요소가 있으면 true이고, 그렇지 않으면 false입니다.</returns>
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dict.TryGetValue(key, out value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전의 값을 순서대로 포함하는 컬렉션을 가져옵니다.
|
||||
/// </summary>
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
// ToList()를 호출하여 즉시 평가된 읽기 전용 컬렉션을 반환합니다.
|
||||
// 이는 스레드 안정성을 위해 중요합니다.
|
||||
return _keys.Select(key => _dict[key]).ToList().AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 키와 연결된 값을 가져오거나 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="key">가져오거나 설정할 요소의 키입니다.</param>
|
||||
/// <returns>지정된 키와 연결된 값입니다.</returns>
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dict[key];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_dict.ContainsKey(key))
|
||||
{
|
||||
_keys.Add(key);
|
||||
}
|
||||
_dict[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<KeyValuePair<TKey, TValue>> Members
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전에서 모든 키와 값을 제거합니다.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_dict.Clear();
|
||||
_keys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return ((ICollection<KeyValuePair<TKey, TValue>>)_dict).Contains(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
if (array.Length - arrayIndex < Count) throw new ArgumentException("대상 배열이 충분히 크지 않습니다.");
|
||||
|
||||
foreach (var key in _keys)
|
||||
{
|
||||
array[arrayIndex++] = new KeyValuePair<TKey, TValue>(key, _dict[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전에 포함된 키/값 쌍의 수를 가져옵니다.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dict.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 사전이 읽기 전용인지 여부를 나타내는 값을 가져옵니다.
|
||||
/// </summary>
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (((ICollection<KeyValuePair<TKey, TValue>>)_dict).Contains(item))
|
||||
{
|
||||
return Remove(item.Key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<KeyValuePair<TKey, TValue>> Members
|
||||
|
||||
/// <summary>
|
||||
/// 사전을 반복하는 열거자를 반환합니다.
|
||||
/// <para>주의: 열거가 진행되는 동안 컬렉션이 다른 스레드에서 수정되면 예외가 발생할 수 있습니다. 스레드 안전한 열거를 위해서는 컬렉션을 복사하거나 lock을 사용해야 합니다.</para>
|
||||
/// </summary>
|
||||
/// <returns>사전을 반복하는 데 사용할 수 있는 <see cref="IEnumerator{T}"/>입니다.</returns>
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
// GetEnumerator는 스레드 안정성을 위해 컬렉션의 복사본을 만들어 순회하는 것이 일반적입니다.
|
||||
List<KeyValuePair<TKey, TValue>> listCopy;
|
||||
lock (_lock)
|
||||
{
|
||||
listCopy = new List<KeyValuePair<TKey, TValue>>(Count);
|
||||
foreach (var key in _keys)
|
||||
{
|
||||
listCopy.Add(new KeyValuePair<TKey, TValue>(key, _dict[key]));
|
||||
}
|
||||
}
|
||||
return listCopy.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OrderedDictionary Specific Members
|
||||
|
||||
/// <summary>
|
||||
/// 사전의 지정된 인덱스에 키/값 쌍을 삽입합니다.
|
||||
/// <para>참고: 이 작업은 내부 리스트에 요소를 삽입하므로 O(n)의 시간 복잡도를 가질 수 있습니다.</para>
|
||||
/// </summary>
|
||||
/// <param name="index">키/값 쌍을 삽입할 인덱스(0부터 시작)입니다.</param>
|
||||
/// <param name="key">삽입할 요소의 키입니다.</param>
|
||||
/// <param name="value">삽입할 요소의 값입니다.</param>
|
||||
public void Insert(int index, TKey key, TValue value)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_dict.ContainsKey(key))
|
||||
throw new ArgumentException("이미 존재하는 키입니다.");
|
||||
_keys.Insert(index, key);
|
||||
_dict.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사전의 지정된 인덱스에 있는 요소를 제거합니다.
|
||||
/// <para>참고: 이 작업은 내부 리스트에서 요소를 제거하므로 O(n)의 시간 복잡도를 가질 수 있습니다.</para>
|
||||
/// </summary>
|
||||
/// <param name="index">제거할 요소의 인덱스(0부터 시작)입니다.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (index < 0 || index >= _keys.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
TKey key = _keys[index];
|
||||
_keys.RemoveAt(index);
|
||||
_dict.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 인덱스의 값을 가져옵니다.
|
||||
/// </summary>
|
||||
/// <param name="index">가져올 값의 인덱스</param>
|
||||
/// <returns>지정된 인덱스에 있는 항목의 값</returns>
|
||||
public TValue GetValueAt(int index)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dict[_keys[index]];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/Extention/OrderedDictionary.cs.meta
Normal file
2
Assets/Scripts/UVC/Extention/OrderedDictionary.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe6fb83b61db8124a88f9d74891f26e9
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace UVC.Editor
|
||||
{
|
||||
public interface ISelectable
|
||||
{
|
||||
void OnSelect();
|
||||
void OnDeselect();
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9156b89e72147142831eb6d58934e49
|
||||
@@ -1,77 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UVC.Editor
|
||||
{
|
||||
public class InteractionController : MonoBehaviour
|
||||
{
|
||||
public static event System.Action<EditableObject> OnObjectSelected;
|
||||
public static event System.Action OnBackgroundClicked;
|
||||
|
||||
private Camera _mainCamera;
|
||||
private EditableObject _selectedObject;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
// UI 위에서 클릭했는지 먼저 확인
|
||||
if (EventSystem.current.IsPointerOverGameObject())
|
||||
{
|
||||
return; // UI 클릭 시 월드 객체 선택 방지
|
||||
}
|
||||
|
||||
HandleSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelection()
|
||||
{
|
||||
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
|
||||
// 3D 객체 선택 (Physics Raycast)
|
||||
if (Physics.Raycast(ray, out RaycastHit hit))
|
||||
{
|
||||
// EditableObject 컴포넌트를 가진 객체인지 확인
|
||||
if (hit.collider.TryGetComponent<EditableObject>(out var target))
|
||||
{
|
||||
SetSelectedObject(target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2D 객체 선택 (Physics2D Raycast) - 필요 시 카메라 설정에 따라 추가
|
||||
// RaycastHit2D hit2D = Physics2D.GetRayIntersection(ray);
|
||||
// if (hit2D.collider != null && hit2D.collider.TryGetComponent<EditableObject>(out var target2D))
|
||||
// {
|
||||
// SetSelectedObject(target2D);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 아무것도 선택되지 않았을 경우
|
||||
SetSelectedObject(null);
|
||||
}
|
||||
|
||||
private void SetSelectedObject(EditableObject target)
|
||||
{
|
||||
if (_selectedObject == target) return;
|
||||
|
||||
_selectedObject?.OnDeselect();
|
||||
_selectedObject = target;
|
||||
_selectedObject?.OnSelect();
|
||||
|
||||
if (_selectedObject != null)
|
||||
{
|
||||
OnObjectSelected?.Invoke(_selectedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnBackgroundClicked?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f8a7b0a02822c942a8c2846d5a2432a
|
||||
119
Assets/Scripts/UVC/Object3d/FactoryObject.cs
Normal file
119
Assets/Scripts/UVC/Object3d/FactoryObject.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UVC.Extention;
|
||||
using UVC.UI.Info;
|
||||
|
||||
namespace UVC.Object3d
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FactoryObject : InteractiveObject
|
||||
{
|
||||
// InfoWindow 인스턴스에 대한 참조
|
||||
protected InfoWindow infoWindow;
|
||||
|
||||
protected Camera mainCamera;
|
||||
|
||||
protected FactoryObjectInfo? factoryObjectInfo;
|
||||
public FactoryObjectInfo? FactoryObjectInfo
|
||||
{
|
||||
get => factoryObjectInfo;
|
||||
set
|
||||
{
|
||||
factoryObjectInfo = value;
|
||||
if (value != null)
|
||||
{
|
||||
// 객체의 이름을 GameObject의 이름으로 설정합니다.
|
||||
gameObject.name = value.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Dictionary<string, object> data = new Dictionary<string, object>();
|
||||
|
||||
protected List<string>? dataOrderedMask;
|
||||
/// <summary>
|
||||
/// InfoWindow에 표시할 데이터의 순서와 항목을 지정하는 마스크입니다.
|
||||
/// </summary>
|
||||
public List<string>? DataOrderedMask
|
||||
{
|
||||
get => dataOrderedMask;
|
||||
set
|
||||
{
|
||||
dataOrderedMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
mainCamera = Camera.main;
|
||||
|
||||
// 씬에 있는 InfoWindow 인스턴스를 동적으로 찾습니다.
|
||||
// FindObjectOfType은 씬에서 해당 타입의 활성화된 첫 번째 객체를 반환합니다.
|
||||
infoWindow = InfoWindow.Create();
|
||||
|
||||
if (infoWindow == null)
|
||||
{
|
||||
Debug.LogError("씬에서 InfoWindow 컴포넌트를 찾을 수 없습니다. InfoWindow가 씬에 존재하고 활성화되어 있는지 확인해주세요.");
|
||||
enabled = false; // infoWindow가 없으면 이 스크립트를 비활성화합니다.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 포인터 클릭 이벤트를 처리하고 관련 데이터가 포함된 정보 창을 표시합니다.
|
||||
/// </summary>
|
||||
/// <remarks>이 메서드는 정보 창이 현재 표시되어 있는지, 그리고
|
||||
/// 유효한 데이터가 있는지 확인합니다. 데이터가 마스크를 사용하여 정렬된 경우 마스크된 데이터만 표시되고, 그렇지 않은 경우
|
||||
/// 사용 가능한 모든 데이터가 표시됩니다. 정보 창은 현재
|
||||
/// 변환을 기준으로 배치됩니다.</remarks>
|
||||
/// <param name="eventData">포인터 클릭과 관련된 이벤트 데이터입니다.</param>
|
||||
public override void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if(!infoWindow.IsVisible && data != null && data.Count > 0)
|
||||
{
|
||||
Dictionary<string, object> info = new Dictionary<string, object>();
|
||||
// dataOrderedMask가 설정되어 있으면 해당 순서대로 정보를 가져옵니다.
|
||||
if (dataOrderedMask != null && dataOrderedMask.Count > 0)
|
||||
{
|
||||
foreach (var key in dataOrderedMask)
|
||||
{
|
||||
if (data.ContainsKey(key))
|
||||
{
|
||||
info[key] = data[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// dataOrderedMask가 설정되어 있지 않으면 모든 데이터를 사용합니다.
|
||||
info = new Dictionary<string, object>(data);
|
||||
}
|
||||
infoWindow.Show(transform, info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 변경된 데이터만 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void UpdateData(IDictionary<string, object> data)
|
||||
{
|
||||
foreach (var kvp in data)
|
||||
{
|
||||
if (this.data.ContainsKey(kvp.Key))
|
||||
{
|
||||
this.data[kvp.Key] = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.data.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
51
Assets/Scripts/UVC/Object3d/FactoryObjectInfo.cs
Normal file
51
Assets/Scripts/UVC/Object3d/FactoryObjectInfo.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace UVC.Object3d
|
||||
{
|
||||
public class FactoryObjectInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 이름
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 아이디
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 위치
|
||||
/// </summary>
|
||||
public string Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 구역
|
||||
/// </summary>
|
||||
public string Area { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 층
|
||||
/// </summary>
|
||||
public string Floor { get; set; }
|
||||
|
||||
public FactoryObjectInfo(string name, string id, string position, string area, string floor)
|
||||
{
|
||||
Name = name;
|
||||
Id = id;
|
||||
Position = position;
|
||||
Area = area;
|
||||
Floor = floor;
|
||||
}
|
||||
|
||||
public bool Equals(FactoryObjectInfo other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
return Name == other.Name && Id == other.Id && Position == other.Position && Area == other.Area && Floor == other.Floor;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Name:{Name},Id:{Id},Position:{Position},Area:{Area},Floor:{Floor}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/Object3d/FactoryObjectInfo.cs.meta
Normal file
2
Assets/Scripts/UVC/Object3d/FactoryObjectInfo.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88a40c66f1e6dad4daa5a1339bd1a0bb
|
||||
@@ -1,55 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UVC.Extention;
|
||||
using UVC.UI.Info;
|
||||
|
||||
namespace UVC.Object3d
|
||||
{
|
||||
/// <summary>
|
||||
/// 사용자의 마우스 클릭 입력을 감지하고 3D 객체와 상호작용합니다.
|
||||
/// </summary>
|
||||
public class InteractionController : InteractiveObject
|
||||
{
|
||||
// InfoWindow 인스턴스에 대한 참조
|
||||
private InfoWindow infoWindow;
|
||||
|
||||
private Camera mainCamera;
|
||||
|
||||
private Dictionary<string, object>? infoData;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
mainCamera = Camera.main;
|
||||
|
||||
// 씬에 있는 InfoWindow 인스턴스를 동적으로 찾습니다.
|
||||
// FindObjectOfType은 씬에서 해당 타입의 활성화된 첫 번째 객체를 반환합니다.
|
||||
infoWindow = InfoWindow.Create();
|
||||
|
||||
if (infoWindow == null)
|
||||
{
|
||||
Debug.LogError("씬에서 InfoWindow 컴포넌트를 찾을 수 없습니다. InfoWindow가 씬에 존재하고 활성화되어 있는지 확인해주세요.");
|
||||
enabled = false; // infoWindow가 없으면 이 스크립트를 비활성화합니다.
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
Dictionary<string, object> info = new Dictionary<string, object>
|
||||
{
|
||||
{ "objectName", gameObject.name },
|
||||
{ "objectPosition", transform.position },
|
||||
{ "objectRotation", transform.rotation },
|
||||
{ "objectScale", transform.localScale }
|
||||
};
|
||||
// 변경 된 정보가 있을 때 클릭된 객체의 정보를 InfoWindow에 전달합니다.
|
||||
if (!infoWindow.IsVisible && (infoData != null && !infoData.IsSame(info)))
|
||||
{
|
||||
infoWindow.Show(transform, info);
|
||||
infoData = info;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.Editor
|
||||
{
|
||||
[RequireComponent(typeof(Renderer))]
|
||||
public class OutlineEffect : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Material _outlineMaterial;
|
||||
private Renderer _renderer;
|
||||
private bool _isOutlined = false;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_renderer = GetComponent<Renderer>();
|
||||
}
|
||||
|
||||
public void SetOutline(bool visible)
|
||||
{
|
||||
if (_isOutlined == visible) return;
|
||||
|
||||
_isOutlined = visible;
|
||||
var materials = _renderer.sharedMaterials.ToList();
|
||||
if (_isOutlined)
|
||||
{
|
||||
materials.Add(_outlineMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials.Remove(_outlineMaterial);
|
||||
}
|
||||
_renderer.materials = materials.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 575f56d1e1229a543a23502e94149f71
|
||||
Reference in New Issue
Block a user