Here are the scripts and sprites I use in the video.
CustomButton.cs
I wrote this script as a template to further customize Unity’s default Button component. It includes all the features of the standard Button, allowing me to add my own custom behavior on top of it. I use this in my own games. For the shadow effect, I added animations to both the button and its icon using DOTween. You can review the code for more details.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using DG.Tweening;
public enum ButtonIconAnimStyle
{
Move,
Scale,
Rotate
}
public class CustomButton : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
{
[Header("Settings")]
public ButtonIconAnimStyle iconAnimStyle = ButtonIconAnimStyle.Scale;
[SerializeField] protected float pressedScale = 0.9f;
[SerializeField] protected float scaleDuration = 0.2f;
[Header("References")]
public Image outerGlowImage;
public Color glowStartColor;
public RectTransform iconRect;
[Header("Events")]
public UnityEvent onPointerClick;
public bool isInteractable = true;
private Vector2 _iconDefaultPos;
private Vector3 _iconDefaultScale;
protected virtual void Start()
{
SetInteractable(isInteractable);
if (outerGlowImage != null)
{
Color c = outerGlowImage.color;
c.a = 0;
outerGlowImage.color = c;
}
if (iconRect != null)
{
_iconDefaultPos = iconRect.anchoredPosition;
_iconDefaultScale = iconRect.localScale;
}
}
public virtual void SetInteractable(bool isInteractable)
{
this.isInteractable = isInteractable;
}
protected virtual void OnDisable()
{
transform.DOKill();
if (outerGlowImage != null) outerGlowImage.DOKill();
if (iconRect != null) iconRect.DOKill();
transform.localScale = Vector3.one;
if (iconRect != null)
{
iconRect.localScale = _iconDefaultScale;
iconRect.localRotation = Quaternion.identity;
iconRect.anchoredPosition = _iconDefaultPos;
}
}
public virtual void OnPointerClick(PointerEventData eventData)
{
if (!isInteractable) return;
if (outerGlowImage != null)
{
outerGlowImage.DOKill();
outerGlowImage.color = glowStartColor;
outerGlowImage.DOFade(0f, 1.2f).SetEase(Ease.OutQuad);
}
if (iconRect != null)
{
PlayIconAnimation();
}
transform.DOKill();
transform.localScale = Vector3.one;
transform.DOPunchScale(new Vector3(0.25f, 0.25f, 0.25f), 1.1f, 3, 0.1f);
NavigationButtonsManager.I.OnNavButtonClicked(this);
onPointerClick?.Invoke();
}
private void PlayIconAnimation()
{
iconRect.DOKill();
iconRect.localScale = _iconDefaultScale;
iconRect.localRotation = Quaternion.identity;
iconRect.anchoredPosition = _iconDefaultPos;
Sequence seq = DOTween.Sequence();
switch (iconAnimStyle)
{
case ButtonIconAnimStyle.Move:
float moveDist = 10f;
float moveSpeed = 0.1f;
seq.Append(iconRect.DOAnchorPosX(_iconDefaultPos.x + moveDist, moveSpeed).SetEase(Ease.OutQuad));
seq.Append(iconRect.DOAnchorPosX(_iconDefaultPos.x - moveDist, moveSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOAnchorPosX(_iconDefaultPos.x + moveDist, moveSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOAnchorPosX(_iconDefaultPos.x - moveDist, moveSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOAnchorPosX(_iconDefaultPos.x, moveSpeed).SetEase(Ease.OutQuad));
break;
case ButtonIconAnimStyle.Scale:
float scaleMultiplier = 1.4f;
float dampFactor = 0.1f;
float scaleSpeed = 0.12f;
for (int i = 0; i < 4; i++)
{
seq.Append(iconRect.DOScale(_iconDefaultScale * scaleMultiplier, scaleSpeed).SetEase(Ease.OutQuad));
seq.Append(iconRect.DOScale(_iconDefaultScale, scaleSpeed).SetEase(Ease.InQuad));
scaleMultiplier -= dampFactor;
}
break;
case ButtonIconAnimStyle.Rotate:
float rotAngle = 20f;
float rotSpeed = 0.1f;
seq.Append(iconRect.DOLocalRotate(new Vector3(0, 0, -rotAngle), rotSpeed).SetEase(Ease.OutQuad));
seq.Append(iconRect.DOLocalRotate(new Vector3(0, 0, rotAngle), rotSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOLocalRotate(new Vector3(0, 0, -rotAngle), rotSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOLocalRotate(new Vector3(0, 0, rotAngle), rotSpeed).SetEase(Ease.InOutQuad));
seq.Append(iconRect.DOLocalRotate(Vector3.zero, rotSpeed).SetEase(Ease.OutQuad));
break;
}
}
public virtual void OnPointerDown(PointerEventData eventData)
{
if (!isInteractable) return;
transform.DOKill();
transform.DOScale(pressedScale, scaleDuration).SetEase(Ease.OutBack);
}
public virtual void OnPointerUp(PointerEventData eventData)
{
}
}NavigationButtonsManager.cs
This script uses a Singleton pattern and updates the light’s position based on calls from the buttons.
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
public class NavigationButtonsManager : MonoBehaviour
{
public List<CustomButton> navigationButtons = new List<CustomButton>();
public Transform uiLightTransform;
public static NavigationButtonsManager I;
void Awake()
{
if (I == null) I = this;
else Destroy(gameObject);
}
public void OnNavButtonClicked(CustomButton clickedButton)
{
uiLightTransform.DOKill();
var targetWorldX = clickedButton.transform.position.x;
uiLightTransform.DOMoveX(targetWorldX, 0.3f).SetEase(Ease.OutQuad);
}
}
Button Sprites



Bir yanıt yazın