Unity 3D - 脉冲反应行为上的意外轴捕捉和未知力

Unity 3D - Unintended axis snapping and unknown force on Impulse reaction behavior

提问人:Logan Alexander Thies 提问时间:11/9/2023 最后编辑:Logan Alexander Thies 更新时间:11/10/2023 访问量:51

问:

UNITY 版本 2022.3.11f1 3D URP项目

我正在开发一个简单的播放器控制器脚本,但很难确定速度以意外方式运行的原因。

两个问题:

1 - 当我在向前移动时旋转玩家刚体时 - 速度/方向会捕捉到世界的 x 轴。

黄线是速度 红线是作用力(根据键 (W) 输入和鼠标输入的方向推动)

视频演示 : https://clipchamp.com/watch/kpePTVpR73s

问题 - 为什么速度会捕捉到这样的世界轴,我该如何开始解决?

2 - 当我按下空格键在 y 方向上施加脉冲力时,在 z 世界轴方向上施加了一个力,该力正在推动刚体,我花了很多时间注释掉其他作用力并查看 UI 以查找可能起作用的力。

黄线是速度 红线是作用力(根据键 (W) 输入和鼠标输入的方向推动)

视频演示 : https://clipchamp.com/watch/phAYs1v4ULU

问题 - 为什么当脉冲设置为世界 y 时,速度会立即与 z 方向成一定角度,为什么在弧路径的末端会发生偏转,就好像刚体在撞到一堵看不见的墙上一样?

资产结构

照相机 父级 - CameraHolder - MoveCamera 脚本

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

public class MoveCamera : MonoBehaviour
{
    public Transform cameraPosition;
 
    // Update is called once per frame
    void Update()
    {
        transform.position = cameraPosition.position;
    }
}

儿童 - PlayerCam(主摄像头) - PlayerCam脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class PlayerCam : MonoBehaviour
{
 
    public float sensX;
    public float sensY;
 
    public Transform orientation;
 
    float xRotation;
    float yRotation;
 
 
    // Start is called before the first frame update
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }
 
    // Update is called once per frame
    void Update()
    {
        // Get Mouse Input
 
        float mouseX = Input.GetAxisRaw("Mouse X") * Time.deltaTime * sensX;
        float mouseY = Input.GetAxisRaw("Mouse Y") * Time.deltaTime * sensY;
 
        yRotation += mouseX;
        xRotation -= mouseY;
 
        xRotation = Mathf.Clamp(xRotation, -90f, 90f);
 
 
 
        transform.rotation = Quaternion.Euler(xRotation, yRotation, 0);
        orientation.rotation = Quaternion.Euler(0, yRotation, 0);
    }
}

选手 父级 - 玩家 - 刚体 - 玩家移动脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class PlayerMovement : MonoBehaviour
{
    [Header("Movement")]
    public float moveSpeed;
 
    float horizontalInput;
    float verticalInput;
 
    Vector3 moveDirection;
 
    Rigidbody rb;
 
    public float groundDrag;
 
    public float jumpForce;
    public float jumpCooldown;
    public float airMultiplier;
    bool readyToJump = true;
 
    [Header("Keybinds")]
    public KeyCode jumpKey = KeyCode.Space;
 
    [Header("Ground Check")]
    public float playerHeight;
    public LayerMask whatIsGround;
    public Transform orientation;
    bool grounded;
 
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
    }
 
    // Update is called once per frame
    void Update()
    {
         
        Debug.DrawRay(rb.position, rb.velocity, Color.yellow);
        Debug.DrawRay(rb.position, rb.GetAccumulatedForce(), Color.red);
 
        // Ground Check
        grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
 
        MyInput();
        SpeedControl();
 
        // Handle drag
        if (grounded)
            rb.drag = groundDrag;
        else
            rb.drag = 0;
    }
 
    private void FixedUpdate()
    {
        MovePlayer();
    }
    private void MyInput()
    {
        horizontalInput = Input.GetAxis("Horizontal");
        verticalInput = Input.GetAxis("Vertical");
 
        if (Input.GetKey(jumpKey) && readyToJump && grounded)
        {
            readyToJump = false;
 
            Jump();
 
            Invoke(nameof(ResetJump), jumpCooldown);
        }
    }
 
    private void MovePlayer()
    {
        // Calculate movement direction
        moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
        Debug.Log("Orientation --- " + orientation.position);
        Debug.Log("Orientation --- " + orientation.forward);
        Debug.DrawLine(rb.position, moveDirection, Color.green);
 
        // On Ground
        if (grounded)
            rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force);
        else if (!grounded)
            rb.AddForce(moveDirection.normalized * moveSpeed * 10f * airMultiplier, ForceMode.Force);
    }
   
    private void SpeedControl()
    {
        Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);
 
        // Limit velocity if needed
        if(flatVelocity.magnitude > moveSpeed)
        {
            Vector3 limitedVelocity = flatVelocity.normalized * moveSpeed;
            rb.velocity = new Vector3(limitedVelocity.x, rb.velocity.y, limitedVelocity.z);
        }
    }
 
    private void Jump()
    {
        // Reset Y Velocity
        rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
    }
 
    private void ResetJump()
    {
        readyToJump = true;
    }
}

儿童 - PlayerObj - 胶囊对撞机

子 - 方向 - 只需变换方向

儿童 - CameraPos - 只需变换位置

我尝试了各种代码注释、日志记录和 UI 配置,以尝试推理力如何作用在刚体上。

C# unity-游戏引擎 3D 物理

评论

1赞 hijinxbassist 11/9/2023
输入应在 Update 函数中处理,而不是在 Fixed Update 中处理。
0赞 Logan Alexander Thies 11/9/2023
我已经更新了脚本 - 这是我调试的一部分。这些视频反映了正在更新的代码,而不是 fixedUpdate。fixedUpdate 应该只有 movePlayer。

答:

0赞 Ryan Gray 11/9/2023 #1

我看到你的方法你有.SpeedControl()Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);

这显然会将 RigidBody 的速度分量映射到其分量。 这可以解释捕捉,因为大多数时候它看起来都是零。因此,当您达到速度限制时,您需要将组件设置为 0,这将沿轴对齐它。YZYZX

这也可以解释跳跃问题,因为在这种情况下,速度不会为 0,这意味着映射的速度将映射到轴上,在跳跃时将角色向前推。YYZ

我想你想说的是:Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);

评论

1赞 Logan Alexander Thies 11/10/2023
谢谢!这确实是罪魁祸首。对结果行为的很好的解释 - 完全有道理。感谢您的时间!
0赞 Ryan Gray 11/10/2023
棒!我很高兴我能提供帮助。跟上发展,不要忘记在这样做的同时玩得开心。