Files
RoadRunner/Assets/Gley/TrafficSystem/Scripts/Editor/Other/VehicleComponentEditor.cs
Kasper Karlgren c84494a697 new files
2025-01-21 13:54:40 +01:00

590 lines
24 KiB
C#

using Gley.TrafficSystem.Internal;
using Gley.UrbanSystem.Editor;
using Gley.UrbanSystem.Internal;
using UnityEditor;
using UnityEngine;
namespace Gley.TrafficSystem.Editor
{
[CustomEditor(typeof(VehicleComponent))]
public class VehicleComponentEditor : UnityEditor.Editor
{
const string _carHolderName = "CarHolder";
const string _frontTriggerHolderName = "FrontTriggerHolder";
const string _frontTriggerName = "FrontTrigger";
const string _wheelsHolderName = "Wheels";
const string _shadowHolderName = "ShadowHolder";
const string _maxTriggerLengthName = "Max Trigger Length";
const string _updateTriggerName = "Update Trigger";
const string _trailerComponentName = "Trailer Component";
const string _trailerAnchorPointName = "Trailer Connection Point";
private VehicleComponent _targetScript;
private void OnEnable()
{
_targetScript = (VehicleComponent)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUI.BeginChangeCheck();
_targetScript.updateTrigger = EditorGUILayout.Toggle(new GUIContent(_updateTriggerName, "It checked, vehicle trigger length will increase with speed"), _targetScript.updateTrigger);
if (_targetScript.updateTrigger)
{
_targetScript.maxTriggerLength = EditorGUILayout.FloatField(new GUIContent(_maxTriggerLengthName, "The length of the front trigger at max speed"), _targetScript.maxTriggerLength);
}
_targetScript.trailer = (TrailerComponent)EditorGUILayout.ObjectField(_trailerComponentName, _targetScript.trailer, typeof(TrailerComponent), true);
if(_targetScript.trailer)
{
_targetScript.trailerConnectionPoint = (Transform)EditorGUILayout.ObjectField(_trailerAnchorPointName, _targetScript.trailerConnectionPoint, typeof(Transform), true);
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Automatically Computed", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Length: " + _targetScript.length.ToString());
EditorGUILayout.LabelField("Collider Height: " + _targetScript.coliderHeight.ToString());
EditorGUILayout.LabelField("Wheel Distance: " + _targetScript.wheelDistance.ToString());
EditorGUILayout.LabelField("Excluded: " + _targetScript.excluded.ToString());
if (EditorGUI.EndChangeCheck())
{
Save(_targetScript);
}
EditorGUILayout.Space();
if (GUILayout.Button("Configure Car"))
{
ConfigureCar(_targetScript);
}
EditorGUILayout.Space();
if (GUILayout.Button("View Tutorial"))
{
Application.OpenURL("https://youtu.be/moGHcd2Jaa4");
}
}
public static void ConfigureCar(VehicleComponent targetScript)
{
bool correct = true;
SetCarHolder(targetScript, ref correct);
SetupRigidbody(targetScript, ref correct);
AssignWheels(targetScript, ref correct);
CreateFrontTrigger(targetScript, ref correct);
AddShadow(targetScript);
SetPivotOnBackWheels(targetScript, ref correct);
SetWheelDimensions(targetScript, ref correct);
CheckForZero(targetScript.accelerationTime, nameof(targetScript.accelerationTime), ref correct);
CheckForZero(targetScript.minPossibleSpeed, nameof(targetScript.minPossibleSpeed), ref correct);
CheckForZero(targetScript.maxPossibleSpeed, nameof(targetScript.maxPossibleSpeed), ref correct);
AssignLights(targetScript);
AssignVisibilityScript(targetScript);
CheckColliers(targetScript, ref correct);
ConnectTrailer(targetScript, ref correct);
if (correct)
{
Debug.Log("Success! All references for " + targetScript.name + " ware correct");
Save(targetScript);
}
else
{
Debug.LogError(targetScript.name + " will not work correctly. See above messages for details");
}
}
private static void ConnectTrailer(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
if (targetScript.trailer == null)
return;
if(targetScript.trailerConnectionPoint==null)
{
LogError(ref correct, "Trailer Connection Point is not assigned. " +
"Please assign it on Vehicle Component.");
return;
}
targetScript.trailer.transform.position = new Vector3(targetScript.trailerConnectionPoint.position.x,targetScript.carHolder.position.y, targetScript.trailerConnectionPoint.position.z);
if(targetScript.trailer.joint==null)
{
LogError(ref correct, "No joint assigned. Please Configure Trailer first.");
return;
}
if(targetScript.trailer.truckConnectionPoint==null)
{
LogError(ref correct, "No truckConnectionPoint assigned. Please Configure Trailer first.");
return;
}
targetScript.trailer.joint.connectedBody = targetScript.rb;
targetScript.trailer.joint.autoConfigureConnectedAnchor = false;
targetScript.trailer.joint.anchor = targetScript.trailer.truckConnectionPoint.GetLocalPositionRelativeToTopParent(targetScript.trailer.transform);
targetScript.trailer.joint.connectedAnchor = targetScript.trailerConnectionPoint.GetLocalPositionRelativeToTopParent(targetScript.transform);
targetScript.trailer.rb.mass = targetScript.rb.mass;
#if UNITY_6000_0_OR_NEWER
targetScript.trailer.rb.linearDamping = targetScript.rb.linearDamping;
targetScript.trailer.rb.angularDamping = targetScript.rb.angularDamping;
#else
targetScript.trailer.rb.drag = targetScript.rb.drag;
targetScript.trailer.rb.angularDrag = targetScript.rb.angularDrag;
#endif
}
static void Save(VehicleComponent targetScript)
{
EditorUtility.SetDirty(targetScript);
AssetDatabase.SaveAssets();
}
private static void CheckColliers(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
Collider[] allColliders = targetScript.carHolder.GetComponentsInChildren<Collider>();
targetScript.coliderHeight = 0;
bool hasColliders = false;
for (int i = 0; i < allColliders.Length; i++)
{
if (!allColliders[i].isTrigger)
{
float colliderDimension = allColliders[i].bounds.size.y * allColliders[i].transform.lossyScale.y;
if (targetScript.coliderHeight < colliderDimension)
{
targetScript.coliderHeight = colliderDimension;
}
hasColliders = true;
}
}
if (!hasColliders)
{
LogError(ref correct, "No collider found -> Please assign a collider on the car.");
}
if (targetScript.coliderHeight == 0)
{
LogError(ref correct, "Collider height is 0 -> Verify your colliders or press configure car in prefab mode.");
}
}
private static void AssignVisibilityScript(VehicleComponent targetScript)
{
VisibilityScript[] allComponents = targetScript.gameObject.GetComponentsInChildren<VisibilityScript>();
if (allComponents.Length > 1)
{
for (int i = 1; i < allComponents.Length; i++)
{
DestroyImmediate(allComponents[i]);
}
}
if (allComponents.Length == 1)
{
if (allComponents[0].GetComponent<Renderer>() != null)
{
targetScript.visibilityScript = allComponents[0];
return;
}
else
{
DestroyImmediate(allComponents[0]);
}
}
Renderer renderer = targetScript.GetComponentInChildren<Renderer>();
targetScript.visibilityScript = renderer.gameObject.AddComponent<VisibilityScript>();
}
private static void AssignLights(VehicleComponent targetScript)
{
VehicleLightsComponent vehicleLights = targetScript.GetComponent<VehicleLightsComponent>();
if (vehicleLights)
{
AssignGameobject(targetScript, ref vehicleLights.frontLights, "FrontLights");
AssignGameobject(targetScript, ref vehicleLights.reverseLights, "ReverseLights");
AssignGameobject(targetScript, ref vehicleLights.rearLights, "RearLights");
AssignGameobject(targetScript, ref vehicleLights.stopLights, "StopLights");
AssignGameobject(targetScript, ref vehicleLights.blinkerLeft, "BlinkersLeft");
AssignGameobject(targetScript, ref vehicleLights.blinkerRight, "BlinkersRight");
}
}
private static void AssignGameobject(VehicleComponent targetScript, ref GameObject objToAssign, string objectName)
{
Transform go = targetScript.transform.FindDeepChild(objectName);
if (go == null)
{
Debug.Log(objectName + " not found");
return;
}
objToAssign = go.gameObject;
}
private static void SetWheelDimensions(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
for (int i = 0; i < targetScript.allWheels.Length; i++)
{
MeshFilter[] allMeshes = targetScript.allWheels[i].wheelTransform.GetComponentsInChildren<MeshFilter>();
if (allMeshes.Length == 0)
{
Debug.LogWarning("No meshes found inside " + targetScript.allWheels[0].wheelTransform);
}
float maxSize = 0;
float wheelRadius = 0;
//create mesh
for (int j = 0; j < allMeshes.Length; j++)
{
float xSize = allMeshes[j].sharedMesh.bounds.size.x * allMeshes[j].transform.lossyScale.x;
float ySize = allMeshes[j].sharedMesh.bounds.size.y * allMeshes[j].transform.lossyScale.y;
float zSize = allMeshes[j].sharedMesh.bounds.size.z * allMeshes[j].transform.lossyScale.z;
bool changed = false;
if (xSize > maxSize)
{
maxSize = xSize;
changed = true;
}
if (ySize > maxSize)
{
maxSize = ySize;
changed = true;
}
if (zSize > maxSize)
{
maxSize = zSize;
changed = true;
}
if (changed)
{
wheelRadius = Mathf.Max(xSize, ySize, zSize);
wheelRadius /= 2;
}
}
if (targetScript.allWheels[i].wheelRadius == 0)
{
targetScript.allWheels[i].wheelRadius = wheelRadius;
}
if (targetScript.maxSuspension == 0)
{
targetScript.allWheels[i].maxSuspension = wheelRadius / 2;
}
else
{
targetScript.allWheels[i].maxSuspension = targetScript.maxSuspension;
}
targetScript.allWheels[i].raycastLength = targetScript.allWheels[i].wheelRadius + targetScript.allWheels[i].maxSuspension;
targetScript.allWheels[i].wheelCircumference = targetScript.allWheels[i].wheelRadius * Mathf.PI * 2;
}
}
private static void CheckForZero(float value, string propertyName, ref bool correct)
{
if (value <= 0)
{
LogError(ref correct, propertyName + " needs to be > 0");
}
}
private static void SetPivotOnBackWheels(VehicleComponent targetScript, ref bool correct)
{
Vector3 poz = new Vector3();
int nr = 0;
for (int i = 0; i < targetScript.allWheels.Length; i++)
{
if (targetScript.allWheels[i].wheelPosition == Wheel.WheelPosition.Back)
{
nr++;
poz += targetScript.allWheels[i].wheelTransform.localPosition;
}
}
if (nr == 0)
{
LogError(ref correct, "No back wheels set. Mark Wheel Position as Back for the back car wheels");
return;
}
targetScript.carHolder.localPosition = new Vector3(0, 0, -(poz / nr).z);
}
private static void AssignWheels(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
Transform wheelsHolder = targetScript.carHolder.Find(_wheelsHolderName);
if (wheelsHolder == null)
{
for (int i = 0; i < targetScript.allWheels.Length; i++)
{
if (targetScript.allWheels[i].wheelTransform == null)
{
LogError(ref correct, "A GameObject named " + _wheelsHolderName + " was not found under " + targetScript.name);
return;
}
}
Debug.Log("All wheels ware manually assigned, make sure they are correct");
return;
}
//verify if wheels ware already assigned
bool allAssigned = true;
if (targetScript.allWheels != null)
{
if (targetScript.allWheels.Length < 2)
{
allAssigned = false;
}
for (int i = 0; i < targetScript.allWheels.Length; i++)
{
if (targetScript.allWheels[i].wheelTransform == null)
{
allAssigned = false;
}
}
}
else
{
allAssigned = false;
}
if (allAssigned == true)
{
Debug.Log("All wheels ware manually assigned, make sure they are correct");
return;
}
if (wheelsHolder.childCount == 0)
{
LogError(ref correct, "No GameObject was not found under " + _wheelsHolderName);
}
targetScript.allWheels = new Wheel[wheelsHolder.childCount];
for (int i = 0; i < wheelsHolder.childCount; i++)
{
targetScript.allWheels[i] = new Wheel();
targetScript.allWheels[i].wheelTransform = wheelsHolder.GetChild(i);
if (targetScript.allWheels[i].wheelTransform.name.Contains("Front"))
{
targetScript.allWheels[i].wheelPosition = Wheel.WheelPosition.Front;
}
if (targetScript.allWheels[i].wheelTransform.name.Contains("Back") || targetScript.allWheels[i].wheelTransform.name.Contains("Rear"))
{
targetScript.allWheels[i].wheelPosition = Wheel.WheelPosition.Back;
}
try
{
targetScript.allWheels[i].wheelGraphics = wheelsHolder.GetChild(i).GetChild(0);
}
catch
{
LogError(ref correct, "No GameObject was not found under " + wheelsHolder.GetChild(i).name);
}
}
}
private static void AddShadow(VehicleComponent targetScript)
{
Transform shadowHolder = targetScript.carHolder.transform.Find(_shadowHolderName);
if (shadowHolder)
{
targetScript.shadowHolder = shadowHolder;
}
else
{
Debug.Log("Shadow not found. Please assign the shadow manually if you need it");
}
}
private static void CreateFrontTrigger(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
float triggerLength = targetScript.distanceToStop + targetScript.triggerLength;
float triggerHeight = 1;
Transform frontTriggerHolder = targetScript.carHolder.transform.Find(_frontTriggerHolderName);
GleyPrefabUtilities.DestroyTransform(frontTriggerHolder);
if (frontTriggerHolder == null)
{
if (GleyPrefabUtilities.EditingInsidePrefab())
{
GameObject prefabRoot = GleyPrefabUtilities.GetScenePrefabRoot();
frontTriggerHolder = new GameObject(_frontTriggerHolderName).transform;
frontTriggerHolder.SetParent(prefabRoot.transform.Find(targetScript.carHolder.name).transform, false);
}
else
{
frontTriggerHolder = new GameObject(_frontTriggerHolderName).transform;
frontTriggerHolder.SetParent(targetScript.carHolder.transform, false);
}
}
Vector3 triggerPoz = new Vector3();
int nr = 0;
for (int i = 0; i < targetScript.allWheels.Length; i++)
{
if (targetScript.allWheels[i].wheelPosition == Wheel.WheelPosition.Front)
{
nr++;
triggerPoz += targetScript.allWheels[i].wheelTransform.position;
}
}
if (nr == 0)
{
LogError(ref correct, "No front wheels set. Mark Wheel Position as Front for the front car wheels");
return;
}
triggerPoz = triggerPoz / nr;
frontTriggerHolder.position = triggerPoz;
targetScript.frontTrigger = frontTriggerHolder;
targetScript.wheelDistance = targetScript.transform.InverseTransformPoint(triggerPoz).z;
if (targetScript.wheelDistance == 0)
{
LogError(ref correct, " Distance between wheels is 0. Make sure your wheel assignments are correct");
return;
}
Transform frontTrigger = frontTriggerHolder.Find(_frontTriggerName);
if (frontTrigger == null)
{
MeshFilter[] allMeshes = targetScript.carHolder.transform.GetComponentsInChildren<MeshFilter>();
if (allMeshes.Length == 0)
{
LogError(ref correct, "No meshes found inside " + targetScript.name);
return;
}
float maxSize = 0;
float triggerSize = 0;
//create mesh
for (int i = 0; i < allMeshes.Length; i++)
{
float xSize = allMeshes[i].sharedMesh.bounds.size.x * allMeshes[i].transform.localScale.x;
float ySize = allMeshes[i].sharedMesh.bounds.size.y * allMeshes[i].transform.localScale.y;
float zSize = allMeshes[i].sharedMesh.bounds.size.z * allMeshes[i].transform.localScale.z;
bool changed = false;
if (xSize > maxSize)
{
maxSize = xSize;
changed = true;
}
if (zSize > maxSize)
{
maxSize = zSize;
changed = true;
}
if (changed)
{
triggerSize = Mathf.Min(xSize, zSize);
targetScript.length = Mathf.Max(xSize, zSize);
}
if (ySize > triggerHeight)
{
triggerHeight = ySize;
}
}
frontTrigger = new GameObject(_frontTriggerName).transform;
frontTrigger.SetParent(frontTriggerHolder);
BoxCollider boxCollider = frontTrigger.gameObject.AddComponent<BoxCollider>();
boxCollider.size = new Vector3(triggerSize, triggerHeight, triggerLength);
boxCollider.center = new Vector3(0, 0, triggerLength / 2);
boxCollider.isTrigger = true;
frontTriggerHolder.gameObject.SetLayer(targetScript.gameObject.layer);
frontTrigger.transform.localPosition = new Vector3(0, triggerHeight / 2, 0);
}
}
private static void SetupRigidbody(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
Rigidbody rb = targetScript.GetComponent<Rigidbody>();
if (rb == null)
{
LogError(ref correct, "RigidBody not found on " + targetScript.name);
}
else
{
targetScript.rb = rb;
#if UNITY_6000_0_OR_NEWER
if (rb.linearDamping == 0)
{
targetScript.rb.linearDamping = 0.1f;
targetScript.rb.angularDamping = 3;
}
#else
if (rb.drag == 0)
{
targetScript.rb.drag = 0.1f;
targetScript.rb.angularDrag = 3;
}
#endif
}
}
private static void SetCarHolder(VehicleComponent targetScript, ref bool correct)
{
if (!correct)
return;
Transform carHolder = targetScript.transform.Find(_carHolderName);
if (carHolder == null)
{
if (targetScript.carHolder != null)
{
Debug.Log("A GameObject was manually assigned inside " + nameof(targetScript.carHolder) + ". Make sure is the root GameObject");
}
else
{
LogError(ref correct, "A GameObject named " + _carHolderName + " was not found under " + targetScript.name);
}
}
else
{
targetScript.carHolder = carHolder;
}
}
private static void LogError(ref bool correct, string message)
{
correct = false;
Debug.LogError(message + " Auto assign will stop now.");
Debug.Log("Please fix the above errors and press Configure Car again or assign missing references and values manually.");
}
}
}