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

Unity移动端:如何实现流畅的滑动冲刺移动效果?

移动端Unity角色冲刺移动优化方案

问题现状

开发移动端Unity项目时,需要实现以下机制:玩家滑动屏幕后,将滑动方向转为最近的轴向向量(如70度对应(0,1),35度对应(1,0)),角色向该方向流畅冲刺到目标位置。当前实现仅能瞬移,使用Vector2.Lerp时卡顿、响应延迟,尝试Vector2.MovePosition()、rb.AddForce()等方法效果均不理想。

现有核心移动代码:

if (trigger)
{
    fractionOfWayThere += 0.1f;
    transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
    isMoving = true;
}

核心问题分析

  1. Lerp用法错误:直接在Update中累加fractionOfWayThere并以当前位置作为插值起点,会导致移动速度随帧率波动,且插值逻辑不符合"平滑冲刺"的预期(正确的Lerp应该用固定的起始位置和基于时间的进度)。
  2. 帧率依赖:用固定增量0.1f更新进度,不同设备帧率不同会导致移动速度不一致,出现卡顿或过快。
  3. 位置判断精度问题:直接用(Vector2)transform.position == finish判断到达,由于浮点精度误差,可能永远无法触发停止逻辑。
  4. 移动逻辑与输入逻辑耦合:所有逻辑都放在Update中,物理相关的移动未使用FixedUpdate,导致移动不流畅。

优化实现思路与代码修改

1. 修正Lerp插值逻辑

将冲刺的起始位置缓存,使用基于时间的进度值,保证移动速度稳定:

  • 新增变量startMovePos存储冲刺开始时的位置
  • 用Time.deltaTime计算进度,确保不同帧率下速度一致

2. 优化位置到达判断

用距离阈值代替直接相等判断,避免浮点精度问题:

if (Vector2.Distance(transform.position, finish) < 0.01f)
{
    // 到达目标位置的逻辑
}

3. 分离输入与移动逻辑

输入处理放在Update中,物理移动放在FixedUpdate中(如果使用刚体),或者在Update中用时间增量保证平滑。

4. 简化方向向量转换逻辑

原有的三个if判断可以合并为更简洁的逻辑:

float absX = Mathf.Abs(destination.x);
float absY = Mathf.Abs(destination.y);

if (absX > absY)
{
    destination = new Vector2(Mathf.Sign(destination.x), 0);
}
else if (absY > absX)
{
    destination = new Vector2(0, Mathf.Sign(destination.y));
}
else
{
    // 对角线滑动时优先X方向,可按需调整
    destination = new Vector2(Mathf.Sign(destination.x), 0);
}

(替代原有的Funkcija方法,直接用Mathf.Sign获取方向)

5. 完整优化后代码示例

private Vector2 startMovePos;
private Vector2 finish;
private float moveDuration = 0.2f; // 冲刺持续时间,可调整
private float moveTimer;
private Camera cam;
private Rigidbody2D rb; // 如果使用刚体,记得在Awake中赋值
private bool isMoving;
private SpriteRenderer sr;
private Animator animator;
private LayerMask spike;

private void Awake()
{
    cam = Camera.main;
    rb = GetComponent<Rigidbody2D>();
    sr = GetComponent<SpriteRenderer>();
    animator = GetComponent<Animator>();
}

private void Update()
{
    if (GetComponent<OnDeath>().isDead) return;

    // 输入处理
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0);
        Vector2 startPos = Vector2.zero;
        if (touch.phase == TouchPhase.Began)
        {
            startPos = cam.ScreenToWorldPoint(touch.position);
        }
        else if (touch.phase == TouchPhase.Ended)
        {
            Vector2 endPos = cam.ScreenToWorldPoint(touch.position);
            Vector2 destination = endPos - startPos;

            // 转换为轴向方向
            float absX = Mathf.Abs(destination.x);
            float absY = Mathf.Abs(destination.y);
            if (absX > absY)
            {
                destination = new Vector2(Mathf.Sign(destination.x), 0);
            }
            else if (absY > absX)
            {
                destination = new Vector2(0, Mathf.Sign(destination.y));
            }
            else
            {
                destination = new Vector2(Mathf.Sign(destination.x), 0);
            }

            finish = (Vector2)transform.position + destination.normalized;
            // 检测目标位置是否合法(原代码逻辑:避开spike层)
            if (Physics2D.OverlapCircle(finish, 0.1f, spike) == null)
            {
                // 开始冲刺
                startMovePos = transform.position;
                moveTimer = 0;
                isMoving = true;
                animator.SetBool("isMoving", true);
                // 翻转角色
                sr.flipX = finish.x < transform.position.x;
            }
            else
            {
                finish = transform.position;
            }
        }
    }

    // 非刚体模式下的移动处理
    if (isMoving)
    {
        moveTimer += Time.deltaTime;
        float progress = Mathf.Clamp01(moveTimer / moveDuration);
        // 使用Lerp从起始位置到目标位置,保证平滑
        transform.position = Vector2.Lerp(startMovePos, finish, progress);

        if (progress >= 1f)
        {
            isMoving = false;
            animator.SetBool("isMoving", false);
            transform.position = finish; // 确保精确到达目标
        }
    }
}

// 如果使用刚体,改用FixedUpdate处理移动
/*
private void FixedUpdate()
{
    if (!isMoving) return;

    moveTimer += Time.fixedDeltaTime;
    float progress = Mathf.Clamp01(moveTimer / moveDuration);
    Vector2 targetPos = Vector2.Lerp(startMovePos, finish, progress);
    rb.MovePosition(targetPos);

    if (progress >= 1f)
    {
        isMoving = false;
        animator.SetBool("isMoving", false);
        rb.MovePosition(finish);
    }
}
*/

额外建议

  • 调整冲刺持续时间:moveDuration控制冲刺的快慢,根据手感调整0.1~0.3秒左右。
  • 加入缓动效果:可以用Mathf.SmoothStep或动画曲线代替线性Lerp,让冲刺有加速减速的效果,手感更自然:
    float progress = Mathf.SmoothStep(0, 1, moveTimer / moveDuration);
    
  • 禁用输入锁定:冲刺过程中可以锁定输入,避免重复触发移动,提升手感。

内容的提问来源于stack exchange,提问作者Ilija Lazović

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 21:18:18