Skip to content

Instantly share code, notes, and snippets.

@nekomimi-daimao
Last active April 22, 2024 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nekomimi-daimao/7b29fbb6f5bda1bf71791412f35fa7ea to your computer and use it in GitHub Desktop.
Save nekomimi-daimao/7b29fbb6f5bda1bf71791412f35fa7ea to your computer and use it in GitHub Desktop.
ButtonExtended
// https://gist.github.com/nekomimi-daimao/7b29fbb6f5bda1bf71791412f35fa7ea
using System;
using UniRx;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Nekomimi.Daimao
{
[RequireComponent(typeof(CanvasGroup))]
public class ButtonExtended : MonoBehaviour,
IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler, IDragHandler
{
private readonly BoolReactiveProperty _pointerIn = new(false);
/// <summary>
/// ポインターのイン/アウト.
/// </summary>
public IReadOnlyReactiveProperty<bool> PointerIn => _pointerIn;
/// <summary>
/// 有効/無効の切り替え.
/// </summary>
public readonly BoolReactiveProperty Interactable = new(true);
private readonly Subject<float> _onClick = new();
/// <summary>
/// クリックイベント.
/// 押されていた間の秒数も同時に通知される.
/// </summary>
public IObservable<float> OnClick => _onClick;
private IDisposable _disposablePressed;
private readonly FloatReactiveProperty _pressedSecond = new(0f);
/// <summary>
/// 押され続けている秒数.
/// 押されている間は<see cref="Observable.EveryUpdate"/>の間隔で更新される.
/// </summary>
public IReadOnlyReactiveProperty<float> PressedSecond => _pressedSecond;
#region IPointerCallbacks
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
Release();
if (!Interactable.Value)
{
return;
}
_disposablePressed = Observable.EveryUpdate()
.Select(_ => Time.deltaTime)
.TakeUntilDisable(this)
.Subscribe(f => _pressedSecond.Value += f);
}
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
if (PointerIn.Value && Interactable.Value)
{
_onClick.OnNext(PressedSecond.Value);
}
Release();
}
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
{
_pointerIn.Value = true;
}
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{
_pointerIn.Value = false;
Release();
}
void IDragHandler.OnDrag(PointerEventData eventData)
{
// https://discussions.unity.com/t/mouse-drag-element-inside-scrollrect-throws-pointerup-event/150840/4
}
#endregion
private void Release()
{
_disposablePressed?.Dispose();
_disposablePressed = null;
_pressedSecond.Value = 0f;
}
public virtual void OnStart()
{
}
[SerializeField]
public CanvasGroup canvasGroup;
private readonly CompositeDisposable _compositeDisposable = new();
private void Awake()
{
_compositeDisposable.Add(_pointerIn);
_compositeDisposable.Add(Interactable);
_compositeDisposable.Add(_onClick);
_compositeDisposable.Add(_pressedSecond);
_compositeDisposable.AddTo(this);
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
}
}
private void Start()
{
OnStart();
}
#if UNITY_EDITOR
private void Reset()
{
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
}
}
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment