WXMC/.svn/pristine/55/55db68f15639a7549b0260546c64b2ee39c66d3d.svn-base
2024-12-04 16:18:46 +08:00

358 lines
9.8 KiB
Plaintext

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysicsMovement2D : MonoBehaviour
{
#region Define
public const float OnGroundTestLength = 0.02f;
[System.Serializable]
public struct AccelerationBindSetting
{
public PhysicMaterial OtherMaterial;
public AccelerationSetting Setting;
}
[System.Serializable]
public struct AccelerationSetting
{
public float DestAcc;
public float DragAcc;
}
private enum Collider2DTypes { None, Box, Sphere, Capsule };
#endregion
#region Interface
public Vector2 DestVelocity
{
get => m_destVelocity;
set
{
m_destVelocity = value;
}
}
public bool IsOnGround
{
get
{
return m_dummyHitted;
}
}
#endregion
#region Setting
public bool MoveOnXY = false;
public AccelerationSetting InAirAccSetting;
public AccelerationSetting DefaultGroundAccSetting;
public List<AccelerationBindSetting> AdditionalGroundSettings;
#endregion
#region Movement
private AccelerationSetting GetCurrentAccSetting()
{
if (!m_dummyHitted)
{
return InAirAccSetting;
}
else
{
return DefaultGroundAccSetting;
}
}
private Vector2 CalcYAxis(Vector2 xaxis)
{
Vector2 result;
// Rotate 90 deg
result.x = -xaxis.y;
result.y = xaxis.x;
return result;
}
private float CalcProjLength(Vector2 dest, Vector2 fromProj)
{
return Vector2.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 Vector2 GetApplyAcc(Vector2 currentVel, Vector2 destVel)
{
Vector2 result = Vector2.zero;
AccelerationSetting accSetting = GetCurrentAccSetting();
if (MoveOnXY)
{
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
{
Vector2 destDir = destVel.normalized;
float destProj = CalcProjLength(destDir, currentVel);
result += destDir * CalcAccOnAxis(destSpeed, destProj, ref accSetting);
Vector2 destYDir = CalcYAxis(destDir);
float destYProj = CalcProjLength(destYDir, currentVel);
result += destYDir * CalcAccOnAxis(0.0f, destYProj, ref accSetting);
}
}
else
{
float curSpeed = Mathf.Abs(currentVel.x);
float destSpeed = Mathf.Abs(destVel.x);
if (Mathf.Approximately(destVel.x, 0.0f))
{
if (currentVel.x < 0.0f)
{
result -= Vector2.right * CalcAccOnAxis(destSpeed, curSpeed, ref accSetting);
}
else
{
result += Vector2.right * CalcAccOnAxis(destSpeed, curSpeed, ref accSetting);
}
}
else
{
Vector2 destDir = destVel.normalized;
float destProj = currentVel.x;
if (destVel.x < 0.0f)
{
destProj *= -1.0f;
}
result += destDir * CalcAccOnAxis(destSpeed, destProj, ref accSetting);
}
result.y = 0.0f;
}
return result;
}
private void GetOnGround()
{
// Jumping is disable in XY mode
if (MoveOnXY)
{
m_dummyHitted = true;
return;
}
Collider2D compareCollider = null;
int addCount = 0;
// Do not touch these code
switch (m_colliderType)
{
case Collider2DTypes.None:
{
Vector2 colliderPos = m_rigidbody.position;
m_dummyHitted = Physics2D.Raycast(m_rigidbody.position, Vector2.down, OnGroundTestLength);
return;
}
case Collider2DTypes.Box:
{
Vector3 colliderPos3 = m_boxCollider.transform.position;
Vector2 colliderPos = new Vector2(colliderPos3.x, colliderPos3.y);
addCount = Physics2D.OverlapBoxNonAlloc(colliderPos + Vector2.down * OnGroundTestLength, m_boxCollider.size, m_boxCollider.transform.eulerAngles.z, m_dummyContainer);
compareCollider = m_boxCollider;
}
break;
case Collider2DTypes.Sphere:
{
Vector3 colliderPos3 = m_circleCollider.transform.position;
Vector2 colliderPos = new Vector2(colliderPos3.x, colliderPos3.y);
addCount = Physics2D.OverlapCircleNonAlloc(colliderPos + Vector2.down * OnGroundTestLength, m_circleCollider.radius, m_dummyContainer);
compareCollider = m_circleCollider;
}
break;
case Collider2DTypes.Capsule:
{
Vector3 colliderPos3 = m_capsuleCollider.transform.position;
Vector2 colliderPos = new Vector2(colliderPos3.x, colliderPos3.y);
addCount = Physics2D.OverlapCapsuleNonAlloc(colliderPos + Vector2.down * OnGroundTestLength, m_capsuleCollider.size, m_capsuleCollider.direction, m_capsuleCollider.transform.eulerAngles.z, m_dummyContainer);
compareCollider = m_capsuleCollider;
}
break;
default:
break;
}
int noneSelfCollider = 0;
for (int i = 0; i < addCount; i++)
{
if (m_dummyContainer[i])
{
if (m_dummyContainer[i] != compareCollider)
{
noneSelfCollider++;
}
m_dummyContainer[i] = null;
}
else
{
break;
}
}
m_dummyHitted = noneSelfCollider > 0;
}
[SerializeField]
private bool m_dummyHitted;
private RaycastHit2D m_dummyHit;
private Collider2D[] m_dummyContainer = new Collider2D[10];
private Rigidbody2D m_rigidbody;
private Collider2DTypes m_colliderType;
private CircleCollider2D m_circleCollider;
private BoxCollider2D m_boxCollider;
private CapsuleCollider2D m_capsuleCollider;
protected Vector2 m_destVelocity;
private Vector2 m_fixedUpdateVelocity;
private Dictionary<PhysicMaterial, AccelerationSetting> m_settingsDic = new Dictionary<PhysicMaterial, AccelerationSetting>();
public Rigidbody2D RigidBody
{
get
{
return m_rigidbody;
}
}
private void FixedUpdate_Movement()
{
GetOnGround();
Vector2 currentVel = m_rigidbody.velocity;
Vector2 applyAcc = GetApplyAcc(currentVel, m_fixedUpdateVelocity);
m_rigidbody.AddForce(applyAcc * m_rigidbody.mass, ForceMode2D.Force);
}
#endregion
private T FindComponent<T>()
{
T result = GetComponent<T>();
if (result == null)
{
return GetComponentInChildren<T>();
}
return result;
}
private void Awake()
{
m_rigidbody = GetComponent<Rigidbody2D>();
m_circleCollider = FindComponent<CircleCollider2D>();
if (m_circleCollider)
{
m_colliderType = Collider2DTypes.Sphere;
}
else
{
m_boxCollider = FindComponent<BoxCollider2D>();
if (m_boxCollider)
{
m_colliderType = Collider2DTypes.Box;
}
else
{
m_capsuleCollider = FindComponent<CapsuleCollider2D>();
if (m_capsuleCollider)
{
m_colliderType = Collider2DTypes.Capsule;
}
else
{
m_colliderType = Collider2DTypes.None;
}
}
}
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 = Vector2.zero;
}
}