void OnEnable() { wc = (WheelController)target; // Wheel tireRadius = serializedObject.FindProperty("wheel.tireRadius"); wheelWidth = serializedObject.FindProperty("wheel.width"); rimOffset = serializedObject.FindProperty("wheel.rimOffset"); wheelMass = serializedObject.FindProperty("wheel.mass"); useRimCollider = serializedObject.FindProperty("useRimCollider"); vehicleSide = serializedObject.FindProperty("vehicleSide"); dragForce = serializedObject.FindProperty("wheel.dragForce"); //rimRadius = serializedObject.FindProperty("wheel.rimRadius"); //motorTorque = serializedObject.FindProperty("wheel.motorTorque"); //brakeTorque = serializedObject.FindProperty("wheel.brakeTorque"); // Geometry camberCurve = serializedObject.FindProperty("wheel.camberCurve"); // Spring maxSpringForce = serializedObject.FindProperty("spring.maxForce"); springTravel = serializedObject.FindProperty("spring.maxLength"); springForceCurve = serializedObject.FindProperty("spring.forceCurve"); // Damper damperBump = serializedObject.FindProperty("damper.unitBumpForce"); damperRebound = serializedObject.FindProperty("damper.unitReboundForce"); dampingForceCurve = serializedObject.FindProperty("damper.dampingCurve"); // Forward friction fSlipCoefficient = serializedObject.FindProperty("fFriction.slipCoefficient"); fForceCoefficient = serializedObject.FindProperty("fFriction.forceCoefficient"); fMaxForce = serializedObject.FindProperty("fFriction.maxForce"); // Side friction sSlipCoefficient = serializedObject.FindProperty("sFriction.slipCoefficient"); sForceCoefficient = serializedObject.FindProperty("sFriction.forceCoefficient"); sMaxForce = serializedObject.FindProperty("sFriction.maxForce"); // Scan forwardScanResolution = serializedObject.FindProperty("forwardScanResolution"); sideToSideScanResolution = serializedObject.FindProperty("sideToSideScanResolution"); // Misc activeFrictionPresetEnum = serializedObject.FindProperty("activeFrictionPresetEnum"); parentObject = serializedObject.FindProperty("parent"); wheelVisual = serializedObject.FindProperty("wheel.visual"); wheelNonRotating = serializedObject.FindProperty("wheel.nonRotating"); dbg = serializedObject.FindProperty("debug"); scanIgnoreLayers = serializedObject.FindProperty("scanIgnoreLayers"); singleRay = serializedObject.FindProperty("singleRay"); applyForceToOthers = serializedObject.FindProperty("applyForceToOthers"); }
/// <summary> /// Calculation of static parameters and creation of rim collider. /// </summary> public void Initialize(WheelController wc) { visualIsNull = visual == null; nonRotatingVisualIsNull = nonRotatingVisual == null; // Precalculate wheel variables inertia = 0.5f * mass * (radius * radius + radius * radius); if (rimColliderGO != null || !wc.useRimCollider || visual == null) { return; } // Instantiate rim (prevent ground passing through the side of the wheel) rimColliderGO = new GameObject(); rimColliderGO.name = "RimCollider"; rimColliderGO.transform.position = wc.transform.position + wc.transform.right * (rimOffset * (int)wc.vehicleSide); rimColliderGO.transform.parent = wc.transform; rimColliderGO.layer = LayerMask.NameToLayer("Ignore Raycast"); MeshFilter mf = rimColliderGO.AddComponent <MeshFilter>(); mf.name = "Rim Mesh Filter"; mf.mesh = GenerateRimColliderMesh(visual.transform); mf.mesh.name = "Rim Mesh"; MeshCollider mc = rimColliderGO.AddComponent <MeshCollider>(); mc.name = "Rim MeshCollider"; mc.convex = true; PhysicMaterial material = new PhysicMaterial(); material.staticFriction = 0f; material.dynamicFriction = 0f; material.bounciness = 0.3f; mc.material = material; }
public override bool OnInspectorNUI() { if (!base.OnInspectorNUI()) { return(false); } float logoHeight = 40f; Rect texRect = drawer.positionRect; texRect.height = logoHeight; drawer.DrawEditorTexture(texRect, "Wheel Controller 3D/Editor/logo_wc3d", ScaleMode.ScaleToFit); drawer.Space(logoHeight); drawer.HorizontalRuler(); drawer.BeginSubsection("Wheel"); drawer.Field("vehicleSide"); drawer.Field("wheel.radius", true, "m"); drawer.Field("wheel.width", true, "m"); drawer.Field("wheel.mass", true, "kg"); drawer.Field("wheel.rimOffset", true, "m"); drawer.Field("dragTorque", true, "Nm"); drawer.Field("parent"); drawer.Field("useRimCollider"); drawer.EndSubsection(); drawer.BeginSubsection("Wheel Model"); drawer.Field("wheel.visual"); drawer.Field("wheel.visualPositionOffset"); drawer.Field("wheel.visualRotationOffset"); drawer.Field("wheel.nonRotatingVisual"); drawer.EndSubsection(); drawer.BeginSubsection("Spring"); drawer.Field("spring.maxForce", true, "Nm"); if (Application.isPlaying) { WheelController wc = target as WheelController; if (wc != null && wc.vehicleWheelCount > 0) { float minRecommended = wc.parentRigidbody.mass * -Physics.gravity.y * 2f / wc.vehicleWheelCount; if (wc.spring.maxForce < minRecommended) { drawer.Info( "MaxForce of Spring is most likely too low for the vehicle mass. Minimum recommended for current configuration is" + $" {minRecommended}N.", MessageType.Warning); } } } if (drawer.Field("spring.maxLength", true, "m").floatValue < Time.fixedDeltaTime * 10f) { drawer.Info( $"Minimum recommended spring length for Time.fixedDeltaTime of {Time.fixedDeltaTime} is {Time.fixedDeltaTime * 10f}"); } drawer.Field("spring.forceCurve"); drawer.Info("X: Spring compression [%], Y: Force coefficient"); drawer.Field("spring.bottomOutForceCoefficient"); drawer.EndSubsection(); drawer.BeginSubsection("Damper"); drawer.Field("damper.bumpForce"); drawer.Field("damper.reboundForce"); drawer.Field("damper.curve"); drawer.Info("X: Spring velocity (normalized) [m/s], Y: Force coefficient (normalized)"); drawer.Info( "Since NVP2 damper curve is using normalized values. Make sure to adjust bump and rebound forces if you are updating from an older version."); drawer.EndSubsection(); drawer.BeginSubsection("Geometry"); drawer.Field("wheel.camberAtTop", true, "deg"); drawer.Field("wheel.camberAtBottom", true, "deg"); drawer.Field("squat"); drawer.EndSubsection(); drawer.BeginSubsection("Friction"); drawer.Field("activeFrictionPreset"); drawer.EmbeddedObjectEditor(((WheelController)target).activeFrictionPreset, drawer.positionRect); drawer.BeginSubsection("Longitudinal"); drawer.Field("forwardFriction.slipCoefficient", true, "x100 %"); drawer.Field("forwardFriction.forceCoefficient", true, "x100 %"); drawer.EndSubsection(); drawer.BeginSubsection("Lateral"); drawer.Field("sideFriction.slipCoefficient", true, "x100 %"); drawer.Field("sideFriction.forceCoefficient", true, "x100 %"); drawer.EndSubsection(); drawer.BeginSubsection("Load and Forces"); drawer.Field("loadGripCurve"); drawer.Info("X: Tire Load [N], Y: Max Tire Friction Force [N]"); drawer.Field("maximumTireLoad", true, "Nm"); drawer.Field("maximumTireGripForce", true, "Nm"); drawer.EndSubsection(); drawer.EndSubsection(); drawer.BeginSubsection("Ground Detection"); if (!drawer.Field("singleRay").boolValue) { SerializedProperty longScanRes = drawer.Field("longitudinalScanResolution", !Application.isPlaying); SerializedProperty latScanRes = drawer.Field("lateralScanResolution", !Application.isPlaying); if (longScanRes.intValue < 5) { longScanRes.intValue = 5; } if (latScanRes.intValue < 1) { latScanRes.intValue = 1; } int rayCount = longScanRes.intValue * latScanRes.intValue; drawer.Info($"Ray count: {rayCount}"); } drawer.Field("applyForceToOthers"); if (!drawer.Field("autoSetupLayerMask").boolValue) { drawer.Field("layerMask"); drawer.Info( "Make sure that vehicle's collider layers are unselected in the layerMask, as well as Physics.IgnoreRaycast layer. If not, " + "wheels will collide with vehicle itself sand result in it behaving unpredictably."); } drawer.EndSubsection(); drawer.BeginSubsection("Debug Values"); drawer.Field("forwardFriction.slip", false, null, "Longitudinal Slip"); drawer.Field("sideFriction.slip", false, null, "Lateral Slip"); drawer.Field("suspensionForceMagnitude", false); drawer.Field("spring.bottomedOut", false); drawer.Field("wheel.motorTorque", false); drawer.Field("wheel.brakeTorque", false); drawer.Space(); drawer.Field("debug"); drawer.EndSubsection(); drawer.EndEditor(this); return(true); }