Perekyp/Assets/Scripts/Car/Repair/CarWheelCondition.cs

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;
}
}