diff --git a/Assets/Scenes/Scene.unity b/Assets/Scenes/Scene.unity index e591821..9e95f4c 100644 --- a/Assets/Scenes/Scene.unity +++ b/Assets/Scenes/Scene.unity @@ -619,9 +619,9 @@ RectTransform: m_Children: - {fileID: 1599947843} - {fileID: 959545886} + - {fileID: 4208645783119030842} - {fileID: 3765562131710099388} - {fileID: 4795592279549688925} - - {fileID: 4208645783119030842} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} diff --git a/Assets/Scripts/Dice/Dice.cs b/Assets/Scripts/Dice/Dice.cs index e46dab8..ce43db2 100644 --- a/Assets/Scripts/Dice/Dice.cs +++ b/Assets/Scripts/Dice/Dice.cs @@ -44,6 +44,12 @@ namespace YachtDice.Dice return e.Value; } + public Quaternion GetClosestTopAlignedWorldRotation(Quaternion currentWorldRotation) + { + var e = GetExtremeEntryByWorldY(isTop: true); + return GetClosestAlignedWorldRotation(e.Point, currentWorldRotation); + } + public int AlignToBottomByLocalAngles() { var e = GetExtremeEntryByWorldY(isTop: false); @@ -82,6 +88,28 @@ namespace YachtDice.Dice return true; } + private Quaternion GetClosestAlignedWorldRotation(Transform facePoint, Quaternion currentWorldRotation) + { + if (!facePoint) throw new InvalidOperationException("Dice: facePoint == null."); + + var baseWorldRotation = GetAlignedWorldRotation(facePoint); + var bestWorldRotation = baseWorldRotation; + var smallestAngle = Quaternion.Angle(currentWorldRotation, baseWorldRotation); + + for (var i = 1; i < 4; i++) + { + var candidateWorldRotation = Quaternion.AngleAxis(90f * i, Vector3.up) * baseWorldRotation; + var candidateAngle = Quaternion.Angle(currentWorldRotation, candidateWorldRotation); + + if (candidateAngle >= smallestAngle) continue; + + smallestAngle = candidateAngle; + bestWorldRotation = candidateWorldRotation; + } + + return bestWorldRotation; + } + private Entry GetExtremeEntryByWorldY(bool isTop) { if (_entrySet == null || _entrySet.Count == 0) @@ -118,11 +146,23 @@ namespace YachtDice.Dice return a > 180f ? a - 360f : (a < -180f ? a + 360f : a); } + private Quaternion GetAlignedWorldRotation(Transform facePoint) + { + var parentRotation = transform.parent != null ? transform.parent.rotation : Quaternion.identity; + return parentRotation * GetAlignedLocalRotation(facePoint); + } + + private static Quaternion GetAlignedLocalRotation(Transform facePoint) + { + if (!facePoint) throw new InvalidOperationException("Dice: facePoint == null."); + return Quaternion.Inverse(facePoint.localRotation); + } + private void AlignByFaceLocalAngles(Transform facePoint) { if (!facePoint) throw new InvalidOperationException("Dice: facePoint == null."); - transform.localEulerAngles = Vector3.Scale(facePoint.localEulerAngles, new Vector3(-1f, -1f, -1f)); + transform.localRotation = GetAlignedLocalRotation(facePoint); var e = transform.localEulerAngles; transform.localEulerAngles = new Vector3(Norm180(e.x), Norm180(e.y), Norm180(e.z)); diff --git a/Assets/Scripts/Dice/DiceRoller.cs b/Assets/Scripts/Dice/DiceRoller.cs index 93ea115..8e8c0da 100644 --- a/Assets/Scripts/Dice/DiceRoller.cs +++ b/Assets/Scripts/Dice/DiceRoller.cs @@ -157,14 +157,12 @@ namespace YachtDice.Dice // ── 4. Ждём пока кубик успокоится ─────────────────────────── var stillTimer = 0f; var sqrThreshold = settleSpeed * settleSpeed; - var didTimeout = false; var maxDuration = Mathf.Max(0.1f, maxRollDuration); while (stillTimer < settleDelay) { if (Time.time - rollStartedAt >= maxDuration) { - didTimeout = true; break; } @@ -189,13 +187,7 @@ namespace YachtDice.Dice // ── 6. Плавный доворот до ровного положения ───────────────── Quaternion startRot = transform.rotation; - - // Вычисляем целевой поворот через Dice.AlignToTopByLocalAngles - dice.AlignToTopByLocalAngles(); - Quaternion targetRot = transform.rotation; - - // Откатываемся обратно — будем интерполировать - transform.rotation = startRot; + Quaternion targetRot = dice.GetClosestTopAlignedWorldRotation(startRot); var elapsed = 0f; var snapTime = Mathf.Max(0.01f, snapDuration);