183 lines
6.5 KiB
C#
183 lines
6.5 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class CarWheelCondition : MonoBehaviour
|
|
{
|
|
public enum TireState
|
|
{
|
|
Normal, // Целое накачанное колесо
|
|
Flat, // Спущено (нужно заменить)
|
|
Missing // Колеса изначально нет на ступице
|
|
}
|
|
|
|
// Хранит истинный начальный радиус каждого WheelCollider, чтобы новое колесо никогда не проваливалось
|
|
private static readonly Dictionary<WheelCollider, float> trueOriginalRadii = new Dictionary<WheelCollider, float>();
|
|
|
|
[Header("Состояние колеса")]
|
|
public TireState currentState = TireState.Normal;
|
|
|
|
[Header("Настройки деформации покрышки (Диск не затрагивается)")]
|
|
[Range(0.4f, 0.8f)] [SerializeField] private float rimRadiusRatio = 0.62f;
|
|
[Range(0.02f, 0.12f)] [SerializeField] private float flatSquashAmount = 0.055f;
|
|
[Range(0f, 0.06f)] [SerializeField] private float sidewallBulge = 0.025f;
|
|
|
|
private CarPart carPart;
|
|
private MeshFilter meshFilter;
|
|
private Mesh runtimeMesh;
|
|
private Vector3[] originalVertices;
|
|
private Vector3[] deformedVertices;
|
|
private float maxMeshRadius = 0.32f;
|
|
private Vector3 meshCenter;
|
|
private Quaternion lastDeformedRot;
|
|
|
|
public bool IsFlat => currentState == TireState.Flat;
|
|
|
|
private void Awake()
|
|
{
|
|
carPart = GetComponent<CarPart>();
|
|
meshFilter = GetComponent<MeshFilter>();
|
|
|
|
// Кэшируем истинный радиус всех WheelCollider машины до любых изменений
|
|
VehicleInteraction car = GetComponentInParent<VehicleInteraction>();
|
|
if (car != null)
|
|
{
|
|
foreach (WheelCollider wc in car.GetComponentsInChildren<WheelCollider>(true))
|
|
{
|
|
if (!trueOriginalRadii.ContainsKey(wc))
|
|
trueOriginalRadii[wc] = wc.radius;
|
|
}
|
|
}
|
|
|
|
if (meshFilter != null && meshFilter.sharedMesh != null && meshFilter.sharedMesh.isReadable)
|
|
{
|
|
runtimeMesh = Instantiate(meshFilter.sharedMesh);
|
|
meshFilter.mesh = runtimeMesh;
|
|
originalVertices = runtimeMesh.vertices;
|
|
deformedVertices = new Vector3[originalVertices.Length];
|
|
|
|
Bounds b = runtimeMesh.bounds;
|
|
meshCenter = b.center;
|
|
maxMeshRadius = Mathf.Max(b.extents.y, b.extents.z);
|
|
}
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
ApplyState(currentState);
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
// Обновляем вмятину покрышки только когда машина реально катится (чтобы не вызывать дрожь на месте)
|
|
if (currentState == TireState.Flat && runtimeMesh != null && carPart != null && carPart.State == PartState.Installed)
|
|
{
|
|
if (Quaternion.Angle(transform.rotation, lastDeformedRot) > 8f)
|
|
{
|
|
DeformTireMesh();
|
|
lastDeformedRot = transform.rotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ApplyState(TireState newState)
|
|
{
|
|
currentState = newState;
|
|
if (carPart == null) carPart = GetComponent<CarPart>();
|
|
|
|
if (currentState == TireState.Missing)
|
|
{
|
|
if (carPart != null && carPart.State == PartState.Installed)
|
|
carPart.MakeMissingAtStart();
|
|
return;
|
|
}
|
|
|
|
UpdateWheelColliderPhysics();
|
|
|
|
if (runtimeMesh != null)
|
|
{
|
|
if (currentState == TireState.Flat)
|
|
{
|
|
DeformTireMesh();
|
|
lastDeformedRot = transform.rotation;
|
|
}
|
|
else
|
|
{
|
|
runtimeMesh.vertices = originalVertices;
|
|
runtimeMesh.RecalculateBounds();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UpdateWheelColliderPhysics()
|
|
{
|
|
if (carPart == null) carPart = GetComponent<CarPart>();
|
|
if (carPart == null) return;
|
|
|
|
WheelCollider wc = carPart.LinkedWheelCollider;
|
|
if (wc == null) return;
|
|
|
|
if (!trueOriginalRadii.TryGetValue(wc, out float baseRadius))
|
|
{
|
|
baseRadius = wc.radius;
|
|
trueOriginalRadii[wc] = baseRadius;
|
|
}
|
|
|
|
if (currentState == TireState.Flat)
|
|
{
|
|
// Мягко занижаем радиус на 10% (без ударов днищем и без раскачки подвески)
|
|
wc.radius = baseRadius * 0.90f;
|
|
WheelFrictionCurve side = wc.sidewaysFriction;
|
|
side.stiffness = 1.0f;
|
|
wc.sidewaysFriction = side;
|
|
}
|
|
else
|
|
{
|
|
// ГАРАНТИРОВАННО возвращаем 100% заводской радиус при установке целого колеса!
|
|
wc.radius = baseRadius;
|
|
WheelFrictionCurve side = wc.sidewaysFriction;
|
|
side.stiffness = 2.0f;
|
|
wc.sidewaysFriction = side;
|
|
}
|
|
}
|
|
|
|
private void DeformTireMesh()
|
|
{
|
|
if (originalVertices == null) return;
|
|
|
|
float rimRadius = maxMeshRadius * rimRadiusRatio;
|
|
Vector3 localDown = (carPart != null && carPart.State == PartState.Installed)
|
|
? transform.InverseTransformDirection(Vector3.down).normalized
|
|
: Vector3.down;
|
|
|
|
for (int i = 0; i < originalVertices.Length; i++)
|
|
{
|
|
Vector3 v = originalVertices[i];
|
|
Vector3 fromCenter = v - meshCenter;
|
|
float radialDist = Mathf.Sqrt(fromCenter.y * fromCenter.y + fromCenter.z * fromCenter.z);
|
|
|
|
if (radialDist <= rimRadius)
|
|
{
|
|
deformedVertices[i] = v;
|
|
continue;
|
|
}
|
|
|
|
float rubberFactor = Mathf.InverseLerp(rimRadius, maxMeshRadius, radialDist);
|
|
Vector3 radialDir = new Vector3(0f, fromCenter.y, fromCenter.z).normalized;
|
|
float bottomDot = Vector3.Dot(radialDir, localDown);
|
|
|
|
if (bottomDot > 0.30f)
|
|
{
|
|
float bottomFactor = Mathf.InverseLerp(0.30f, 1.0f, bottomDot);
|
|
float squash = bottomFactor * rubberFactor * flatSquashAmount;
|
|
|
|
v -= localDown * squash;
|
|
float sideSign = Mathf.Sign(fromCenter.x);
|
|
v.x += sideSign * (bottomFactor * Mathf.Sin(rubberFactor * Mathf.PI) * sidewallBulge);
|
|
}
|
|
|
|
deformedVertices[i] = v;
|
|
}
|
|
|
|
runtimeMesh.vertices = deformedVertices;
|
|
}
|
|
} |