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

Unity开发:自动移动的玩家球体到达平台边缘不坠落的实现问题

问题描述

我通过脚本实现了一个可自动向前移动的球体(玩家角色),现在希望该球体符合正常运动逻辑:到达平台边缘时不会坠落,直接停在边缘位置。我的场景示意图如下:
场景示意图
当前使用的控制脚本如下,第一个为滑动输入系统脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SwerveInputSystem : MonoBehaviour
{

    private float _lastFrameFingerPositionX;
    private float _moveFactorX;
    public float MoveFactorX => _moveFactorX;


    void Start(){
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _lastFrameFingerPositionX = Input.mousePosition.x;
        }
        else if (Input.GetMouseButton(0))
        {
            _moveFactorX = Input.mousePosition.x - _lastFrameFingerPositionX;
            _lastFrameFingerPositionX = Input.mousePosition.x;
        }
        else if (Input.GetMouseButtonUp(0))
        {
            _moveFactorX = 0f;
        }
    }
}

第二个为玩家控制脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour{
    
    private SwerveInputSystem _swerveInputSystem;
    [SerializeField] private float swerveSpeed = 5f;
    [SerializeField] private float maxSwerveAmount = 1f;
    [SerializeField] private float verticalSpeed;

    void Start(){

        _swerveInputSystem = GetComponent<SwerveInputSystem>();
    }


    void Update(){
        float swerveAmount = Time.deltaTime * swerveSpeed * _swerveInputSystem.MoveFactorX;
        swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
        transform.Translate(swerveAmount, 0, 0);
        float verticalDelta = verticalSpeed * Time.deltaTime;

        transform.Translate(swerveAmount, verticalDelta, 0.1f);
    }
}
解决方案

你现有PlayerController脚本中连续两次调用transform.Translate时都传入了swerveAmount作为X轴参数,会导致实际横向移动速度是参数设定值的2倍,建议先修正该逻辑。下面提供两种实现边缘停驻的方案:

方案1:固定边界钳位(适配规则矩形平台)

如果你的平台是左右边界固定的规则矩形,直接限制球体X轴坐标范围是成本最低的实现方式。
修改后的PlayerController代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour{
    
    private SwerveInputSystem _swerveInputSystem;
    [SerializeField] private float swerveSpeed = 5f;
    [SerializeField] private float maxSwerveAmount = 1f;
    [SerializeField] private float verticalSpeed;
    // 新增平台左右边界参数,可在Inspector面板手动设置
    [SerializeField] private float minX;
    [SerializeField] private float maxX;

    void Start(){
        _swerveInputSystem = GetComponent<SwerveInputSystem>();
    }

    void Update(){
        float swerveAmount = Time.deltaTime * swerveSpeed * _swerveInputSystem.MoveFactorX;
        swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
        float verticalDelta = verticalSpeed * Time.deltaTime;
        // 修正重复添加swerveAmount的问题,单次Translate即可完成移动
        transform.Translate(swerveAmount, verticalDelta, 0.1f);
        
        // 钳位X轴坐标,不让球体超出平台范围
        Vector3 currentPos = transform.position;
        currentPos.x = Mathf.Clamp(currentPos.x, minX, maxX);
        transform.position = currentPos;
    }
}

使用说明:脚本挂载完成后,在Inspector面板将minX设为平台左边缘对应的X坐标,maxX设为平台右边缘对应的X坐标即可。

方案2:射线检测(适配任意形状平台)

如果平台形状不规则,或者后续会有动态变化,可以通过射线检测判断移动方向上是否存在地面,无地面时停止对应方向的移动即可。
修改后的PlayerController代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour{
    
    private SwerveInputSystem _swerveInputSystem;
    [SerializeField] private float swerveSpeed = 5f;
    [SerializeField] private float maxSwerveAmount = 1f;
    [SerializeField] private float verticalSpeed;
    // 边缘检测射线长度,可根据球体半径调整
    [SerializeField] private float edgeCheckRayLength = 0.6f;
    private float _sphereRadius;

    void Start(){
        _swerveInputSystem = GetComponent<SwerveInputSystem>();
        // 自动获取球体碰撞器半径
        _sphereRadius = GetComponent<SphereCollider>().radius;
    }

    void Update(){
        float swerveAmount = Time.deltaTime * swerveSpeed * _swerveInputSystem.MoveFactorX;
        swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
        
        // 边缘检测:判断移动方向上是否有地面
        if (swerveAmount < 0) // 向左移动
        {
            Vector3 rayStart = transform.position + Vector3.left * _sphereRadius;
            if (!Physics.Raycast(rayStart, Vector3.down, edgeCheckRayLength))
            {
                swerveAmount = 0;
            }
        }
        else if (swerveAmount > 0) // 向右移动
        {
            Vector3 rayStart = transform.position + Vector3.right * _sphereRadius;
            if (!Physics.Raycast(rayStart, Vector3.down, edgeCheckRayLength))
            {
                swerveAmount = 0;
            }
        }

        float verticalDelta = verticalSpeed * Time.deltaTime;
        transform.Translate(swerveAmount, verticalDelta, 0.1f);
    }
}

使用说明:确保平台对象已经挂载了碰撞器组件,调整edgeCheckRayLength参数保证射线可以正常检测到脚下的平台即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 22:27:02