fix(heatmap): 按Sprite几何中心旋转风扇避免散架

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-21 17:42:44 +08:00
co-authored by Cursor
parent 046b1a9efb
commit 8fee89db36
2 changed files with 78 additions and 2 deletions
@@ -122,7 +122,7 @@ ScriptedImporter:
- name: "\xE9\xA3\x8E\xE6\x89\x87"
originalName:
pivot: {x: 0.5, y: 0.5}
alignment: 0
alignment: 9
border: {x: 0, y: 0, z: 0, w: 0}
rect:
serializedVersion: 2
@@ -21,6 +21,8 @@ public class HeatMapController : MonoBehaviour
public RectTransform imageToMove; // 需要移动的Image
public Transform imageToRotate; // 需要旋转的Image
[SerializeField, Min(0f)] private float fanStopDuration = 0.15f;
[Tooltip("相对 Sprite 矩形中心的额外旋转轴偏移(本地空间,单位与 Transform 一致)。用于校准扇毂不在图心时的偏心。")]
[SerializeField] private Vector2 fanPivotLocalOffset;
[Header("低温背景")]
[SerializeField] private Color coldBaseColor = new Color(0.005f, 0.02f, 0.55f, 1f);
[SerializeField] private Color coldHighlightColor = new Color(0f, 0.3f, 1f, 1f);
@@ -45,6 +47,9 @@ public class HeatMapController : MonoBehaviour
private Tween _temptureTween;
private Tween _fanSpeedTween;
private float _fanAngularSpeed;
private SpriteRenderer _fanRenderer;
private Vector3 _fanLocalPivot; // 相对 imageToRotate 的本地旋转轴
private bool _fanPivotReady;
private const float FanAngularSpeed = 1800f;
@@ -145,13 +150,53 @@ public class HeatMapController : MonoBehaviour
if (imageToRotate == null || Mathf.Approximately(_fanAngularSpeed, 0f))
return;
imageToRotate.Rotate(0f, 0f, _fanAngularSpeed * Time.deltaTime, Space.Self);
EnsureFanPivot();
float angle = _fanAngularSpeed * Time.deltaTime;
// 绕 Sprite 几何中心转,避免 pivot 不在扇叶中心时整圈公转(看起来像散架)。
Vector3 worldPivot = imageToRotate.TransformPoint(_fanLocalPivot);
imageToRotate.RotateAround(worldPivot, imageToRotate.forward, angle);
}
private void EnsureFanPivot()
{
if (_fanPivotReady)
return;
_fanLocalPivot = Vector3.zero;
if (imageToRotate == null)
{
_fanPivotReady = true;
return;
}
_fanRenderer = imageToRotate.GetComponent<SpriteRenderer>();
if (_fanRenderer != null && _fanRenderer.sprite != null)
{
var sprite = _fanRenderer.sprite;
// transform 在 sprite.pivot;矩形几何中心相对 pivot 的本地偏移。
// Aseprite Canvas 空间 pivot 会远超出 [0,1],直接 Rotate 会整圈公转。
Vector2 size = sprite.rect.size;
Vector2 pivotPx = sprite.pivot;
float ppu = sprite.pixelsPerUnit;
if (ppu > 0f)
{
_fanLocalPivot = new Vector3(
(size.x * 0.5f - pivotPx.x) / ppu,
(size.y * 0.5f - pivotPx.y) / ppu,
0f);
}
}
_fanLocalPivot += (Vector3)fanPivotLocalOffset;
_fanPivotReady = true;
}
private void StartFan()
{
_fanSpeedTween?.Kill();
_fanSpeedTween = null;
_fanPivotReady = false;
EnsureFanPivot();
_fanAngularSpeed = FanAngularSpeed;
}
@@ -182,6 +227,7 @@ public class HeatMapController : MonoBehaviour
void Start()
{
bodyModuleSystem = FindObjectOfType<BodyModuleSystem>();
EnsureFanPivot();
SetDataToMaterial();
}
@@ -319,4 +365,34 @@ public class HeatMapController : MonoBehaviour
.SetEase(Ease.InOutQuad)
.SetTarget(this);
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (imageToRotate == null)
return;
// 编辑器下也能预览旋转轴(不依赖 Play 时缓存)。
var sr = imageToRotate.GetComponent<SpriteRenderer>();
Vector3 localPivot = (Vector3)fanPivotLocalOffset;
if (sr != null && sr.sprite != null && sr.sprite.pixelsPerUnit > 0f)
{
var sprite = sr.sprite;
Vector2 size = sprite.rect.size;
Vector2 pivotPx = sprite.pivot;
float ppu = sprite.pixelsPerUnit;
localPivot += new Vector3(
(size.x * 0.5f - pivotPx.x) / ppu,
(size.y * 0.5f - pivotPx.y) / ppu,
0f);
}
Vector3 worldPivot = imageToRotate.TransformPoint(localPivot);
Gizmos.color = new Color(1f, 0.45f, 0.1f, 0.9f);
Gizmos.DrawWireSphere(worldPivot, 0.08f);
Gizmos.color = new Color(0.3f, 0.9f, 0.55f, 0.7f);
Gizmos.DrawLine(worldPivot + Vector3.left * 0.2f, worldPivot + Vector3.right * 0.2f);
Gizmos.DrawLine(worldPivot + Vector3.down * 0.2f, worldPivot + Vector3.up * 0.2f);
}
#endif
}