namespace UIWidgets { using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Serialization; using UnityEngine.UI; /// /// Ripple effect. /// [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(Graphic))] [AddComponentMenu("UI/New UI Widgets/Effects/Ripple Effect")] public class RippleEffect : UV1Effect, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler, IMaterialModifier, IMeshModifier { /// /// IDs of ripple shader properties. /// protected struct RippleShaderIDs : IEquatable { /// /// Start color ID. /// public readonly int StartColor; /// /// End color ID. /// public readonly int EndColor; /// /// Speed ID. /// public readonly int Speed; /// /// Max size ID. /// public readonly int MaxSize; /// /// Count ID. /// public readonly int Count; /// /// Ripple ID. /// public readonly int Ripple; private RippleShaderIDs(int startColor, int endColor, int speed, int maxSize, int count, int ripple) { StartColor = startColor; EndColor = endColor; Speed = speed; MaxSize = maxSize; Count = count; Ripple = ripple; } /// /// Get RippleShaderIDs instance. /// public static RippleShaderIDs Instance { get { return new RippleShaderIDs( Shader.PropertyToID("_RippleStartColor"), Shader.PropertyToID("_RippleEndColor"), Shader.PropertyToID("_RippleSpeed"), Shader.PropertyToID("_RippleMaxSize"), Shader.PropertyToID("_RippleCount"), Shader.PropertyToID("_Ripple")); } } /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { if (obj is RippleShaderIDs) { return Equals((RippleShaderIDs)obj); } return false; } /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public bool Equals(RippleShaderIDs other) { return StartColor == other.StartColor && EndColor == other.EndColor && Speed == other.Speed && MaxSize == other.MaxSize && Count == other.Count && Ripple == other.Ripple; } /// /// Hash function. /// /// A hash code for the current object. public override int GetHashCode() { return StartColor ^ EndColor ^ Speed ^ MaxSize ^ Count ^ Ripple; } /// /// Compare specified instances. /// /// Left instance. /// Right instances. /// true if the instances are equal; otherwise, false. public static bool operator ==(RippleShaderIDs left, RippleShaderIDs right) { return left.Equals(right); } /// /// Compare specified instances. /// /// Left instance. /// Right instances. /// true if the instances are now equal; otherwise, false. public static bool operator !=(RippleShaderIDs left, RippleShaderIDs right) { return !left.Equals(right); } } [SerializeField] [FormerlySerializedAs("color")] Color startColor = Color.white; /// /// Start color of the ripple. /// public Color StartColor { get { return startColor; } set { startColor = value; UpdateMaterial(); } } [SerializeField] Color endColor = Color.white; /// /// End color of the ripple. /// public Color EndColor { get { return endColor; } set { endColor = value; UpdateMaterial(); } } [SerializeField] float speed = 0.5f; /// /// Ripple speed. /// public float Speed { get { return speed; } set { speed = value; UpdateMaterial(); } } [SerializeField] [Range(0f, 1f)] float maxSize = 1f; /// /// Ripple size. /// public float MaxSize { get { return maxSize; } set { maxSize = value; UpdateMaterial(); } } /// /// Ripples data. /// [NonSerialized] protected List RipplesData; /// /// Max ripples count. /// protected static int MaxRipples = 10; /// /// Float values per ripple. /// protected static int FloatPerRipple = 3; /// /// Ripple shader ids. /// protected static RippleShaderIDs ShaderIDs = RippleShaderIDs.Instance; /// /// Remove oldest and dead ripples. /// protected void CleanRipples() { // remove oldest ripple if (RipplesData.Count > ((MaxRipples - 1) * FloatPerRipple)) { RipplesData.RemoveRange(0, FloatPerRipple); } // remove dead ripples var died = UtilitiesTime.GetTime(false) - (MaxSize / Speed); var start = RipplesData.Count - FloatPerRipple; for (var i = start; i >= 0; i -= FloatPerRipple) { if (RipplesData[i + 2] < died) { RipplesData.RemoveRange(i, FloatPerRipple); } } } /// /// Add ripple. /// /// Event data. protected virtual void AddRipple(PointerEventData eventData) { CleanRipples(); var size = RectTransform.rect.size; var aspect_ratio = size.x / size.y; var pivot = RectTransform.pivot; Vector2 current_point; RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, eventData.position, eventData.pressEventCamera, out current_point); current_point.x += size.x * pivot.x; current_point.y -= size.y * (1f - pivot.y); var center_x = current_point.x / size.x; var center_y = (1f + (current_point.y / size.y)) / aspect_ratio; RipplesData.Add(center_x); RipplesData.Add(center_y); RipplesData.Add(UtilitiesTime.GetTime(false)); if (EffectMaterial != null) { EffectMaterial.SetInt(ShaderIDs.Count, RipplesData.Count / FloatPerRipple); EffectMaterial.SetFloatArray(ShaderIDs.Ripple, RipplesData); graphic.SetMaterialDirty(); } } /// /// Process pointer click event. /// /// Event data. public virtual void OnPointerClick(PointerEventData eventData) { AddRipple(eventData); } /// /// Process pointer down event. /// /// Event data. public virtual void OnPointerDown(PointerEventData eventData) { } /// /// Process pointer up event. /// /// Event data. public virtual void OnPointerUp(PointerEventData eventData) { } /// /// Init material. /// protected override void InitMaterial() { var n = MaxRipples * FloatPerRipple; if (RipplesData == null) { RipplesData = new List(n); } for (int i = RipplesData.Count; i < n; i++) { RipplesData.Add(-2); } base.InitMaterial(); } /// /// Set material properties. /// protected override void SetMaterialProperties() { if (EffectMaterial != null) { EffectMaterial.SetColor(ShaderIDs.StartColor, startColor); EffectMaterial.SetColor(ShaderIDs.EndColor, endColor); EffectMaterial.SetFloat(ShaderIDs.Speed, speed); EffectMaterial.SetFloat(ShaderIDs.MaxSize, maxSize); EffectMaterial.SetInt(ShaderIDs.Count, RipplesData.Count / FloatPerRipple); EffectMaterial.SetFloatArray(ShaderIDs.Ripple, RipplesData); } } } }