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

Unity地面移动时盒子自转且移动受阻问题求助

问题解决:Unity角色碰撞后自转且移动异常

问题描述

制作的可跳跃、前后左右移动的盒子角色,在接触带有Collider的物体时会开始自转,且难以正常移动(仅能小幅移动并陷入小范围转圈)。角色包含以下组件:

  • 未设置为Trigger的Box Collider
  • 未启用重力的Rigidbody(重力逻辑在自定义脚本中实现)
  • 自定义玩家移动脚本(代码如下)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]

public class PlayerMovement : MonoBehaviour {
    public float moveSpeed = 9f;
    public float airMoveSpeed = 0.6f;
    public float jumpSpeed = 8f;
    public float gravity = 20f;
    public float groundDrag = 6f;
    public float airDrag = 4f;
    public Camera playerCamera;
    public float sensitivity = 2f;
    public float lookXLimit = 45f;
    public GroundCheck groundCheck;

    Vector3 moveDirection = Vector3.zero;
    Rigidbody rb;
    float rotationX = 0;
    bool isGrounded;

    [HideInInspector]
    public bool canMove = true;

    void Start()
    {
        rb = gameObject.GetComponent<Rigidbody>();

        isGrounded = false;

        // Lock cursor
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    void Update()
    {
        CheckGround();
        MovePlayer();
        Gun();
        ControlDrag();
    }

    void CheckGround() {
        isGrounded = groundCheck.isGrounded;
    }

    void MovePlayer() {
        // Recalculate move direction based on axes
        Vector3 forward = transform.TransformDirection(Vector3.forward);
        Vector3 right = transform.TransformDirection(Vector3.right);
        float curSpeedX = canMove ? moveSpeed * Input.GetAxis("Vertical") : 0;
        float curSpeedY = canMove ? moveSpeed * Input.GetAxis("Horizontal") : 0;
        moveDirection = (forward * curSpeedX) + (right * curSpeedY);

        // moveDirection.y -= gravity * 1000;

        if (Input.GetButton("Jump") && canMove && isGrounded)
        {
            rb.AddForce(transform.up * jumpSpeed, ForceMode.Impulse);
        }

        // Move the player
        if(isGrounded) {
            // transform.position += moveDirection;
            rb.AddForce(moveDirection * Time.deltaTime, ForceMode.Acceleration);
        } else {
            // transform.position += moveDirection;
            rb.AddForce(moveDirection * Time.deltaTime * airMoveSpeed, ForceMode.Acceleration);
        }

        // Player and Camera rotation
        if (canMove)
        {
            rotationX += -Input.GetAxis("Mouse Y") * sensitivity;
            rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
            playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
            transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * sensitivity, 0);
        }
    }

    void ControlDrag() {
        if(isGrounded) rb.drag = groundDrag;
        else rb.drag = airDrag;
    }
}

核心问题分析

  • Rigidbody未锁定旋转:默认状态下Rigidbody允许旋转,碰撞时物理引擎产生的力矩会让角色自转。
  • 移动方向依赖角色自身坐标系:碰撞后角色姿态偏移,移动方向随之旋转,导致陷入转圈。
  • 自定义重力未正确施加:脚本中注释了重力相关代码,角色缺少向下的力,碰撞时物理状态异常。

修复步骤

1. 锁定Rigidbody旋转

在Rigidbody组件的Inspector面板中,勾选Freeze Rotation下的X、Y、Z选项;或者在脚本的Start()方法中添加代码锁定:

void Start()
{
    rb = gameObject.GetComponent<Rigidbody>();
    // 锁定所有轴的旋转,避免碰撞引发自转
    rb.freezeRotation = true;
    isGrounded = false;
    // Lock cursor
    Cursor.lockState = CursorLockMode.Locked;
    Cursor.visible = false;
}

2. 重构移动方向计算逻辑

改为基于相机的世界坐标系计算移动方向,避免角色旋转影响移动路径:

void MovePlayer() {
    // 基于相机的水平方向计算移动向量,忽略Y轴避免上下偏移
    Vector3 cameraForward = playerCamera.transform.forward;
    Vector3 cameraRight = playerCamera.transform.right;
    cameraForward.y = 0;
    cameraRight.y = 0;
    cameraForward.Normalize();
    cameraRight.Normalize();

    float curSpeedX = canMove ? moveSpeed * Input.GetAxis("Vertical") : 0;
    float curSpeedY = canMove ? moveSpeed * Input.GetAxis("Horizontal") : 0;
    moveDirection = (cameraForward * curSpeedX) + (cameraRight * curSpeedY);

    // 施加自定义重力
    if(!isGrounded)
    {
        rb.AddForce(Vector3.down * gravity * Time.deltaTime, ForceMode.Acceleration);
    }

    if (Input.GetButton("Jump") && canMove && isGrounded)
    {
        rb.AddForce(transform.up * jumpSpeed, ForceMode.Impulse);
    }

    // 统一处理地面与空中移动,使用ForceMode.Force更稳定
    if(canMove)
    {
        float currentMoveSpeed = isGrounded ? 1f : airMoveSpeed;
        rb.AddForce(moveDirection * currentMoveSpeed * Time.deltaTime, ForceMode.Force);
    }

    // 玩家与相机旋转逻辑保持不变
    if (canMove)
    {
        rotationX += -Input.GetAxis("Mouse Y") * sensitivity;
        rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
        playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
        transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * sensitivity, 0);
    }
}

3. 优化地面检测与Drag设置(可选)

确保GroundCheck脚本的地面检测逻辑准确,避免isGrounded状态误判;如果移动仍有卡顿,可以适当调整groundDrag和airDrag的数值,或者在地面时限制角色的Y轴速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:20:39