This repository has been archived on 2026-04-18. You can view files and clone it, but cannot push or open issues or pull requests.
Virtual-Memory-Demo/Assets/Scripts/UI/StepFlowAnimator.cs

180 lines
5.4 KiB
C#

using System;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
using VMdemo.Simulation;
namespace VMdemo.UI
{
public class StepFlowAnimator : MonoBehaviour
{
[Serializable]
public class StepNodeView
{
public TranslationStep step;
public RectTransform target;
public CanvasGroup canvasGroup;
public Image background;
}
[Header("Step Nodes")] [SerializeField]
private List<StepNodeView> stepNodes = new List<StepNodeView>();
[Header("Colors")] [SerializeField] private Color idleColor = new Color(0.18f, 0.22f, 0.28f, 1f);
[SerializeField] private Color activeColor = new Color(0.19f, 0.62f, 0.98f, 1f);
[SerializeField] private Color faultColor = new Color(0.97f, 0.31f, 0.27f, 1f);
[Header("Animation")] [SerializeField] private float highlightDuration = 0.2f;
[SerializeField] private float normalScale = 1f;
[SerializeField] private float activeScale = 1.08f;
[SerializeField] private float pulseScale = 1.04f;
[SerializeField] private RectTransform accessPulseTarget;
private readonly Dictionary<TranslationStep, StepNodeView> _nodeMap =
new Dictionary<TranslationStep, StepNodeView>();
private TranslationStep? _lastStep;
private void Awake()
{
BuildNodeMap();
ResetVisual();
}
public void ResetVisual()
{
DOTween.Kill(this);
_lastStep = null;
foreach (var node in stepNodes)
{
if (node == null)
{
continue;
}
if (node.target != null)
{
node.target.localScale = Vector3.one * normalScale;
}
if (node.canvasGroup != null)
{
node.canvasGroup.alpha = 0.72f;
}
if (node.background != null)
{
node.background.color = idleColor;
}
}
if (accessPulseTarget != null)
{
accessPulseTarget.localScale = Vector3.one;
}
}
public void AnimateStep(TranslationStep currentStep, bool isPageFault, bool accessCompleted)
{
BuildNodeMap();
if (_lastStep.HasValue && _nodeMap.TryGetValue(_lastStep.Value, out var lastNode))
{
AnimateToIdle(lastNode);
}
if (_nodeMap.TryGetValue(currentStep, out var currentNode))
{
AnimateToActive(currentNode, isPageFault && currentStep == TranslationStep.HandlePageFault);
}
if (accessCompleted)
{
AnimateAccessCompletedPulse();
}
_lastStep = currentStep;
}
private void BuildNodeMap()
{
_nodeMap.Clear();
foreach (var node in stepNodes)
{
if (node == null)
{
continue;
}
if (!_nodeMap.ContainsKey(node.step))
{
_nodeMap.Add(node.step, node);
}
}
}
private void AnimateToIdle(StepNodeView node)
{
if (node.target != null)
{
node.target.DOKill();
node.target.DOScale(normalScale, highlightDuration).SetEase(Ease.OutQuad).SetTarget(this);
}
if (node.canvasGroup != null)
{
node.canvasGroup.DOKill();
node.canvasGroup.DOFade(0.72f, highlightDuration).SetEase(Ease.OutQuad).SetTarget(this);
}
if (node.background != null)
{
node.background.DOKill();
node.background.DOColor(idleColor, highlightDuration).SetEase(Ease.OutQuad).SetTarget(this);
}
}
private void AnimateToActive(StepNodeView node, bool emphasizeFault)
{
if (node.target != null)
{
node.target.DOKill();
node.target.DOScale(activeScale, highlightDuration).SetEase(Ease.OutBack).SetTarget(this);
}
if (node.canvasGroup != null)
{
node.canvasGroup.DOKill();
node.canvasGroup.DOFade(1f, highlightDuration).SetEase(Ease.OutQuad).SetTarget(this);
}
if (node.background != null)
{
node.background.DOKill();
var targetColor = emphasizeFault ? faultColor : activeColor;
node.background.DOColor(targetColor, highlightDuration).SetEase(Ease.OutQuad).SetTarget(this);
}
if (emphasizeFault && node.target != null)
{
node.target.DOPunchScale(new Vector3(0.07f, 0.07f, 0f), 0.28f, 6, 0.75f).SetTarget(this);
}
}
private void AnimateAccessCompletedPulse()
{
if (accessPulseTarget == null)
{
return;
}
accessPulseTarget.DOKill();
accessPulseTarget
.DOScale(pulseScale, 0.12f)
.SetEase(Ease.OutQuad)
.SetLoops(2, LoopType.Yoyo)
.SetTarget(this);
}
}
}