Part of these game systems:
beginner Touch & Mobile

Touch Button

Mobile-friendly UI button with press, release, and hold events plus visual feedback. Includes configurable cooldown and hold duration tracking for responsive touch controls.

Unity 2022.3+ · 3.1 KB · TouchButton.cs

How to Use

1

Create a UI Button inside a Canvas with an EventSystem in the scene

2

Attach the TouchButton script to the Button GameObject

3

Configure cooldown time and hold threshold in the inspector

4

Wire up onPressed, onReleased, or onHoldStart events to your game actions

5

Adjust pressedScale for visual press feedback

Source Code

TouchButton.cs
C#
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class TouchButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    [Header("Button Settings")]
    [SerializeField] private float cooldownTime = 0.1f;
    [SerializeField] private float holdThreshold = 0.4f;

    [Header("Visual Feedback")]
    [SerializeField] private float pressedScale = 0.9f;
    [SerializeField] private float scaleSpeed = 10f;

    [Header("Events")]
    public UnityEvent onPressed;
    public UnityEvent onReleased;
    public UnityEvent onHoldStart;
    public UnityEvent<float> onHolding;

    public bool IsPressed { get; private set; }
    public bool IsHolding { get; private set; }
    public float HoldDuration { get; private set; }

    private RectTransform rectTransform;
    private Vector3 originalScale;
    private Vector3 targetScale;
    private float cooldownTimer;
    private bool holdFired;

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
        originalScale = rectTransform.localScale;
        targetScale = originalScale;
    }

    private void Update()
    {
        // Scale animation
        rectTransform.localScale = Vector3.Lerp(
            rectTransform.localScale, targetScale, Time.deltaTime * scaleSpeed);

        // Cooldown
        if (cooldownTimer > 0f)
            cooldownTimer -= Time.deltaTime;

        // Hold tracking
        if (IsPressed)
        {
            HoldDuration += Time.deltaTime;

            if (!holdFired && HoldDuration >= holdThreshold)
            {
                holdFired = true;
                IsHolding = true;
                onHoldStart?.Invoke();
            }

            if (IsHolding)
            {
                onHolding?.Invoke(HoldDuration);
            }
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if (cooldownTimer > 0f) return;

        IsPressed = true;
        HoldDuration = 0f;
        holdFired = false;
        IsHolding = false;
        targetScale = originalScale * pressedScale;

        onPressed?.Invoke();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (!IsPressed) return;

        IsPressed = false;
        IsHolding = false;
        targetScale = originalScale;
        cooldownTimer = cooldownTime;

        onReleased?.Invoke();
    }

    public void SetInteractable(bool interactable)
    {
        enabled = interactable;

        Image img = GetComponent<Image>();
        if (img != null)
        {
            Color c = img.color;
            c.a = interactable ? 1f : 0.5f;
            img.color = c;
        }

        if (!interactable && IsPressed)
        {
            IsPressed = false;
            IsHolding = false;
            targetScale = originalScale;
        }
    }

    public void ResetButton()
    {
        IsPressed = false;
        IsHolding = false;
        HoldDuration = 0f;
        holdFired = false;
        cooldownTimer = 0f;
        targetScale = originalScale;
        rectTransform.localScale = originalScale;
    }

    private void OnDisable()
    {
        ResetButton();
    }
}
Ready for more? Pinch-to-Zoom Camera Smooth pinch-to-zoom camera control for mobile devices supporting both orthographic and perspective cameras.