DEV Community

Cover image for Creating of wave / ripple effect for buttons like in Material Design in Unity
Devs Daddy
Devs Daddy

Posted on

Creating of wave / ripple effect for buttons like in Material Design in Unity

Hey, everybody. In today's short tutorial I'd like to show you how to work with the built-in Unity UI (UGUI) event system on the example of creating a wave effect when you click on an element (whether it's a button or Image doesn't matter), like in Material Design


So, let's get started!
Let's make an universal component based on MonoBehaviour and IPointerClickHandler

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

// We need to Disallow Multiple Component for Performance Issue
[DisallowMultipleComponent]
public class UIRippleEffect : MonoBehaviour, IPointerClickHandler
{
    [Header("Ripple Setup")]
    public Sprite m_EffectSprite;     // Our Ripple Sprite
    public Color RippleColor;         // Ripple Color
    public float MaxPower = .25f;     // Max Opacity of Ripple (from 0 to 1)
    public float Duration = .25f;     // Duration of Ripple effect (in sec)

    // Our Internal Parameters
    private bool m_IsInitialized = false;  // Initialization Flag
    private RectMask2D m_RectMask;         // Rect Mask for Ripple

    // Here we Check our Effect Sprite and Setup Container
    private void Awake() {
        if (m_EffectSprite == null) {
            Debug.LogWarning("Failed to add ripple graphics. Not Ripple found.");
            return;
        }

        SetupRippleContainer();
    }

    // Here we add our mask for ripple effect
    private void SetupRippleContainer() {
        m_RectMask = gameObject.AddComponent<RectMask2D>();
        m_RectMask.padding = new Vector4(5, 5, 5, 5);
        m_RectMask.softness = new Vector2Int(20, 20);
        m_IsInitialized = true;
    }

    // This is our Click event based on IPointerClickHandler for Unity Event System
    public void OnPointerClick(PointerEventData pointerEventData) {
        if(!m_IsInitialized) return;
        GameObject rippleObject = new GameObject("_ripple_");
        LayoutElement crl = rippleObject.AddComponent<LayoutElement>();
        crl.ignoreLayout = true;

        Image currentRippleImage = rippleObject.AddComponent<Image>();
        currentRippleImage.sprite = m_EffectSprite;
        currentRippleImage.transform.SetAsLastSibling();
        currentRippleImage.transform.SetPositionAndRotation(pointerEventData.position, Quaternion.identity);
        currentRippleImage.transform.SetParent(transform);
        currentRippleImage.color = new Color(RippleColor.r, RippleColor.g, RippleColor.b, 0f);
        currentRippleImage.raycastTarget = false;
        StartCoroutine(AnimateRipple(rippleObject.GetComponent<RectTransform>(), currentRippleImage, () => {
            currentRippleImage = null;
            Destroy(rippleObject);
            StopCoroutine(nameof(AnimateRipple));
        }));
    }

    // Here we work with animation of single ripple
    private IEnumerator AnimateRipple(RectTransform rippleTransform, Image rippleImage, Action onComplete) {
        Vector2 initialSize = Vector2.zero;
        Vector2 targetSize = new Vector2(150,150);
        Color initialColor = new Color(RippleColor.r, RippleColor.g, RippleColor.b, MaxPower);
        Color targetColor = new Color(RippleColor.r, RippleColor.g, RippleColor.b, 0f);
        float elapsedTime = 0f;

        while (elapsedTime < Duration)
        {
            elapsedTime += Time.deltaTime;
            rippleTransform.sizeDelta = Vector2.Lerp(initialSize, targetSize, elapsedTime / Duration);
            rippleImage.color = Color.Lerp(initialColor, targetColor, elapsedTime / Duration);
            yield return null;
        }

        onComplete?.Invoke();
    }
}
Enter fullscreen mode Exit fullscreen mode

So, using standard Unity interfaces, we created a wave effect inside the mask created on our element (this can also be replaced with a shader-based effect for better performance) when clicked. It doesn't matter what type of element our UI will be - the main thing is that we can catch it with Raycast.

Do not forgot to setup your new component at UI:
UI Editor

You can practice more by adding new effects using hover/unhover and other UIs for that. Use the IPointerEnterHandler, IPointerExitHandler interfaces to do this.

Thanks for reading the article, I'll always be happy to discuss any projects with you and help you with your ideas on Unity.


My Discord | My Blog | My GitHub | Buy me a Beer

Top comments (0)