Part of these game systems:
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.
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
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();
}
}