using UnityEngine; using UnityEngine.UI; using DG.Tweening; using System.Collections; using AibisDream; using AibisDream.FixSystem; using AibisDream.Kit; public class HeatMapController : MonoBehaviour { public Material heatMaterial; // 需要传递数据的材质 public Transform[] Pos; public Button coolingButton; private BodyModuleSystem bodyModuleSystem; public GameObject heatmapGameobject; public RectTransform imageToMove; // 需要移动的Image public Transform imageToRotate; // 需要旋转的Image [SerializeField, Min(0f)] private float fanStopDuration = 0.15f; [Header("低温背景")] [SerializeField] private Color coldBaseColor = new Color(0.015f, 0.075f, 0.16f, 1f); [SerializeField] private Color coldHighlightColor = new Color(0.02f, 0.27f, 0.48f, 1f); [SerializeField, Min(0.01f)] private float coldNoiseScale = 1.8f; [SerializeField, Range(0f, 1f)] private float coldNoiseStrength = 0.45f; public Vector3 inPos; // Heatmap进入位置 public Vector3 outPos; // Heatmap退出位置 private Vector3 imageInitialPos; // Image的初始位置 [HideInInspector] private Vector4[] points = new Vector4[7]; // 存储6个点的数组,(x, y, z) = center, w = temperature [HideInInspector] private Vector4[] properties = new Vector4[7]; // 存储6个盒子大小的数组, (x,y) = boxsize private float uftempture = 1; private float backtemture = 1; private float modulesTempture = 1; private float tempture = 1; private Tween _temptureTween; private Tween _fanSpeedTween; private float _fanAngularSpeed; private const float FanAngularSpeed = 1800f; /// 热力图面板当前是否在显示(用于 SetHeat 决定硬切还是平滑)。 public bool IsHeatMapVisible => heatmapGameobject != null && heatmapGameobject.activeInHierarchy; public float Tempture { get => tempture; set { if (tempture != value) { tempture = value; SetDataToMaterial(); // 调用方法更新材质数据 } } } public void ShowHeatMap() { StartCoroutine(ShowHeatMapCoroutine()); } private IEnumerator ShowHeatMapCoroutine() { // 初始化Heatmap位置 heatmapGameobject.transform.localPosition = outPos; heatmapGameobject.SetActive(true); SetDataToMaterial(); heatmapGameobject.transform.DOLocalMove(inPos, 1f); yield return new WaitForSeconds(1f); // 等待动画完成 heatmapGameobject.GetComponent().UnlockDrag(); coolingButton.interactable = false; } public void HideHeatMap() { var draggableUI = heatmapGameobject.GetComponent(); heatmapGameobject.transform.DOLocalMove(outPos, 1f).OnComplete(() => { draggableUI.UnlockDrag(); heatmapGameobject.SetActive(false);}); } public void StartCooling() { coolingButton.interactable = false; StartCoroutine(ReduceValueCoroutine()); } IEnumerator ReduceValueCoroutine() { var draggableUI = heatmapGameobject.GetComponent(); draggableUI.LockDrag(); // 存储初始位置 imageInitialPos = imageToMove.localPosition; // 移动和旋转动画 imageToMove.DOLocalMoveX(imageInitialPos.x + 3.5f, 1f); StartFan(); bool coolingSucceeded = Tempture < 2; if (coolingSucceeded) { Debug.Log("降温成功"); float duration = 3.5f; SetTemptureSmooth(0.5f, duration); AudioManager.Instance.PlaySfx("event:/FollowInput/cooler_cooling"); yield return new WaitForSeconds(duration); } else { Debug.Log("降温失败"); yield return new WaitForSeconds(3f); } yield return StopFanSmoothly(); // 恢复初始状态 imageToMove.DOLocalMove(imageInitialPos, 1f); if (coolingSucceeded) { yield return new WaitForSeconds(1.5f); DialogController.Instance.StartDialogNode("降温成功"); bodyModuleSystem.OnCooling(); } else { DialogController.Instance.StartDialogNode("降温失败"); } } private void Update() { if (imageToRotate == null || Mathf.Approximately(_fanAngularSpeed, 0f)) return; imageToRotate.Rotate(0f, 0f, _fanAngularSpeed * Time.deltaTime, Space.Self); } private void StartFan() { _fanSpeedTween?.Kill(); _fanSpeedTween = null; _fanAngularSpeed = FanAngularSpeed; } private IEnumerator StopFanSmoothly() { _fanSpeedTween?.Kill(); if (fanStopDuration <= 0f) { _fanAngularSpeed = 0f; _fanSpeedTween = null; yield break; } _fanSpeedTween = DOTween.To( () => _fanAngularSpeed, value => _fanAngularSpeed = value, 0f, fanStopDuration) .SetEase(Ease.OutQuad) .SetTarget(this); yield return _fanSpeedTween.WaitForCompletion(); _fanAngularSpeed = 0f; _fanSpeedTween = null; } void Start() { bodyModuleSystem = FindObjectOfType(); SetDataToMaterial(); } void OnDestroy() { _temptureTween?.Kill(); _fanSpeedTween?.Kill(); _fanAngularSpeed = 0f; } /// /// 热点尺寸(世界单位):自身 Sprite/Image → 父级 Sprite → RectTransform。 /// UFheatmap 等空 Sprite 锚点会回退到父级 Module Pic,避免静默变成 (1,1) 导致热区偏小。 /// private static bool TryGetHotspotSize(Transform t, out Vector2 size) { size = default; if (t == null) return false; if (TryGetSpriteLocalSize(t, out size)) return true; // 空 SpriteRenderer / 纯锚点:沿父链找有 sprite 的模块图 for (var p = t.parent; p != null; p = p.parent) { if (TryGetSpriteLocalSize(p, out size)) return true; } var image = t.GetComponent(); if (image != null && image.sprite != null) { var b = image.sprite.bounds.size; size = new Vector2(b.x, b.y); return true; } if (t is RectTransform rt) { size = new Vector2(rt.rect.width, rt.rect.height); return size.x > 0f || size.y > 0f; } return false; } private static bool TryGetSpriteLocalSize(Transform t, out Vector2 size) { size = default; var sr = t.GetComponent(); if (sr == null || sr.sprite == null) return false; var b = sr.sprite.bounds.size; var ls = t.lossyScale; size = new Vector2(b.x * Mathf.Abs(ls.x), b.y * Mathf.Abs(ls.y)); return size.x > 0.001f || size.y > 0.001f; } public void SetDataToMaterial() { // 逻辑温度 0~2 → shader 侧发热强度;各段端点连续,避免跨档跳变。 // t=0 冷态压低,接近「看不见」;t=1/2 保持原有发热观感。 if (tempture <= 1f) { float t = Mathf.Clamp01(tempture); uftempture = Mathf.Lerp(0.85f, 2.8f, t); modulesTempture = Mathf.Lerp(0.55f, 1.6f, t); backtemture = Mathf.Lerp(0.7f, 2.25f, t); } else { float t = Mathf.Clamp01(tempture - 1f); uftempture = Mathf.Lerp(2.8f, 3.2f, t); modulesTempture = Mathf.Lerp(1.6f, 2.3f, t); // 原 1.8 起点与上一段不连续 backtemture = Mathf.Lerp(2.25f, 3.4f, t); } if (Pos == null || heatMaterial == null) return; for (int i = 0; i < 7; i++) { if (Pos[i] == null) { points[i] = Vector4.zero; properties[i] = Vector4.zero; continue; } Transform currentTransform = Pos[i].GetComponent(); if (i == 0) { points[i] = new Vector4(Pos[i].position.x, Pos[i].position.y, uftempture, 0); } else if (i == 6) { points[i] = new Vector4(Pos[i].position.x, Pos[i].position.y, backtemture, 0); } else { points[i] = new Vector4(Pos[i].position.x, Pos[i].position.y, modulesTempture, 0); // (x, y, z, temperature) } if (!TryGetHotspotSize(currentTransform, out var boxSize)) { Debug.LogWarning($"[HeatMapController] Pos[{i}] '{Pos[i].name}' 无法取得热点尺寸,回退为 (1,1)。请检查 Sprite 或父级 Module Pic。", Pos[i]); boxSize = Vector2.one; } properties[i] = new Vector4(boxSize.x, boxSize.y, 0.0f, 0.0f); } // 将数据传递给 Shader heatMaterial.SetVectorArray("_Points", points); heatMaterial.SetVectorArray("_Properties", properties); heatMaterial.SetColor("_ColdBaseColor", coldBaseColor); heatMaterial.SetColor("_ColdHighlightColor", coldHighlightColor); heatMaterial.SetFloat("_ColdNoiseScale", coldNoiseScale); heatMaterial.SetFloat("_ColdNoiseStrength", coldNoiseStrength); } public void SetTemptureDirect(float value) { _temptureTween?.Kill(); Tempture = value; } public void SetTemptureSmooth(float targetValue, float duration) { _temptureTween?.Kill(); _temptureTween = DOTween.To(() => tempture, x => Tempture = x, targetValue, duration) .SetEase(Ease.InOutQuad) .SetTarget(this); } }