如何使用C#在Unity中防止我的第一人称角色控制器穿过墙壁?

3

请查看此视频:https://i.gyazo.com/ad45ef9e231fd2f9ec6d4cf76889aece.mp4

我的代码:

MouseLook.cs:

using UnityEngine;
using System.Collections;

public class MouseLook : MonoBehaviour
{
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 3F;
public float sensitivityY = 3F;
public Camera playerCamera;

public float minimumX = -360F;
public float maximumX = 360F;

public float minimumY = -60F;
public float maximumY = 60F;



private float rotationX = 0F;
private float rotationY = 0F;

private Quaternion originalRotation;

void Update()
{
    if (axes == RotationAxes.MouseXAndY)
    {
        rotationX += Input.GetAxis("Mouse X") * sensitivityX;
        rotationY += Input.GetAxis("Mouse Y") * sensitivityY;

        rotationX = ClampAngle(rotationX, minimumX, maximumX);
        rotationY = ClampAngle(rotationY, minimumY, maximumY);

        Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
        Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -Vector3.right);

        transform.localRotation = originalRotation * xQuaternion * yQuaternion;
    }

    if (axes == RotationAxes.MouseX)
    {
        rotationX += Input.GetAxis("Mouse X") * sensitivityX;
        rotationX = ClampAngle(rotationX, minimumX, maximumX);

        Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
        transform.localRotation = originalRotation * xQuaternion;
    }

    if (axes == RotationAxes.MouseY || playerCamera != null)
    {
        rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
        rotationY = ClampAngle(rotationY, minimumY, maximumY);

        Quaternion yQuaternion = Quaternion.AngleAxis(-rotationY, Vector3.right);

        if (playerCamera != null)
        {
            playerCamera.transform.localRotation = originalRotation * yQuaternion;
        }
        else
        {
            transform.localRotation = originalRotation * yQuaternion;
        }
    }
}

void Start()
{
    /*
    if (gameObject.GetComponent<Rigidbody>())
    {
        gameObject.GetComponent<Rigidbody>().freezeRotation = true;
    }
    */
    originalRotation = transform.localRotation;
}

public static float ClampAngle(float angle, float min, float max)
{
    if (angle < -360F)
    {
        angle += 360F;
    }

    if (angle > 360F)
    {
        angle -= 360F;
    }

    return Mathf.Clamp(angle, min, max);
}

}

FirstPersonController.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Cursor = UnityEngine.Cursor;




public class FirstPersonController : MonoBehaviour
{
    private float speed = 5;
    private float jumpPower = 4;
    Rigidbody rb;
    CapsuleCollider col;
    public GameObject crossHair;
    bool isActive;
    float HorizontalInput;
    float VerticalInput;


    void Start()
    {
        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;
        rb = GetComponent<Rigidbody>();
        col = GetComponent<CapsuleCollider>();
        crossHair = GameObject.FindWithTag("CrossHair");
    }


    void Update()
    {
        HorizontalInput = Input.GetAxisRaw("Horizontal");
        VerticalInput = Input.GetAxisRaw("Vertical");

        if (Input.GetKeyDown("escape"))
        {
            Cursor.lockState = CursorLockMode.None;
        }

        if (Input.GetButtonDown("Sprint"))
        {
            speed = 15;
        }

        if (Input.GetButtonUp("Sprint"))
        {
            speed = 5;
        }

        if (Input.GetKeyDown(KeyCode.H))
        {
            isActive = !isActive;
        }

        if (isActive)
        {
            crossHair.SetActive(true);
        }
        else
        {
            crossHair.SetActive(false);
        }

    }


    void FixedUpdate()
    {
        Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
        Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
        rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed;



        if (isGrounded() && Input.GetButtonDown("Jump"))

        {
            rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
        }
    }


    private bool isGrounded()
    {
        return Physics.Raycast(transform.position, Vector3.down, col.bounds.extents.y + 0.1f);
    }
}

这段代码有什么问题?如果有,如何解决?

整个项目可在此处下载:https://github.com/Some-T/FirstPersonController-CSharp

项目已设置相关的碰撞体和刚体!

有人建议我使用shapecast,但我认为可能不正确?我看不出来这怎么能行,因为我的玩家没有添加角色控制器组件?

总的来说,我该如何防止我的第一人称角色控制器像上面指定的视频中一样穿过墙壁?

经过进一步的研究,我发现了以下内容:

答案是使用:

https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html 作为快速修复。

但是,为了完美无缺,要使用:

https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html

至于如何在C#中实现,我不太确定,但这是我在bolt asset中拍摄的截图。

enter image description here

目前,我使用速度使运动正常工作,但它不起作用,我不确定为什么?因此,现在我的问题是如何使用速度使运动正常工作?我在FirstPersonController.cs中添加了一条线,使用速度移动角色:rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed; 所以我唯一的问题和问题现在是我的玩家不朝着我玩家上的相机面向的方向移动,所以我不确定如何总体修复这个特定的问题?


你的玩家刚体设置为连续碰撞检测还是离散碰撞检测?应该设置为连续。另外,如果你不想让玩家透过墙壁看到里面,可以改变相机的裁剪平面。 - Antoine
感谢您的回复。我已将其设置为连续运行模式。我更改了剪裁平面,视觉上它消除了问题,但实际上我仍然可以穿过墙壁或实体物体。 - user9843319
你能再检查一下你的碰撞器是否覆盖了整个墙面吗?因为似乎只有特定的部分可以穿过。 - Antoine
是的,它们是网格碰撞器。问题是,我有完全相同的项目,唯一的区别是我使用了Bolt可视化脚本,而这里使用了raycast(但这仅用于弯腰和天花板检查,如在隧道天花板中),这很好地运作,并且该级别也相同。我尝试了使用Unity创建一个纯实心物体而不是使用probuilder的实验,但我遇到了完全相同的问题,可以穿过墙壁走路。有时与实心物体碰撞会使玩家跳得很高。关键在于找出我需要改变的那一件事情是什么? - user9843319
这个问题发生在Unity对象墙和ProBuilder网格墙上,尽管已经检查了它们的碰撞器,但是否绝对是整个墙面都有问题呢? - user9843319
1个回答

0
1. 在您的项目代码与您提供的代码不同。 2. 尝试启用刚体(rigidbody)的旋转约束 - 冻结 X 和 Z 旋转,仅保留 Y。当您旋转胶囊碰撞器(如在您的项目中工作时),它可以“爬”在墙上。 3. 在移动时进行isGrounded检查,并在没有接地时锁定移动。 4. 尝试增加Collider.contactOffset。 5. 如果上述不起作用,请尝试使用Rigidbody.velocity而不是Rigidbody.MovePosition。 6. 您还可以减少Rigidbody.drag。 7. 总的来说,使用NavMesh进行移动是一种非常好的技术 - 这样,您就可以明确地将玩家锁在NavMesh表面之外的运动之外。但是,当然,这并不适用于许多游戏玩法。
希望能对您有所帮助。
更新 您可以像这样移动您的玩家:
void FixedUpdate()
    {
        Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
        Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
    }

请注意,您甚至不需要将其应用于刚体; 但是您可以通过以下方式获取输入:
float HorizontalInput = Input.GetAxis("Horizontal");         
float VerticalInput = Input.GetAxis("Vertical");

在Update中,输入只停留在Update内部,根本没有应用。你声明了两次变量,一次在类顶部,另一次在Update()中。

此外,为了防止透视效果穿墙而出现的问题,需要调整摄像机的近裁剪面。 - Mykhailo Khadzhynov
我已经尝试了所有这些,目前我正在尝试实现速度作为一种修复措施。此外,通过Github链接获取的代码是最好的,因为它会不断更新。 - user9843319
你可以像这样移动你的玩家角色:Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime; Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime; 在 FixedUpdate 中,但是你需要在 Update 中获取输入:float HorizontalInput = Input.GetAxis("Horizontal"); float VerticalInput = Input.GetAxis("Vertical"); 因此输入只会留在 Update 中,并且不会被应用到 FixedUpdate 中 :) - Mykhailo Khadzhynov
我的最大问题是我无法在Stack Overflow上更新代码,相对于我在Github上的更新量,但我会尽力而为。目前我正在实现速度,但它没有正常工作。 - user9843319
让我们在聊天中继续这个讨论 - user9843319
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接