using UnityEngine; namespace S_SnapTools { public class S_SnapToGrid : MonoBehaviour { public enum ESnapCondition { ON_UPDATE = 0, WHEN_STILL = 1 } [SerializeField] private float m_epsilon = 0.001f; [SerializeField] private float m_maxVelocity_StillMode = 0.001f; [SerializeField] private float m_maxEulerVelocity_StillMode = 0.01f; [SerializeField] private float m_stillTimeBeforSnap_StillMode = 0.2f; [SerializeField] private Vector3 m_gridOffset = Vector3.zero; [SerializeField] private Vector3 m_gridCellSize = Vector3.one * 10f; [SerializeField] private Vector3 m_rotationOffset = Vector3.zero; [SerializeField] private Vector3 m_rotationStepSize = Vector3.one * 22.5f; [SerializeField] private ESnapCondition m_snapCondition; [SerializeField] private bool m_isInstantSnap; [SerializeField] private bool m_isSnapAxisX = true; [SerializeField] private bool m_isSnapAxisY = true; [SerializeField] private bool m_isSnapAxisZ = true; [SerializeField] private bool m_isSnapAxisXRotation = true; [SerializeField] private bool m_isSnapAxisYRotation = true; [SerializeField] private bool m_isSnapAxisZRotation = true; private float m_stillTimePos; private float m_stillTimeRot; private Vector3 m_lastPosition = Vector3.zero; private Vector3 m_lastEuler = Vector3.zero; public float Epsilon { get { return m_epsilon; } set { m_epsilon = value; } } public float MaxVelocity_StillMode { get { return m_maxVelocity_StillMode; } set { m_maxVelocity_StillMode = value; } } public float MaxEulerVelocity_StillMode { get { return m_maxEulerVelocity_StillMode; } set { m_maxEulerVelocity_StillMode = value; } } public float StillTimeBeforSnap_StillMode { get { return m_stillTimeBeforSnap_StillMode; } set { m_stillTimeBeforSnap_StillMode = value; } } public Vector3 GridOffset { get { return m_gridOffset; } set { m_gridOffset = value; } } public Vector3 GridCellSize { get { return m_gridCellSize; } set { m_gridCellSize = value; } } public Vector3 RotationOffset { get { return m_rotationOffset; } set { m_rotationOffset = value; } } public Vector3 RotationStepSize { get { return m_rotationStepSize; } set { m_rotationStepSize = value; } } public ESnapCondition SnapCondition { get { return m_snapCondition; } set { m_snapCondition = value; } } public bool IsInstantSnap { get { return m_isInstantSnap; } set { m_isInstantSnap = value; } } public bool IsSnapAxisX { get { return m_isSnapAxisX; } set { m_isSnapAxisX = value; } } public bool IsSnapAxisY { get { return m_isSnapAxisY; } set { m_isSnapAxisY = value; } } public bool IsSnapAxisZ { get { return m_isSnapAxisZ; } set { m_isSnapAxisZ = value; } } public bool IsSnapAxisXRotation { get { return m_isSnapAxisXRotation; } set { m_isSnapAxisXRotation = value; } } public bool IsSnapAxisYRotation { get { return m_isSnapAxisYRotation; } set { m_isSnapAxisYRotation = value; } } public bool IsSnapAxisZRotation { get { return m_isSnapAxisZRotation; } set { m_isSnapAxisZRotation = value; } } public void DoSnap() { DoSnapPosition(); DoSnapRotation(); } public void DoSnapPosition() { Vector3 o_snapTarget; if (GetSnapTarget(base.transform.position, m_gridOffset, m_gridCellSize, m_isSnapAxisX, m_isSnapAxisY, m_isSnapAxisZ, out o_snapTarget)) { if (m_isInstantSnap) { base.transform.position = o_snapTarget; } else { base.transform.position = Vector3.Lerp(base.transform.position, o_snapTarget, 0.5f); } m_lastPosition = base.transform.position; } } public void DoSnapRotation() { Vector3 o_snapTarget; if (GetSnapTarget(base.transform.localEulerAngles, m_rotationOffset, m_rotationStepSize, m_isSnapAxisXRotation, m_isSnapAxisYRotation, m_isSnapAxisZRotation, out o_snapTarget)) { if (m_isInstantSnap) { base.transform.localEulerAngles = o_snapTarget; } else { base.transform.localEulerAngles = Vector3.Slerp(base.transform.localEulerAngles, o_snapTarget, 0.5f); } m_lastEuler = base.transform.localEulerAngles; } } private void Start() { if (m_gridCellSize.x == 0f || m_gridCellSize.y == 0f || m_gridCellSize.z == 0f) { m_gridCellSize += Vector3.one * 0.1f; Debug.LogError("S_SnapToGrid: Start: All components of the GridCellSize vector must be unequal 0!"); } } private void LateUpdate() { if (m_snapCondition == ESnapCondition.ON_UPDATE) { DoSnap(); return; } if (IsStill(base.transform.position, m_maxVelocity_StillMode, ref m_lastPosition, ref m_stillTimePos)) { DoSnapPosition(); } if (IsStill(base.transform.localEulerAngles, m_maxEulerVelocity_StillMode, ref m_lastEuler, ref m_stillTimeRot)) { DoSnapRotation(); } } private bool IsStill(Vector3 p_currentValue, float p_maxV, ref Vector3 r_lastValue, ref float r_stillTime) { if ((r_lastValue - p_currentValue).magnitude / Time.deltaTime < p_maxV) { r_stillTime += Time.deltaTime; } else { r_stillTime = 0f; } r_lastValue = p_currentValue; return r_stillTime >= m_stillTimeBeforSnap_StillMode; } private bool GetSnapTarget(Vector3 p_value, Vector3 p_offset, Vector3 p_stepSize, bool p_isSnapAxisX, bool p_isSnapAxisY, bool p_isSnapAxisZ, out Vector3 o_snapTarget) { Vector3 vector = p_value - p_offset; float num = vector.x / p_stepSize.x; float num2 = vector.y / p_stepSize.y; float num3 = vector.z / p_stepSize.z; if ((p_isSnapAxisX && num - Mathf.Floor(num) > m_epsilon) || (p_isSnapAxisY && num2 - Mathf.Floor(num2) > m_epsilon) || (p_isSnapAxisZ && num3 - Mathf.Floor(num3) > m_epsilon)) { if (p_isSnapAxisX) { num = Mathf.Round(num); } if (p_isSnapAxisY) { num2 = Mathf.Round(num2); } if (p_isSnapAxisZ) { num3 = Mathf.Round(num3); } o_snapTarget = p_offset + Vector3.Scale(new Vector3(num, num2, num3), p_stepSize); return true; } o_snapTarget = p_value; return false; } } }