using UnityEngine; public enum PartState { Installed, InHand, Dropped } public class CarPart : MonoBehaviour, IInteractable, ISecondaryInteractable { [Header("Данные")] [SerializeField] private CarPartData partData; [SerializeField] private float mass = 12f; [Header("Позиционирование в руках")] [Tooltip("Дополнительное смещение детали в руках, если она перекрывает весь обзор")] [SerializeField] private Vector3 inHandPositionOffset = Vector3.zero; [Tooltip("Поворот детали в руках (например, если бампер берется боком)")] [SerializeField] private Vector3 inHandRotationOffset = Vector3.zero; private CarPartAnimator partAnimator; private PartState currentState = PartState.Installed; private Transform originalParent; private Vector3 originalLocalPos; private Quaternion originalLocalRot; private Vector3 originalLocalScale; private Rigidbody rb; private Collider partCollider; private GameObject currentMountZone; public CarPartData Data => partData; public PartState State => currentState; private void Awake() { partCollider = GetComponent(); partAnimator = GetComponent(); originalParent = transform.parent; originalLocalPos = transform.localPosition; originalLocalRot = transform.localRotation; originalLocalScale = transform.localScale; } public string GetPrompt() { if (partData == null) return "Деталь (E)"; if (currentState == PartState.Installed) { // Если на детали есть аниматор — выводим подсказку с открытием if (partAnimator != null) { string action = partAnimator.IsOpen ? "Закрыть" : "Открыть"; return $"[E] {action} {partData.partName} | [R] Снять"; } return $"Снять {partData.partName} (E)"; } if (currentState == PartState.Dropped) { return $"Взять {partData.partName} (E)"; } return string.Empty; } public void Interact(GameObject interactor) { if (currentState == PartState.Installed) { if (partAnimator != null) { // Нажали E — открываем или закрываем partAnimator.TogglePart(); } else { // Обычная деталь (бампер) сразу снимается TakeToHands(interactor); } } else if (currentState == PartState.Dropped) { TakeToHands(interactor); } } // Вызывается при нажатии R (вторичное действие) public void SecondaryInteract(GameObject interactor) { if (currentState == PartState.Installed) { TakeToHands(interactor); } } // Вынесенный общий метод взятия в руки private void TakeToHands(GameObject interactor) { PlayerHands hands = interactor.GetComponent(); if (hands == null) hands = interactor.GetComponentInParent(); if (hands == null) return; if (currentState == PartState.Installed) { if (partAnimator != null) partAnimator.ResetToClosed(); CreateMountZone(); } hands.PickUp(this); } public void OnPickedUp(Transform holdPoint) { currentState = PartState.InHand; // Отключаем физику и коллайдер if (rb != null) Destroy(rb); partCollider.enabled = false; // 1. Привязываем к рукам и сохраняем оригинальный размер (чтобы не сплющило) transform.SetParent(holdPoint); transform.localScale = originalLocalScale; // 2. Задаем поворот вперед с учетом возможной ручной поправки transform.localRotation = Quaternion.Euler(inHandRotationOffset); transform.localPosition = Vector3.zero; // 3. АВТО-ЦЕНТРИРОВАНИЕ: компенсируем сбитый пивот геометрии Renderer meshRenderer = GetComponentInChildren(); if (meshRenderer != null) { // Находим, где сейчас находится визуальный центр геометрии в мире Vector3 visualCenterWorld = meshRenderer.bounds.center; // Вычисляем вектор сдвига, чтобы центр меша совпал с точкой HoldPoint Vector3 offsetToCenter = holdPoint.position - visualCenterWorld; // Смещаем деталь так, чтобы её реальный центр оказался в руках transform.position += offsetToCenter; // Применяем ручной оффсет, если захотите опустить деталь чуть ниже transform.localPosition += inHandPositionOffset; } } public void OnDropped(Vector3 throwForce) { currentState = PartState.Dropped; // Отвязываем от игрока transform.SetParent(null); transform.localScale = originalLocalScale; // Включаем коллайдер обратно partCollider.enabled = true; if (partCollider is MeshCollider meshCol) meshCol.convex = true; // Включаем физику rb = gameObject.AddComponent(); rb.mass = mass; rb.interpolation = RigidbodyInterpolation.Interpolate; if (throwForce != Vector3.zero) { rb.AddForce(throwForce, ForceMode.Impulse); } } public void Install() { currentState = PartState.Installed; if (rb != null) Destroy(rb); // Возвращаем на исходное место на машине transform.SetParent(originalParent); transform.localPosition = originalLocalPos; transform.localRotation = originalLocalRot; transform.localScale = originalLocalScale; partCollider.enabled = true; } private void CreateMountZone() { if (currentMountZone != null) return; currentMountZone = new GameObject($"MountZone_{gameObject.name}"); currentMountZone.transform.SetParent(originalParent); currentMountZone.transform.localPosition = originalLocalPos; currentMountZone.transform.localRotation = originalLocalRot; currentMountZone.transform.localScale = originalLocalScale; // Передаем оригинальный масштаб CarPartMountZone zone = currentMountZone.AddComponent(); zone.Initialize(this); } }