You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

实现类似《过山车之星》的GameObject旋转系统:解决光标与物体跳转问题

嘿,这个问题我做模拟经营类游戏的物体编辑系统时正好踩过坑!你现在的核心问题是把「旋转的鼠标输入」和「物体位置跟随鼠标」给绑在一起了——得把这俩彻底拆开,同时用光标锁定来避免额外干扰,就能解决跳转问题了。

方案1:用鼠标相对位移驱动旋转(最贴合《过山车之星》逻辑)

这种方式完全不碰物体的位置,只利用鼠标的移动增量来控制旋转,从根源上杜绝位置跳转的问题。适合自由旋转场景,代码示例(Unity环境):

public GameObject targetObject;
public float rotationSpeed = 6f;
public KeyCode rotateTriggerKey = KeyCode.LeftAlt; // 自定义旋转触发键

private bool isRotating = false;

void Update()
{
    // 开启旋转模式
    if (Input.GetKeyDown(rotateTriggerKey))
    {
        isRotating = true;
        // 锁定光标到屏幕中心并隐藏,避免光标乱跑干扰操作
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }
    // 关闭旋转模式
    else if (Input.GetKeyUp(rotateTriggerKey))
    {
        isRotating = false;
        // 恢复光标正常状态
        Cursor.lockState = CursorLockMode.None;
        Cursor.visible = true;
    }

    if (isRotating)
    {
        // 获取鼠标的相对移动量(这是旋转的核心输入)
        float mouseXDelta = Input.GetAxis("Mouse X") * rotationSpeed;
        float mouseYDelta = Input.GetAxis("Mouse Y") * rotationSpeed;

        // 围绕物体自身的Y轴(水平旋转)和X轴(垂直旋转)转动
        // 要是需要围绕世界轴旋转,把Space.Self改成Space.World即可
        targetObject.transform.Rotate(Vector3.up, mouseXDelta, Space.Self);
        targetObject.transform.Rotate(Vector3.right, -mouseYDelta, Space.Self);
    }
}

这个逻辑和《过山车之星》完全一致:按住触发键后,鼠标的拖动方向直接对应旋转方向,全程不改变物体位置,释放按键后自然不会有跳转。

方案2:如果需要围绕鼠标指向的平面旋转

要是你的需求是让物体围绕鼠标点击的平面法线旋转(比如在地面上旋转物体),那要在旋转开始时记录初始状态,之后只计算鼠标的偏移量来旋转,绝对不能直接把物体移到鼠标位置:

public GameObject targetObject;
public float rotationSpeed = 3f;
public KeyCode rotateTriggerKey = KeyCode.LeftAlt;
public LayerMask targetPlaneLayer; // 选择你要检测的平面图层

private bool isRotating = false;
private Vector3 initialMouseHitPos;
private Quaternion initialObjectRotation;

void Update()
{
    if (Input.GetKeyDown(rotateTriggerKey))
    {
        isRotating = true;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

        // 记录旋转启动时的鼠标射线交点和物体初始旋转角度
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, targetPlaneLayer))
        {
            initialMouseHitPos = hit.point;
            initialObjectRotation = targetObject.transform.rotation;
        }
    }
    else if (Input.GetKeyUp(rotateTriggerKey))
    {
        isRotating = false;
        Cursor.lockState = CursorLockMode.None;
        Cursor.visible = true;
    }

    if (isRotating)
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, targetPlaneLayer))
        {
            // 计算鼠标从初始点到当前点的水平偏移
            Vector3 offset = hit.point - initialMouseHitPos;
            float rotateAngle = offset.x * rotationSpeed;
            
            // 基于初始旋转叠加角度,完全不修改物体位置
            targetObject.transform.rotation = initialObjectRotation * Quaternion.Euler(0, rotateAngle, 0);
        }
    }
}

这里的核心是:旋转只依赖鼠标的相对偏移量,全程不修改物体的position属性,所以释放按键后不会出现跳位置的情况。

关于阻止光标移动

上面的代码里已经用到了Unity的光标锁定API:

  • 按住旋转键时,设置Cursor.lockState = CursorLockMode.Locked,光标会被固定在屏幕中心且不可见,此时鼠标移动只会产生相对位移,不会让光标乱跑。
  • 释放按键后恢复Cursor.lockState = CursorLockMode.None,光标就回到正常状态了。

这种方式既解决了位置跳转问题,又让操作体验和《过山车之星》保持一致。

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

火山引擎 最新活动