Perekyp/Assets/Scripts/Car/Part/CarPart.cs

200 lines
7.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Collider>();
partAnimator = GetComponent<CarPartAnimator>();
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<PlayerHands>();
if (hands == null) hands = interactor.GetComponentInParent<PlayerHands>();
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<Renderer>();
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<Rigidbody>();
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<CarPartMountZone>();
zone.Initialize(this);
}
}