You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity中基于Dreamteck Splines的赛车游戏,如何实现玩家滑动时临时覆盖样条线旋转、无操作时自动跟随样条线的功能?

Unity中基于Dreamteck Splines的赛车游戏,如何实现玩家滑动时临时覆盖样条线旋转、无操作时自动跟随样条线的功能?

嘿,我仔细看了你的问题和现有代码,核心痛点应该是现在松开触摸后,车子直接跳回样条线的原进度点,而不是从你手动操作后的位置继续跟随对吧?咱们可以调整逻辑,让手动操作和自动跟随之间的过渡更平滑,同时保留手动操作后的位置状态,下面是具体的修改思路和代码调整方案:

核心思路

咱们不要直接粗暴地开关splineFollower.follow,而是在结束手动操作时,先找到车子当前位置在样条线上的最近匹配点,把样条跟随器的进度同步到这个点,再通过平滑过渡让车子回到样线的自动跟随状态,这样就能实现“从上次离开的位置继续”的需求。

代码修改步骤

首先,在你的playerController类里添加几个过渡相关的变量:

[Header("过渡设置")]
[SerializeField] private float transitionDuration = 0.3f; // 手动转自动的平滑过渡时间

private bool transitioningToSpline = false; // 是否正在过渡回样条跟随
private float transitionTimer = 0f;
private Vector3 targetSplinePosition; // 样条上的目标位置
private Quaternion targetSplineRotation; // 样条上的目标旋转

然后,添加一个触发过渡的方法,用来处理触摸结束/静止后的逻辑:

private void StartTransitionToSpline()
{
    // 找到当前车子位置在样条上的最近点
    SplineResult closestPointResult = splineFollower.spline.ClosestPoint(transform.position);
    
    // 更新样条跟随器的进度到这个最近点,避免跳回之前的位置
    splineFollower.SetProgress(closestPointResult.progress);
    
    // 获取样条在该点的标准位置和旋转,作为过渡目标
    targetSplinePosition = splineFollower.spline.EvaluatePosition(closestPointResult.progress);
    targetSplineRotation = splineFollower.spline.EvaluateRotation(closestPointResult.progress);
    
    // 开启过渡状态
    transitioningToSpline = true;
    transitionTimer = 0f;
}

接下来,修改原有的触摸逻辑部分,替换直接开关follow的代码:

void Update()
{
    if (Input.touchCount > 0)
    {
        touch = Input.GetTouch(0);

        switch (touch.phase)
        {
            case TouchPhase.Began:
                localTouchStationeryTime = 0f;
                touchDetected = true;
                transitioningToSpline = false; // 打断正在进行的过渡
                splineFollower.follow = false; // 手动操作时关闭自动跟随
                break;
            case TouchPhase.Moved:
                splineFollower.follow = false;
                transitioningToSpline = false; // 打断过渡
                localTouchStationeryTime = 0f;
                isTouchStationery = false;

                deltaPosition = touch.deltaPosition;
                transform.Rotate(Vector3.up * deltaPosition.x * rotationSpeed * Time.deltaTime);
                break;
            case TouchPhase.Stationary:
                localTouchStationeryTime += Time.deltaTime; 

                if(localTouchStationeryTime > timeToReachStationery)
                {
                    isTouchStationery = true;
                    StartTransitionToSpline(); // 触发过渡,而不是直接开follow
                }
                break;
            case TouchPhase.Ended:
                localTouchStationeryTime = 0f;
                touchDetected = false;
                isTouchStationery = false;
                StartTransitionToSpline(); // 触发过渡
                break;
            case TouchPhase.Canceled:
                localTouchStationeryTime = 0f;
                touchDetected = false;
                isTouchStationery = false;
                StartTransitionToSpline(); // 触发过渡
                break;
            default:
                break;
        }
    }

    // 处理速度逻辑(保留你原来的代码)
    if (touchDetected)
    {
        currentCarSpeed += carAccelerationRate * Time.deltaTime;
    }
    else
    {
        currentCarSpeed -= carDecelerationRate * Time.deltaTime;
    }
    currentCarSpeed = Mathf.Clamp(currentCarSpeed, minimumCarSpeed, maximumCarSpeed);
    splineFollower.followSpeed = currentCarSpeed;

    // 手动移动逻辑:只有在非静止、非过渡状态下才生效
    if(!isTouchStationery && !transitioningToSpline && !splineFollower.follow)
    {
        transform.Translate(transform.forward * currentCarSpeed * Time.deltaTime);
    }

    // 处理过渡逻辑:平滑移动和旋转到样条的目标点
    if (transitioningToSpline)
    {
        transitionTimer += Time.deltaTime;
        float transitionProgress = Mathf.Clamp01(transitionTimer / transitionDuration);
        
        // 平滑插值位置和旋转
        transform.position = Vector3.Lerp(transform.position, targetSplinePosition, transitionProgress);
        transform.rotation = Quaternion.Lerp(transform.rotation, targetSplineRotation, transitionProgress);
        
        // 过渡完成后开启自动跟随
        if (transitionProgress >= 1f)
        {
            transitioningToSpline = false;
            splineFollower.follow = true;
        }
    }
}

关键修改说明

  • 过渡逻辑:通过StartTransitionToSpline找到车子当前位置对应的样条最近点,同步样条跟随器的进度,避免了直接开启follow导致的位置跳转。
  • 平滑过渡:用Vector3.LerpQuaternion.Lerp实现位置和旋转的平滑过渡,提升操作手感,不会出现突兀的变化。
  • 状态互斥:确保手动操作、过渡、自动跟随三个状态不会同时生效,避免逻辑冲突。

额外优化建议

  • 可以给手动移动加一个轨道宽度限制,比如限制车子的X轴位置在样线左右一定范围内,防止完全脱离赛道。
  • 手动旋转时可以加角度限制,比如最多左右转45度,避免车子转得太离谱。

备注:内容来源于stack exchange,提问作者IcyThug

火山引擎 最新活动