198 lines
6.4 KiB
C#
198 lines
6.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
namespace UVC.UI.Buttons
|
|
{
|
|
/// <summary>
|
|
/// Unity 기본 Toggle을 상속하지 않고 직접 토글 기능을 구현한 이미지 토글.
|
|
/// isOn 상태에 따라 targetImage의 Sprite를 교체합니다.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <![CDATA[
|
|
/// using UnityEngine;
|
|
/// using UVC.UI.Buttons; // ImageToggle 네임스페이스
|
|
/// using UnityEngine.UI;
|
|
///
|
|
/// public class ImageToggleSample : MonoBehaviour
|
|
/// {
|
|
/// [SerializeField] private ImageToggle imageToggle; // Hierarchy에 있는 ImageToggle 할당
|
|
/// [SerializeField] private Image targetImage; // Sprite를 교체할 Image
|
|
/// [SerializeField] private Sprite onSprite;
|
|
/// [SerializeField] private Sprite offSprite;
|
|
/// [SerializeField] private Text stateText; // 상태 출력용 (선택)
|
|
///
|
|
/// void Awake()
|
|
/// {
|
|
/// // 런타임 동적 생성도 가능
|
|
/// // var go = new GameObject("DynamicImageToggle", typeof(RectTransform));
|
|
/// // imageToggle = go.AddComponent<ImageToggle>();
|
|
/// }
|
|
///
|
|
/// void Start()
|
|
/// {
|
|
/// // 필드 연결
|
|
/// imageToggle.SetIsOnWithoutNotify(false); // 초기 상태 강제 설정(이벤트 없이)
|
|
///
|
|
/// // targetImage 및 Sprite 지정
|
|
/// var so = new SerializedObject(imageToggle); // (직접 참조 가능하지만 예시로 표시)
|
|
/// imageToggle.GetType(); // 의미 없는 호출(예시 컴파일 방지 목적) -> 실제 코드는 제거 가능
|
|
/// imageToggle.gameObject.name = "SampleImageToggle";
|
|
/// // 에디터에서는 인스펙터에서 설정하는 것이 일반적
|
|
///
|
|
/// // 리스너 등록
|
|
/// imageToggle.OnValueChanged.AddListener(OnToggleChanged);
|
|
///
|
|
/// // 초기 텍스트 표시
|
|
/// if (stateText != null)
|
|
/// stateText.text = imageToggle.isOn ? "ON" : "OFF";
|
|
/// }
|
|
/// }
|
|
///
|
|
/// private void OnToggleChanged(bool value)
|
|
/// {
|
|
/// if (stateText != null)
|
|
/// stateText.text = value ? "ON" : "OFF";
|
|
/// }
|
|
///
|
|
/// // 외부에서 호출하여 상태를 강제로 변경하는 메서드 예시
|
|
/// public void ForceOn()
|
|
/// {
|
|
/// imageToggle.isOn = true;
|
|
/// }
|
|
/// }
|
|
/// ]]>
|
|
/// </code>
|
|
/// </example>
|
|
public class ImageToggle : Selectable, IPointerClickHandler, ISubmitHandler
|
|
{
|
|
#region 이벤트 정의
|
|
/// <summary>
|
|
/// 토글 값 변경 시(bool) 호출되는 UnityEvent.
|
|
/// </summary>
|
|
[System.Serializable]
|
|
public class ImageToggleEvent : UnityEvent<bool> { }
|
|
#endregion
|
|
|
|
[Header("Sprite 교체 대상")]
|
|
[Tooltip("Sprite를 교체할 대상 Image 컴포넌트")]
|
|
[SerializeField] private Image targetImage;
|
|
|
|
[Header("상태별 Sprite")]
|
|
[Tooltip("토글이 켜짐(isOn=true) 상태일 때 사용할 Sprite")]
|
|
[SerializeField] private Sprite onSprite;
|
|
[Tooltip("토글이 꺼짐(isOn=false) 상태일 때 사용할 Sprite")]
|
|
[SerializeField] private Sprite offSprite;
|
|
|
|
[Header("이벤트")]
|
|
[Tooltip("토글 값이 변경될 때 호출되는 이벤트")]
|
|
public ImageToggleEvent OnValueChanged = new ImageToggleEvent();
|
|
|
|
[Header("초기 상태")]
|
|
[Tooltip("시작 시 토글이 켜짐 상태인지 여부")]
|
|
[SerializeField] private bool m_IsOn = false;
|
|
|
|
/// <summary>
|
|
/// 현재 토글 상태(읽기/쓰기). 값을 설정하면 시각과 이벤트를 갱신합니다.
|
|
/// </summary>
|
|
public bool isOn
|
|
{
|
|
get => m_IsOn;
|
|
set => Set(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// isOn 값을 이벤트 없이 변경합니다.
|
|
/// </summary>
|
|
public void SetIsOnWithoutNotify(bool value)
|
|
{
|
|
Set(value, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// isOn 값을 이벤트 없이 강제로 설정하고 시각을 업데이트합니다.
|
|
/// 풀에서 재사용 시 동일 값이어도 시각 갱신이 필요할 때 사용합니다.
|
|
/// </summary>
|
|
public void ForceSetIsOnWithoutNotify(bool value)
|
|
{
|
|
m_IsOn = value;
|
|
UpdateVisual();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 내부 설정 로직. 상태가 변경되면 비주얼과 콜백을 처리합니다.
|
|
/// </summary>
|
|
private void Set(bool value, bool sendCallback = true)
|
|
{
|
|
if (m_IsOn == value) // 동일 값이면 무시
|
|
return;
|
|
m_IsOn = value;
|
|
UpdateVisual();
|
|
if (sendCallback)
|
|
{
|
|
OnValueChanged.Invoke(m_IsOn);
|
|
}
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
UpdateVisual();
|
|
}
|
|
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
UpdateVisual();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
protected override void OnValidate()
|
|
{
|
|
base.OnValidate();
|
|
UpdateVisual();
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// 현재 isOn 상태에 맞게 targetImage Sprite를 교체.
|
|
/// </summary>
|
|
private void UpdateVisual()
|
|
{
|
|
if (targetImage == null)
|
|
return;
|
|
targetImage.sprite = m_IsOn ? onSprite : offSprite;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 토글 상태를 반전시킵니다.
|
|
/// </summary>
|
|
private void Toggle()
|
|
{
|
|
if (!IsActive() || !IsInteractable())
|
|
return;
|
|
isOn = !isOn; // Set 통해 처리
|
|
}
|
|
|
|
/// <summary>
|
|
/// 마우스 클릭 처리(왼쪽 버튼).
|
|
/// </summary>
|
|
public void OnPointerClick(PointerEventData eventData)
|
|
{
|
|
if (eventData.button != PointerEventData.InputButton.Left)
|
|
return;
|
|
Toggle();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 키보드/게임패드 Submit 처리.
|
|
/// </summary>
|
|
public void OnSubmit(BaseEventData eventData)
|
|
{
|
|
Toggle();
|
|
}
|
|
}
|
|
}
|