using RaceFields.CameraSystem; using RaceFields.Vehicles; using UnityEditor; using UnityEngine; namespace RaceFields.EditorTools { public static class BuggyPrefabBuilder { private const string BuggyPrefabPath = "Assets/_Project/Prefabs/Vehicles/Player/Buggy_Player.prefab"; private const string CameraPrefabPath = "Assets/_Project/Prefabs/Cameras/Buggy_ThirdPersonCamera.prefab"; [MenuItem("Race Fields/Setup/Rebuild Buggy Prefab")] public static void RebuildBuggyPrefab() { GameObject root = PrefabUtility.LoadPrefabContents(BuggyPrefabPath); try { EnsureComponent(root, ConfigureBody); EnsureComponent(root, ConfigureBodyCollider); EnsureComponent(root, null); EnsureComponent(root, setup => setup.AlignAllWheels()); SetupWheel(root.transform, "F_L", "WheelCollider_FL"); SetupWheel(root.transform, "F_R", "WheelCollider_FR"); SetupWheel(root.transform, "B_L", "WheelCollider_RL"); SetupWheel(root.transform, "B_R", "WheelCollider_RR"); BuggyWheelSetup wheelSetup = root.GetComponent(); wheelSetup.AlignAllWheels(); PrefabUtility.SaveAsPrefabAsset(root, BuggyPrefabPath); } finally { PrefabUtility.UnloadPrefabContents(root); } RebuildCameraPrefab(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } private static void RebuildCameraPrefab() { GameObject cameraObject = new GameObject("Buggy_ThirdPersonCamera"); try { Camera camera = cameraObject.AddComponent(); camera.fieldOfView = 62f; camera.nearClipPlane = 0.05f; camera.farClipPlane = 600f; cameraObject.AddComponent(); cameraObject.AddComponent().SnapToTarget(); cameraObject.transform.position = new Vector3(0f, 4.2f, -7.5f); cameraObject.transform.rotation = Quaternion.Euler(18f, 0f, 0f); PrefabUtility.SaveAsPrefabAsset(cameraObject, CameraPrefabPath); } finally { Object.DestroyImmediate(cameraObject); } } private static void SetupWheel(Transform root, string visualName, string colliderName) { Transform visual = FindDescendant(root, visualName); if (visual == null) { Debug.LogWarning($"Wheel visual '{visualName}' was not found in {BuggyPrefabPath}."); return; } Transform colliderTransform = FindDescendant(root, colliderName); GameObject colliderObject = colliderTransform != null ? colliderTransform.gameObject : new GameObject(colliderName); colliderObject.name = colliderName; colliderObject.transform.SetParent(root, true); colliderObject.transform.position = visual.position; colliderObject.transform.rotation = root.rotation; colliderObject.transform.localScale = Vector3.one; WheelCollider wheelCollider = colliderObject.GetComponent(); if (wheelCollider == null) wheelCollider = colliderObject.AddComponent(); ConfigureWheel(wheelCollider, visual); } private static void ConfigureBody(Rigidbody body) { body.mass = 850f; body.linearDamping = 0.05f; body.angularDamping = 0.8f; body.interpolation = RigidbodyInterpolation.Interpolate; body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; } private static void ConfigureBodyCollider(BoxCollider collider) { collider.center = new Vector3(0f, 0.55f, 0f); collider.size = new Vector3(1.8f, 0.9f, 3.1f); } private static void ConfigureWheel(WheelCollider wheelCollider, Transform visual) { wheelCollider.mass = 20f; wheelCollider.radius = EstimateRadius(visual); wheelCollider.suspensionDistance = 0.28f; wheelCollider.forceAppPointDistance = 0.05f; wheelCollider.wheelDampingRate = 0.35f; JointSpring spring = wheelCollider.suspensionSpring; spring.spring = 35000f; spring.damper = 4500f; spring.targetPosition = 0.5f; wheelCollider.suspensionSpring = spring; } private static float EstimateRadius(Transform visual) { Renderer renderer = visual.GetComponentInChildren(); if (renderer == null) return 0.34f; Vector3 size = renderer.bounds.size; return Mathf.Max(0.12f, Mathf.Max(size.y, size.z) * 0.5f); } private static T EnsureComponent(GameObject gameObject, System.Action configure) where T : Component { T component = gameObject.GetComponent(); if (component == null) component = gameObject.AddComponent(); configure?.Invoke(component); return component; } private static Transform FindDescendant(Transform root, string name) { Transform[] children = root.GetComponentsInChildren(true); for (int i = 0; i < children.Length; i++) { if (children[i].name == name) return children[i]; } return null; } } }