You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 2D弹跳效果技术咨询:小球每次均匀弹跳至对应进度高度

嘿,这个需求我之前做进度可视化的时候刚好实现过,给你两种Unity 2D下的可行方案,你可以根据场景需求来选:

方案一:基于物理引擎的真实弹跳(适合需要物理交互的场景)

如果你的小球需要和其他2D物体产生物理交互,比如碰到障碍物之类的,用物理引擎来实现会更自然。核心思路是利用物理公式计算弹跳速度,保证每次落地后能弹到对应进度的高度。

先给小球配置基础组件:

  • 添加Rigidbody2D,设置合适的重力缩放(比如2f,调着舒服就行)
  • 给地面加个Tag(比如"Ground"),方便碰撞检测

然后挂这个脚本到小球上:

using UnityEngine;

public class ProgressBouncePhysics : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private float maxBounceHeight = 5f; // 进度100%时的最高弹跳高度
    [SerializeField] private float currentProgress = 0f; // 0-1之间的进度值

    private float groundYPos;
    private bool isGrounded = false;

    void Start()
    {
        // 记录初始地面位置(也可以指定一个地面Transform来获取Y值)
        groundYPos = transform.position.y;
    }

    void OnCollisionEnter2D(Collision2D other)
    {
        if (other.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
            // 用物理公式计算弹跳速度:v = √(2 * g * h)
            float targetHeight = currentProgress * maxBounceHeight;
            float bounceSpeed = Mathf.Sqrt(2 * Mathf.Abs(Physics2D.gravity.y) * rb.gravityScale * targetHeight);
            // 给小球施加向上的速度
            rb.velocity = new Vector2(rb.velocity.x, bounceSpeed);
        }
    }

    void OnCollisionExit2D(Collision2D other)
    {
        if (other.gameObject.CompareTag("Ground"))
        {
            isGrounded = false;
        }
    }

    // 外部调用更新进度的方法(比如从Slider的Value事件传值)
    public void UpdateProgress(float progress)
    {
        currentProgress = Mathf.Clamp01(progress);
    }
}

方案关键点:

  • 每次落地后自动计算对应进度的弹跳速度,保证高度精准匹配
  • 保留物理引擎的真实感,适合有交互需求的场景
  • 通过maxBounceHeight可以快速调整最高弹跳高度,适配你的布局
方案二:基于动画插值的平滑弹跳(适合纯UI场景)

如果只是做UI进度可视化,不需要物理交互,直接用动画插值控制小球位置会更可控,没有物理引擎的不可控因素。

给小球挂这个脚本(如果是UI元素,记得用RectTransform):

using UnityEngine;

public class ProgressBounceUI : MonoBehaviour
{
    [SerializeField] private float maxBounceHeight = 5f;
    [SerializeField] private float bounceCycleTime = 1f; // 一次弹跳的总时长(上去+下来)
    [SerializeField] private float currentProgress = 0f;

    private Vector3 startPos;
    private float cycleTimer = 0f;
    private bool isAscending = true;

    void Start()
    {
        // 记录初始地面位置
        startPos = transform.position;
        // 如果是UI,换成GetComponent<RectTransform>().anchoredPosition
    }

    void Update()
    {
        cycleTimer += Time.deltaTime / bounceCycleTime;
        float targetHeight = currentProgress * maxBounceHeight;

        if (isAscending)
        {
            // 平滑上升到目标高度
            float t = Mathf.SmoothStep(0, 1, cycleTimer);
            transform.position = new Vector3(startPos.x, startPos.y + t * targetHeight, startPos.z);

            if (cycleTimer >= 1f)
            {
                isAscending = false;
                cycleTimer = 0f;
            }
        }
        else
        {
            // 平滑落回地面
            float t = Mathf.SmoothStep(0, 1, cycleTimer);
            transform.position = new Vector3(startPos.x, startPos.y + (1 - t) * targetHeight, startPos.z);

            if (cycleTimer >= 1f)
            {
                isAscending = true;
                cycleTimer = 0f;
            }
        }
    }

    public void UpdateProgress(float progress)
    {
        currentProgress = Mathf.Clamp01(progress);
        // 可选:如果需要立刻同步高度,直接设置位置即可
        // transform.position = new Vector3(startPos.x, startPos.y + currentProgress * maxBounceHeight, startPos.z);
    }
}

方案关键点:

  • 完全脱离物理引擎,弹跳节奏完全可控
  • 用SmoothStep做插值,运动更平滑自然
  • 适合纯UI进度展示,不会受物理参数干扰

通用注意事项:

  • 不管用哪种方案,都要用Mathf.Clamp01限制进度值,确保它在0-1之间
  • 如果是UI场景,记得把小球放在Canvas子节点下,用RectTransform的anchoredPosition代替transform.position
  • 可以根据需求调整弹跳速度、最大高度等参数,适配你的视觉效果

内容的提问来源于stack exchange,提问作者coderxxx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:36:08