协程中使用Mathf.SmoothDamp()耗时超预期的优化问询
The Problem
You're using a coroutine with Mathf.SmoothDamp to adjust your character's collider height during a crouch animation, expecting the transition to take ~0.38 seconds as set in smoothTime, but it's taking around 2 seconds instead. The root issues are with how you're initializing the velocity parameter and your loop termination condition.
The Fix
Here's a refactored version of your code that will match the expected smoothTime duration, along with explanations of key changes:
void OnRifleIdleToCrouchStart() { // Initialize velocities to 0 - let SmoothDamp manage these values internally float heightSmoothVelocity = 0f; float centerSmoothVelocity = 0f; // Start the coroutine with target values, smooth time, and velocity references StartCoroutine(SmoothHeightChange(1.45f, 1.2f, 0.38f, ref heightSmoothVelocity, ref centerSmoothVelocity)); } IEnumerator SmoothHeightChange(float targetHeight, float targetCenterY, float smoothTime, ref float heightVelocity, ref float centerVelocity) { Debug.Log("smoothHeightChange START " + Time.time); const float completionThreshold = 0.001f; // Small threshold for floating-point precision // Check if we're already at the target to avoid unnecessary work if (Mathf.Abs(controller.height - targetHeight) <= completionThreshold) { controller.height = targetHeight; controller.center = new Vector3(controller.center.x, targetCenterY, controller.center.z); yield break; } // Update collider values each frame until we're within the threshold of the target while (Mathf.Abs(controller.height - targetHeight) > completionThreshold) { // Update collider height with SmoothDamp controller.height = Mathf.SmoothDamp(controller.height, targetHeight, ref heightVelocity, smoothTime); // Update collider center Y position (keep X/Z unchanged) float newCenterY = Mathf.SmoothDamp(controller.center.y, targetCenterY, ref centerVelocity, smoothTime); controller.center = new Vector3(controller.center.x, newCenterY, controller.center.z); // Wait for the next frame (use yield return null for standard Update-phase timing) yield return null; } // Force final values to eliminate any floating-point drift controller.height = targetHeight; controller.center = new Vector3(controller.center.x, targetCenterY, controller.center.z); Debug.Log("smoothHeightChange FINISH " + Time.time); }
Key Changes & Explanations
Initialize Velocities to 0
Mathf.SmoothDampuses theref velocityparameter to track the current momentum of the smooth transition. Setting an initial value like-0.01fdisrupts this internal calculation, causing the transition to slow down significantly. Letting it start at 0 ensures the function calculates the correct acceleration to hit yoursmoothTimetarget.
Replace
Math.Roundwith a Precision Threshold- Using
Math.Round(controller.height, 2)as a loop condition introduces floating-point precision issues. You might exit the loop early (if rounding hits the target before the actual value does) or run longer than needed. A small absolute threshold like0.001fgives you reliable termination without unnecessary iterations.
- Using
Use
yield return nullInstead ofWaitForEndOfFrameyield return nullwaits until the next frame's Update phase, which is the standard timing for gameplay-related updates.WaitForEndOfFramedelays execution until after all rendering is done, which can add small, unneeded delays to your transition.
Final Value Cleanup
- After the loop, we explicitly set the collider to the target values. This eliminates any tiny floating-point drift that might remain, ensuring your collider is exactly where you need it.
Bonus Tip
If you need the collider transition to perfectly sync with your animation, you can tie the coroutine's progress to the animation's normalized time instead of relying solely on smoothTime. But for your original goal of matching the specified smoothTime, the above changes should get you exactly where you need to be.
内容的提问来源于stack exchange,提问作者Sciguy77

