using UnityEngine; namespace Heroes.Battle { /// /// Converts a facing direction expressed in the flat 2D battlefield plane /// into the 3D rotation a unit model must have to look like it stands on a /// virtual ground plane viewed by an orthographic camera pitched by /// pitchDegrees. /// /// The unit's *position* stays 2D (it is placed at the hex's on-screen point). /// Only its *rotation* uses the virtual plane, which is what makes moves up / /// down / diagonally turn the model believably. /// /// Math: a camera pitched by φ below the horizon maps a ground direction /// (gx, gz) to the screen as (gx, gz·sinφ). Inverting it, a screen direction /// (sx, sy) corresponds to the ground direction (sx, sy / sinφ). The model is /// yawed around the ground normal to face that ground direction, then tilted /// by R_x(-φ) so that the ordinary (un-pitched) battle camera reproduces the /// image a pitched camera would see of an upright model. /// public static class BattleProjection { /// /// Local rotation for a unit model that should face /// (a direction in battlefield-local 2D space; +x right, +y up). /// /// Facing direction in the 2D battlefield plane. /// Pitch of the virtual viewing camera below the horizon. /// If false, only yaw is applied (no standing lean). /// Correction if the prefab's forward axis is not +Z. public static Quaternion FacingRotation(Vector2 boardDir, float pitchDegrees, bool applyViewTilt, float yawOffsetDegrees) { float sin = Mathf.Sin(pitchDegrees * Mathf.Deg2Rad); if (Mathf.Abs(sin) < 1e-3f) sin = sin < 0f ? -1e-3f : 1e-3f; Vector3 ground = new Vector3(boardDir.x, 0f, boardDir.y / sin); if (ground.sqrMagnitude < 1e-6f) ground = Vector3.forward; float yaw = Mathf.Atan2(ground.x, ground.z) * Mathf.Rad2Deg + yawOffsetDegrees; Quaternion rotation = Quaternion.AngleAxis(yaw, Vector3.up); if (applyViewTilt) { rotation = Quaternion.AngleAxis(-pitchDegrees, Vector3.right) * rotation; } return rotation; } } }