如何在Unity中控制AnimationCurve缓入/缓出速度且不改变总时长
解决方案
问题1:总时长固定的缓入/缓出逻辑实现
核心思路是将缓入/缓出时间作为总时长的比例部分,而非额外追加时间。比如总时长设为5秒,缓入2秒,那这2秒是包含在5秒内的,不会导致总时长增加。具体实现时,我们通过新增变量控制缓入/缓出的绝对时长,再转换为总时长的比例,以此约束动画曲线的有效作用区间。
问题2:通过代码关联变量与AnimationCurve的缓入/缓出时长
Inspector手动调整曲线难以精准对应时长比例,我们可以通过代码将缓入/缓出的时间比例映射到曲线的输入参数,让曲线只在对应比例的时间区间内生效,中间阶段保持匀速(或自定义状态)。
修改后的完整脚本
using System.Collections; using UnityEngine; public class Scaling : MonoBehaviour { public GameObject objectToScale; public float duration = 5f; // 固定总时长 public Vector3 minSize; public Vector3 maxSize; public bool scaleUp = false; public Coroutine scaleCoroutine; public AnimationCurve easeCurve; // 建议设为S型曲线(0到1的平滑过渡) public bool automatic = false; public bool coroutineIsRunning = false; // 新增:控制缓入/缓出的绝对时长(需小于总时长) public float easeInDuration = 1f; public float easeOutDuration = 1f; private void Update() { if (automatic) { if (!coroutineIsRunning) { Scale(); } } else { if (Input.GetKeyDown(KeyCode.G)) { Scale(); } } } private void Scale() { scaleUp = !scaleUp; if (scaleCoroutine != null) StopCoroutine(scaleCoroutine); scaleCoroutine = StartCoroutine(ScaleOverTime(objectToScale, (scaleUp ? maxSize : minSize), duration)); } private IEnumerator ScaleOverTime(GameObject targetObj, Vector3 toScale, float duration) { float counter = 0; Vector3 startScaleSize = targetObj.transform.localScale; // 将缓入/缓出时长转换为总时长的比例 float easeInRatio = Mathf.Clamp01(easeInDuration / duration); float easeOutRatio = Mathf.Clamp01(easeOutDuration / duration); // 确保缓入+缓出比例不超过1,避免逻辑冲突 float totalEaseRatio = Mathf.Min(easeInRatio + easeOutRatio, 1f); float steadyRatio = 1f - totalEaseRatio; coroutineIsRunning = true; while (counter < duration) { counter += Time.deltaTime; float normalizedTime = counter / duration; float curveFactor; if (normalizedTime <= easeInRatio) { // 缓入阶段:将当前时间映射到曲线的0~1区间 float curveInput = normalizedTime / easeInRatio; curveFactor = easeCurve.Evaluate(curveInput); } else if (normalizedTime >= 1f - easeOutRatio) { // 缓出阶段:将当前时间反转映射到曲线的0~1区间 float curveInput = (1f - normalizedTime) / easeOutRatio; curveFactor = 1f - easeCurve.Evaluate(curveInput); } else { // 匀速阶段:直接取曲线的最大值(或固定为1) curveFactor = easeCurve.Evaluate(1f); } targetObj.transform.localScale = Vector3.Lerp(startScaleSize, toScale, curveFactor); if (counter >= duration) { coroutineIsRunning = false; targetObj.transform.localScale = toScale; } yield return null; } } }
使用说明
参数设置:
- 在Inspector中设置
duration为固定总时长(比如5秒) easeInDuration和easeOutDuration设为你需要的缓入/缓出绝对时长(比如各1秒,总占2秒,剩余3秒匀速)- 将
easeCurve设为S型曲线(右键曲线→预设→In-Out),无需手动调整关键帧位置
- 在Inspector中设置
逻辑说明:
- 缓入阶段:前
easeInDuration时间内,按曲线平滑加速缩放 - 匀速阶段:中间剩余时间内保持匀速缩放
- 缓出阶段:最后
easeOutDuration时间内,按曲线平滑减速到目标尺寸 - 所有阶段总时长严格等于
duration,不会额外增加
- 缓入阶段:前
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

