Files
bansonic_beta_main/Assets/idolsDisplay/mtr/idolMaterialController.cs
T

251 lines
6.6 KiB
C#

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Image))]
public class idolMaterialController : MonoBehaviour
{
public enum SweepSide
{
FromLeft = 0,
FromRight = 1,
}
[Header("Material")]
public Material materialTemplate;
[Header("Animation")]
public SweepSide enterDirection = SweepSide.FromLeft;
public SweepSide exitDirection = SweepSide.FromRight;
public float enterDuration = 0.35f;
public float exitDuration = 0.25f;
[Range(0f, 1f)]
public float hiddenAmount = 0f;
[Range(0f, 1f)]
public float shownAmount = 1f;
public bool playEnterOnEnable = false;
private Image image;
private Material runtimeMaterial;
private Coroutine transitionCoroutine;
private Sprite pendingSprite;
private static readonly int DissolveAmountId = Shader.PropertyToID("_DissolveAmount");
private static readonly int DirectionId = Shader.PropertyToID("_Direction");
private void Awake()
{
image = GetComponent<Image>();
EnsureRuntimeMaterial();
ApplyInstantState(shownAmount, GetShaderDirectionForEnter(enterDirection));
}
private void OnEnable()
{
EnsureRuntimeMaterial();
if (playEnterOnEnable && image != null && image.sprite != null)
{
PlayEnter();
}
else
{
ApplyInstantState(shownAmount, GetShaderDirectionForEnter(enterDirection));
}
}
private void OnDisable()
{
if (transitionCoroutine != null)
{
StopCoroutine(transitionCoroutine);
transitionCoroutine = null;
}
}
private void OnDestroy()
{
if (runtimeMaterial != null)
{
if (image != null && image.material == runtimeMaterial)
{
image.material = null;
}
Destroy(runtimeMaterial);
}
}
public void SetSpriteImmediate(Sprite sprite)
{
EnsureRuntimeMaterial();
if (image == null)
{
return;
}
image.sprite = sprite;
ApplyInstantState(shownAmount, GetShaderDirectionForEnter(enterDirection));
}
public void PlayEnter()
{
EnsureRuntimeMaterial();
if (image == null || runtimeMaterial == null)
{
return;
}
if (transitionCoroutine != null)
{
StopCoroutine(transitionCoroutine);
}
transitionCoroutine = StartCoroutine(AnimateAmount(hiddenAmount, shownAmount, Mathf.Max(0.01f, enterDuration), GetShaderDirectionForEnter(enterDirection), null));
}
public void PlayExit()
{
EnsureRuntimeMaterial();
if (image == null || runtimeMaterial == null)
{
return;
}
if (transitionCoroutine != null)
{
StopCoroutine(transitionCoroutine);
}
transitionCoroutine = StartCoroutine(AnimateAmount(shownAmount, hiddenAmount, Mathf.Max(0.01f, exitDuration), GetShaderDirectionForExit(exitDirection), null));
}
public void TransitionToSprite(Sprite sprite)
{
EnsureRuntimeMaterial();
if (image == null)
{
return;
}
pendingSprite = sprite;
if (transitionCoroutine != null)
{
StopCoroutine(transitionCoroutine);
}
bool hasCurrentSprite = image.sprite != null;
bool spriteChanged = image.sprite != sprite;
transitionCoroutine = StartCoroutine(TransitionRoutine(hasCurrentSprite, spriteChanged));
}
private IEnumerator TransitionRoutine(bool hasCurrentSprite, bool spriteChanged)
{
if (runtimeMaterial == null || image == null)
{
yield break;
}
if (hasCurrentSprite && spriteChanged)
{
yield return AnimateAmount(shownAmount, hiddenAmount, Mathf.Max(0.01f, exitDuration), GetShaderDirectionForExit(exitDirection), null);
}
image.sprite = pendingSprite;
if (pendingSprite == null)
{
ApplyInstantState(hiddenAmount, GetShaderDirectionForEnter(enterDirection));
transitionCoroutine = null;
yield break;
}
yield return AnimateAmount(hiddenAmount, shownAmount, Mathf.Max(0.01f, enterDuration), GetShaderDirectionForEnter(enterDirection), null);
transitionCoroutine = null;
}
private IEnumerator AnimateAmount(float from, float to, float duration, float directionValue, System.Action onComplete)
{
if (runtimeMaterial == null)
{
yield break;
}
runtimeMaterial.SetFloat(DirectionId, directionValue);
runtimeMaterial.SetFloat(DissolveAmountId, from);
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / duration);
runtimeMaterial.SetFloat(DissolveAmountId, Mathf.Lerp(from, to, t));
yield return null;
}
runtimeMaterial.SetFloat(DissolveAmountId, to);
onComplete?.Invoke();
}
private void ApplyInstantState(float amount, float directionValue)
{
EnsureRuntimeMaterial();
if (runtimeMaterial == null)
{
return;
}
runtimeMaterial.SetFloat(DirectionId, directionValue);
runtimeMaterial.SetFloat(DissolveAmountId, Mathf.Clamp01(amount));
}
private void EnsureRuntimeMaterial()
{
if (image == null)
{
image = GetComponent<Image>();
}
if (image == null)
{
return;
}
Material source = materialTemplate != null
? materialTemplate
: image.material;
if (source == null)
{
return;
}
if (runtimeMaterial != null && runtimeMaterial.shader == source.shader)
{
if (image.material != runtimeMaterial)
{
image.material = runtimeMaterial;
}
return;
}
if (runtimeMaterial != null)
{
Destroy(runtimeMaterial);
}
runtimeMaterial = new Material(source);
runtimeMaterial.name = source.name + " (idol runtime)";
image.material = runtimeMaterial;
}
private static float GetShaderDirectionForEnter(SweepSide side)
{
return side == SweepSide.FromLeft ? 0f : 1f;
}
private static float GetShaderDirectionForExit(SweepSide side)
{
return side == SweepSide.FromLeft ? 1f : 0f;
}
}