using System.Collections; using System.Collections.Generic; using UnityEngine; public class PhysicsMovement : MonoBehaviour { #region Define public const float OnGroundTestLength = 0.02f; public const float OnGroundTestShiftLength = 0.02f; [System.Serializable] public struct AccelerationBindSetting { public PhysicMaterial OtherMaterial; public AccelerationSetting Setting; } [System.Serializable] public struct AccelerationSetting { public float DestAcc; public float DragAcc; } #endregion #region Interface public Vector3 DestVelocity { get => m_destVelocity; set { m_destVelocity = value; } } public bool IsOnGround { get { return m_dummyHitted; } } #endregion #region Setting public AccelerationSetting InAirAccSetting; public AccelerationSetting DefaultGroundAccSetting; public List AdditionalGroundSettings; #endregion #region Movement private AccelerationSetting GetCurrentAccSetting() { if (!m_dummyHitted) { return InAirAccSetting; } else { if (m_settingsDic.ContainsKey(m_dummyHit.collider.material)) { return m_settingsDic[m_dummyHit.collider.material]; } else { return DefaultGroundAccSetting; } } } private Vector3 CalcYAxis(Vector3 xaxis) { Vector3 result; result.y = xaxis.y; // Rotate 90 deg result.x = -xaxis.z; result.z = xaxis.x; return result; } private float CalcProjLength(Vector3 dest, Vector3 fromProj) { return Vector3.Dot(fromProj, dest) / dest.magnitude; } private float CalcAccOnAxis(float destSpeed, float curSpeed, ref AccelerationSetting accSetting) { if (destSpeed > curSpeed) { float offset = destSpeed - curSpeed; float possibleDecreaseSpeed = accSetting.DestAcc * Time.fixedDeltaTime; if (possibleDecreaseSpeed > offset) { possibleDecreaseSpeed = offset; } return possibleDecreaseSpeed / Time.fixedDeltaTime; } else if (destSpeed == curSpeed) { return 0.0f; } else { float offset = curSpeed - destSpeed; float possibleIncreaseSpeed = accSetting.DragAcc * Time.fixedDeltaTime; if (possibleIncreaseSpeed > offset) { possibleIncreaseSpeed = offset; } return -possibleIncreaseSpeed / Time.fixedDeltaTime; } } private Vector3 GetApplyAcc(Vector3 currentVel, Vector3 destVel) { Vector3 result = Vector3.zero; AccelerationSetting accSetting = GetCurrentAccSetting(); float curSpeed = currentVel.magnitude; float destSpeed = destVel.magnitude; if (Mathf.Approximately(destSpeed, 0.0f)) { if (Mathf.Approximately(curSpeed, 0.0f)) { return Vector3.zero; } float possibleDecreaseSpeed = accSetting.DragAcc * Time.fixedDeltaTime; if (possibleDecreaseSpeed > curSpeed) { possibleDecreaseSpeed = curSpeed; } result = -currentVel.normalized * (possibleDecreaseSpeed / Time.fixedDeltaTime); } else { Vector3 destDir = destVel.normalized; float destProj = CalcProjLength(destDir, currentVel); result += destDir * CalcAccOnAxis(destSpeed, destProj, ref accSetting); Vector3 destYDir = CalcYAxis(destDir); float destYProj = CalcProjLength(destYDir, currentVel); result += destYDir * CalcAccOnAxis(0.0f, destYProj, ref accSetting); } result.y = 0.0f; return result; } private void GetOnGround() { // Do not touch these code Vector3 originalPos = m_rigidbody.position; m_rigidbody.position += Vector3.up * OnGroundTestShiftLength; m_dummyHitted = m_rigidbody.SweepTest(Vector3.down, out m_dummyHit, OnGroundTestLength + OnGroundTestShiftLength); m_rigidbody.position = originalPos; } private bool m_dummyHitted; private RaycastHit m_dummyHit; private Rigidbody m_rigidbody; private Vector3 m_destVelocity; private Vector3 m_fixedUpdateVelocity; private Dictionary m_settingsDic = new Dictionary(); private void FixedUpdate_Movement() { GetOnGround(); Vector3 currentVel = m_rigidbody.velocity; Vector3 applyAcc = GetApplyAcc(currentVel, m_fixedUpdateVelocity); m_rigidbody.AddForce(applyAcc, ForceMode.Acceleration); } #endregion private void Awake() { m_rigidbody = GetComponent(); foreach (var setting in AdditionalGroundSettings) { m_settingsDic.Add(setting.OtherMaterial, setting.Setting); } AdditionalGroundSettings.Clear(); } private void FixedUpdate() { FixedUpdate_Movement(); } private void LateUpdate() { m_fixedUpdateVelocity = m_destVelocity; m_destVelocity = Vector3.zero; } }