Files
Fishing2/Packages/com.nwh.common/Runtime/Vehicle/Vehicle.cs
2026-02-27 17:44:21 +08:00

309 lines
10 KiB
C#

// ╔════════════════════════════════════════════════════════════════╗
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
// ║ Licensed under Unity Asset Store Terms of Service: ║
// ║ https://unity.com/legal/as-terms ║
// ║ Use permitted only in compliance with the License. ║
// ║ Distributed "AS IS", without warranty of any kind. ║
// ╚════════════════════════════════════════════════════════════════╝
#region
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
#endregion
namespace NWH.Common.Vehicles
{
/// <summary>
/// Base class for all NWH vehicles including VehiclePhysics2.VehicleController and DWP2.AdvancedShipController.
/// </summary>
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
public abstract class Vehicle : MonoBehaviour
{
/// <summary>
/// Any input below this value will register as no input.
/// </summary>
public const float INPUT_DEADZONE = 0.02f;
/// <summary>
/// Any speed below this value will register as no speed.
/// </summary>
public const float SPEED_DEADZONE = 0.2f;
/// <summary>
/// Anything below this can be considered 0.
/// </summary>
public const float SMALL_NUMBER = 1e-5f;
/// <summary>
/// Like SMALL_NUMBER but a bit bigger.
/// </summary>
public const float KINDA_SMALL_NUMBER = 0.01f;
public static List<Vehicle> ActiveVehicles = new();
/// <summary>
/// Called when active vehicle is changed.
/// First parameter is the previously active vehicle and the second parameter is the currently active vehicle
/// (at the time of the callback). Params can be null.
/// </summary>
public static UnityEvent<Vehicle, Vehicle> onActiveVehicleChanged = new();
/// <summary>
/// True if the vehicle can be driven by the player. False if the
/// vehicle is passive (such as a trailer). A vehicle that has isPlayerControllable
/// can be the ActiveVehicle.
/// </summary>
public bool isPlayerControllable = true;
/// <summary>
/// Cached value of vehicle rigidbody.
/// </summary>
[Tooltip(" Cached value of vehicle rigidbody.")]
[NonSerialized]
public Rigidbody vehicleRigidbody;
/// <summary>
/// Cached value of vehicle transform.
/// </summary>
[Tooltip(" Cached value of vehicle transform.")]
[NonSerialized]
public Transform vehicleTransform;
// Points to the last enabled vehicle.
public static Vehicle ActiveVehicle
{
get
{
int activeVehicleCount = ActiveVehicles.Count;
if (activeVehicleCount == 0)
{
return null;
}
return ActiveVehicles[activeVehicleCount - 1];
}
}
public virtual void Awake()
{
vehicleTransform = transform;
vehicleRigidbody = GetComponent<Rigidbody>();
vehicleRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
}
public virtual void FixedUpdate()
{
// Pre-calculate values
_prevLocalVelocity = LocalVelocity;
Velocity = vehicleRigidbody.linearVelocity;
LocalVelocity = transform.InverseTransformDirection(Velocity);
LocalAcceleration = (LocalVelocity - _prevLocalVelocity) / Time.fixedDeltaTime;
LocalForwardVelocity = LocalVelocity.z;
LocalForwardAcceleration = LocalAcceleration.z;
VelocityMagnitude = Velocity.magnitude;
AngularVelocity = vehicleRigidbody.angularVelocity;
AngularVelocityMagnitude = AngularVelocity.magnitude;
}
public virtual void OnEnable()
{
onEnable.Invoke();
if (isPlayerControllable)
{
if (!ActiveVehicles.Contains(this))
{
Vehicle previouslyActiveVehicle = ActiveVehicle;
ActiveVehicles.Add(this);
Vehicle currentlyActiveVehicle = ActiveVehicle;
onActiveVehicleChanged.Invoke(previouslyActiveVehicle, currentlyActiveVehicle);
}
}
}
public virtual void OnDisable()
{
onDisable.Invoke();
if (isPlayerControllable)
{
Vehicle previouslyActiveVehicle = ActiveVehicle;
ActiveVehicles.RemoveAll(vehicle => vehicle == this);
Vehicle currentlyActiveVehicle = ActiveVehicle;
onActiveVehicleChanged.Invoke(previouslyActiveVehicle, currentlyActiveVehicle);
}
}
#region CAMERA
/// <summary>
/// True when camera is inside vehicle (cockpit, cabin, etc.).
/// Set by the 'CameraInsideVehicle' component.
/// Used for audio effects.
/// </summary>
public bool CameraInsideVehicle
{
get { return _cameraInsideVehicle; }
set
{
if (_cameraInsideVehicle && !value)
{
onCameraExitVehicle.Invoke();
}
else if (!CameraInsideVehicle && value)
{
onCameraEnterVehicle.Invoke();
}
_cameraInsideVehicle = value;
}
}
private bool _cameraInsideVehicle;
/// <summary>
/// Called when the camera enters the vehicle.
/// </summary>
public UnityEvent onCameraEnterVehicle = new();
/// <summary>
/// Called when the camera exits the vehicle.
/// </summary>
public UnityEvent onCameraExitVehicle = new();
#endregion
#region EVENTS
/// <summary>
/// Called when vehicle is put to sleep.
/// </summary>
[Tooltip(" Called when vehicle is put to sleep.")]
[NonSerialized]
public UnityEvent onDisable = new();
/// <summary>
/// Called when vehicle is woken up.
/// </summary>
[Tooltip(" Called when vehicle is woken up.")]
[NonSerialized]
public UnityEvent onEnable = new();
#endregion
#region MULTIPLAYER
/// <summary>
/// Determines if vehicle is running locally is synchronized over active multiplayer framework.
/// </summary>
[Tooltip(" Determines if vehicle is running locally is synchronized over active multiplayer framework.")]
private bool _multiplayerIsRemote;
/// <summary>
/// True if the vehicle is a client (remote) and not simulated.
/// If true the input is expected to be synced through the network.
/// </summary>
public bool MultiplayerIsRemote
{
get { return _multiplayerIsRemote; }
set
{
if (_multiplayerIsRemote && !value)
{
onMultiplayerStatusChanged.Invoke(false);
}
else if (!_multiplayerIsRemote && value)
{
onMultiplayerStatusChanged.Invoke(true);
}
_multiplayerIsRemote = value;
}
}
/// <summary>
/// Invoked when MultiplayerIsRemote value gets changed.
/// Is true if remote.
/// </summary>
public UnityEvent<bool> onMultiplayerStatusChanged = new();
#endregion
#region PHYSICAL_PROPERTIES
/// <summary>
/// Cached acceleration in local coordinates (z-forward)
/// </summary>
public Vector3 LocalAcceleration { get; private set; }
/// <summary>
/// Cached acceleration in forward direction in local coordinates (z-forward).
/// </summary>
public float LocalForwardAcceleration { get; private set; }
/// <summary>
/// Cached velocity in forward direction in local coordinates (z-forward).
/// </summary>
public float LocalForwardVelocity { get; private set; }
/// <summary>
/// Cached velocity in m/s in local coordinates.
/// </summary>
[ShowInTelemetry]
public Vector3 LocalVelocity { get; private set; }
/// <summary>
/// Cached speed of the vehicle in the forward direction. ALWAYS POSITIVE.
/// For positive/negative version use SpeedSigned.
/// </summary>
[ShowInTelemetry(format: "0.0", unit: "m/s")]
public float Speed
{
get { return LocalForwardVelocity < 0 ? -LocalForwardVelocity : LocalForwardVelocity; }
}
/// <summary>
/// Cached speed of the vehicle in the forward direction. Can be positive (forward) or negative (reverse).
/// Equal to LocalForwardVelocity.
/// </summary>
[ShowInTelemetry(format: "0.0", unit: "m/s")]
public float SpeedSigned
{
get { return LocalForwardVelocity; }
}
/// <summary>
/// Cached velocity of the vehicle in world coordinates.
/// </summary>
public Vector3 Velocity { get; protected set; }
/// <summary>
/// Cached velocity magnitude of the vehicle in world coordinates.
/// </summary>
public float VelocityMagnitude { get; protected set; }
/// <summary>
/// Cached angular velocity of the vehicle.
/// </summary>
public Vector3 AngularVelocity { get; protected set; }
/// <summary>
/// Cached angular velocity maginitude of the vehicle.
/// </summary>
public float AngularVelocityMagnitude { get; protected set; }
private Vector3 _prevLocalVelocity;
#endregion
}
}