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

问题描述 投票:0回答:1

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;
    }
}

Child - 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 - 胶囊碰撞器

儿童 - 方向 - 只需变换方向

Child - CameraPos - 只需变换位置

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

c# unity-game-engine 3d physics
1个回答
0
投票

我在你的

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

这显然会将 RigidBody 的

Y
组件映射到其
Z
组件。 这可以解释捕捉的原因,因为
Y
看起来大多数时候都是零。因此,当您达到速度限制时,您将
Z
分量设置为 0,这将使其沿
X
轴捕捉。

这也可以解释跳跃问题,因为在这种情况下,

Y
速度不会为0,这意味着映射的
Y
速度将映射到
Z
轴,在跳跃时推动角色向前。

我想你想说的是:

Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);

© www.soinside.com 2019 - 2024. All rights reserved.