OldBlueWater/BlueWater/Assets/NWH/Common/Scripts/Scene/VehicleChanger.cs
2023-12-19 11:31:29 +09:00

423 lines
14 KiB
C#

using System.Collections.Generic;
using NWH.Common.Input;
using UnityEngine;
using NWH.Common.Vehicles;
using UnityEngine.Events;
#if UNITY_EDITOR
using NWH.NUI;
using UnityEditor;
#endif
namespace NWH.Common.SceneManagement
{
[DefaultExecutionOrder(500)]
public class VehicleChanger : MonoBehaviour
{
public enum CharacterLocation
{
OutOfRange,
Near,
Inside
}
/// <summary>
/// Index of the current vehicle in vehicles list.
/// </summary>
[Tooltip(" Index of the current vehicle in vehicles list.")]
public int activeVehicleIndex = 0;
/// <summary>
/// Should the vehicles that the player is currently not using be put to sleep to improve performance?
/// </summary>
[Tooltip(
" Should the vehicles that the player is currently not using be put to sleep to improve performance?")]
public bool putOtherVehiclesToSleep = true;
/// <summary>
/// List of all of the vehicles that can be selected and driven in the scene.
/// </summary>
[Tooltip("List of all of the vehicles that can be selected and driven in the scene. " +
"If set to 0 script will try to auto-find all the vehicles in the scene with a tag define by VehiclesTag parameter.")]
public List<Vehicle> vehicles = new List<Vehicle>();
/// <summary>
/// Is vehicle changing character based? When true changing vehicles will require getting close to them
/// to be able to enter, opposed to pressing a button to switch between vehicles.
/// </summary>
[Tooltip(
"Is vehicle changing character based? When true changing vehicles will require getting close to them\r\nto be able to enter, opposed to pressing a button to switch between vehicles.")]
public bool characterBased;
/// <summary>
/// Game object representing a character. Can also be another vehicle.
/// </summary>
[Tooltip(" Game object representing a character. Can also be another vehicle.")]
public GameObject characterObject;
/// <summary>
/// Maximum distance at which the character will be able to enter the vehicle.
/// </summary>
[Range(0.2f, 3f)]
[Tooltip(" Maximum distance at which the character will be able to enter the vehicle.")]
public float enterDistance = 2f;
/// <summary>
/// Tag of the object representing the point from which the enter distance will be measured. Useful if you want to
/// enable you character to enter only when near the door.
/// </summary>
[Tooltip(
"Tag of the object representing the point from which the enter distance will be measured. Useful if you want to enable you character to enter only when near the door.")]
public string enterExitTag = "EnterExitPoint";
/// <summary>
/// Maximum speed at which the character will be able to enter / exit the vehicle.
/// </summary>
[Tooltip(" Maximum speed at which the character will be able to enter / exit the vehicle.")]
public float maxEnterExitVehicleSpeed = 2f;
/// <summary>
/// When the location is Near, the player can enter the vehicle.
/// </summary>
[UnityEngine.Tooltip("When the location is Near, the player can enter the vehicle.")]
public CharacterLocation location = CharacterLocation.OutOfRange;
/// <summary>
/// Should the player start inside the vehicle?
/// </summary>
[UnityEngine.Tooltip("Should the player start inside the vehicle?")]
public bool startInVehicle = false;
public UnityEvent onVehicleChanged = new UnityEvent();
public UnityEvent onDeactivateAll = new UnityEvent();
/// <summary>
/// The vehicle the player is nearest to, or in case the player is inside the vehicle, the vehicle the player is inside of.
/// </summary>
private Vehicle _nearestVehicle;
private Vector3 _relativeEnterPosition;
private GameObject[] _enterExitPoints;
private GameObject _nearestEnterExitPoint;
public static VehicleChanger Instance { get; private set; }
private static Vehicle ActiveVehicle
{
get
{
if (Instance == null) return null;
return Instance.activeVehicleIndex < 0 || Instance.activeVehicleIndex >= Instance.vehicles.Count ?
null :
Instance.vehicles[Instance.activeVehicleIndex];
}
}
private void Awake()
{
Instance = this;
// Remove null vehicles from the vehicles list
for (int i = vehicles.Count - 1; i >= 0; i--)
{
if (vehicles[i] == null)
{
Debug.LogWarning("There is a null reference in the vehicles list. Removing. Make sure that" +
" vehicles list does not contain any null references.");
vehicles.RemoveAt(i);
}
}
}
private void Start()
{
if (characterBased && !startInVehicle)
{
DeactivateAllIncludingActive();
}
else
{
DeactivateAllExceptActive();
}
if (startInVehicle && ActiveVehicle != null)
{
EnterVehicle(ActiveVehicle);
_relativeEnterPosition = new Vector3(-2.5f, 1f, 0.5f); // There was no enter/exit point, make a guess
}
}
private void Update()
{
if (!characterBased)
{
bool changeVehicleInput = InputProvider.CombinedInput<SceneInputProviderBase>(i => i.ChangeVehicle());
if (changeVehicleInput)
{
NextVehicle();
}
}
else if (characterObject != null)
{
if (location != CharacterLocation.Inside)
{
location = CharacterLocation.OutOfRange;
if (!characterObject.activeSelf)
{
characterObject.SetActive(true);
}
_enterExitPoints = GameObject.FindGameObjectsWithTag(enterExitTag);
_nearestEnterExitPoint = null;
float nearestSqrDist = Mathf.Infinity;
foreach (GameObject eep in _enterExitPoints)
{
float sqrDist = Vector3.SqrMagnitude(characterObject.transform.position - eep.transform.position);
if (sqrDist < nearestSqrDist)
{
nearestSqrDist = sqrDist;
_nearestEnterExitPoint = eep;
}
}
if (_nearestEnterExitPoint == null)
{
return;
}
if (Vector3.Magnitude(Vector3.ProjectOnPlane(
_nearestEnterExitPoint.transform.position -
characterObject.transform.position,
Vector3.up)) < enterDistance)
{
location = CharacterLocation.Near;
_nearestVehicle = _nearestEnterExitPoint.GetComponentInParent<Vehicle>();
}
}
bool changeVehiclePressed = InputProvider.CombinedInput<SceneInputProviderBase>(i => i.ChangeVehicle());
if (InputProvider.Instances.Count > 0 && changeVehiclePressed)
{
// Enter vehicle
if (location == CharacterLocation.Near && _nearestVehicle.Speed < maxEnterExitVehicleSpeed)
{
EnterVehicle(_nearestVehicle);
}
// Exit vehicle
else if (location == CharacterLocation.Inside && _nearestVehicle.Speed < maxEnterExitVehicleSpeed)
{
ExitVehicle(_nearestVehicle);
}
}
}
}
public void EnterVehicle(Vehicle v)
{
_nearestVehicle = v;
if (characterBased)
{
characterObject.SetActive(false);
_relativeEnterPosition = v.transform.InverseTransformPoint(characterObject.transform.position);
location = CharacterLocation.Inside;
}
Instance.ChangeVehicle(v);
}
public void ExitVehicle(Vehicle v)
{
// Call deactivate all to deactivate on the same frame, preventing 2 audio listeners warning.
Instance.DeactivateAllIncludingActive();
location = CharacterLocation.OutOfRange;
if (characterBased)
{
characterObject.transform.position = v.transform.TransformPoint(_relativeEnterPosition);
characterObject.transform.forward = v.transform.right;
characterObject.transform.up = Vector3.up;
characterObject.SetActive(true);
}
}
public void RegisterVehicle(Vehicle v)
{
if (!vehicles.Contains(v))
{
vehicles.Add(v);
if (activeVehicleIndex != vehicles.Count - 1)
{
v.enabled = false;
}
}
}
public void DeregisterVehicle(Vehicle v)
{
if (ActiveVehicle == v)
{
NextVehicle();
}
vehicles.Remove(v);
}
/// <summary>
/// Changes vehicle to requested vehicle.
/// </summary>
/// <param name="index">Index of a vehicle in Vehicles list.</param>
public void ChangeVehicle(int index)
{
if (vehicles.Count == 0) return;
activeVehicleIndex = index;
if (activeVehicleIndex >= vehicles.Count)
{
activeVehicleIndex = 0;
}
DeactivateAllExceptActive();
onVehicleChanged.Invoke();
}
/// <summary>
/// Changes vehicle to a vehicle with the requested name if there is such a vehicle.
/// </summary>
public void ChangeVehicle(Vehicle ac)
{
int vehicleIndex = vehicles.IndexOf(ac);
if (vehicleIndex >= 0)
{
ChangeVehicle(vehicleIndex);
}
}
/// <summary>
/// Changes vehicle to a next vehicle on the Vehicles list.
/// </summary>
public void NextVehicle()
{
if (vehicles.Count == 1)
{
return;
}
ChangeVehicle(activeVehicleIndex + 1);
}
public void PreviousVehicle()
{
if (vehicles.Count == 1)
{
return;
}
int previousIndex = activeVehicleIndex == 0 ? vehicles.Count - 1 : activeVehicleIndex - 1;
ChangeVehicle(previousIndex);
}
public void DeactivateAllExceptActive()
{
for (int i = 0; i < vehicles.Count; i++)
{
if (i == activeVehicleIndex)
{
vehicles[i].enabled = true;
}
else if (putOtherVehiclesToSleep)
{
vehicles[i].enabled = false;
}
}
}
public void DeactivateAllIncludingActive()
{
for (int i = 0; i < vehicles.Count; i++)
{
vehicles[i].enabled = false;
}
onDeactivateAll.Invoke();
}
}
}
#if UNITY_EDITOR
namespace NWH.Common.SceneManagement
{
[CustomEditor(typeof(VehicleChanger))]
[CanEditMultipleObjects]
public class VehicleChangerEditor : NUIEditor
{
public override bool OnInspectorNUI()
{
if (!base.OnInspectorNUI())
{
return false;
}
VehicleChanger sc = drawer.GetObject<VehicleChanger>();
drawer.BeginSubsection("Vehicles");
drawer.ReorderableList("vehicles");
//drawer.Field("deactivateAll");
drawer.Field("putOtherVehiclesToSleep");
drawer.Field("activeVehicleIndex");
if (Application.isPlaying)
{
drawer.Label("Active Vehicle: " +
(Vehicle.ActiveVehicle == null ? "None" : Vehicle.ActiveVehicle.name));
}
drawer.EndSubsection();
drawer.BeginSubsection("Character-based Switching");
if (drawer.Field("characterBased").boolValue)
{
drawer.Field("characterObject");
drawer.Field("enterDistance", true, "m");
drawer.Field("startInVehicle");
drawer.Field("enterExitTag");
drawer.Field("maxEnterExitVehicleSpeed", true, "m/s");
drawer.Field("location", false);
}
drawer.EndSubsection();
drawer.EndEditor(this);
return true;
}
public override bool UseDefaultMargins()
{
return false;
}
}
}
#endif